feat: explorer API, wallet, CCIP scripts, and config refresh
- Backend REST/gateway/track routes, analytics, Blockscout proxy paths. - Frontend wallet and liquidity surfaces; MetaMask token list alignment. - Deployment docs, verification scripts, address inventory updates. Check: go build ./... under backend/ (pass). Made-with: Cursor
This commit is contained in:
@@ -3,10 +3,64 @@ package rest
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type explorerStats struct {
|
||||
TotalBlocks int64 `json:"total_blocks"`
|
||||
TotalTransactions int64 `json:"total_transactions"`
|
||||
TotalAddresses int64 `json:"total_addresses"`
|
||||
LatestBlock int64 `json:"latest_block"`
|
||||
}
|
||||
|
||||
type statsQueryFunc func(ctx context.Context, sql string, args ...any) pgx.Row
|
||||
|
||||
func loadExplorerStats(ctx context.Context, chainID int, queryRow statsQueryFunc) (explorerStats, error) {
|
||||
var stats explorerStats
|
||||
|
||||
if err := queryRow(ctx,
|
||||
`SELECT COUNT(*) FROM blocks WHERE chain_id = $1`,
|
||||
chainID,
|
||||
).Scan(&stats.TotalBlocks); err != nil {
|
||||
return explorerStats{}, fmt.Errorf("query total blocks: %w", err)
|
||||
}
|
||||
|
||||
if err := queryRow(ctx,
|
||||
`SELECT COUNT(*) FROM transactions WHERE chain_id = $1`,
|
||||
chainID,
|
||||
).Scan(&stats.TotalTransactions); err != nil {
|
||||
return explorerStats{}, fmt.Errorf("query total transactions: %w", err)
|
||||
}
|
||||
|
||||
if err := queryRow(ctx,
|
||||
`SELECT COUNT(*) FROM (
|
||||
SELECT from_address AS address
|
||||
FROM transactions
|
||||
WHERE chain_id = $1 AND from_address IS NOT NULL AND from_address <> ''
|
||||
UNION
|
||||
SELECT to_address AS address
|
||||
FROM transactions
|
||||
WHERE chain_id = $1 AND to_address IS NOT NULL AND to_address <> ''
|
||||
) unique_addresses`,
|
||||
chainID,
|
||||
).Scan(&stats.TotalAddresses); err != nil {
|
||||
return explorerStats{}, fmt.Errorf("query total addresses: %w", err)
|
||||
}
|
||||
|
||||
if err := queryRow(ctx,
|
||||
`SELECT COALESCE(MAX(number), 0) FROM blocks WHERE chain_id = $1`,
|
||||
chainID,
|
||||
).Scan(&stats.LatestBlock); err != nil {
|
||||
return explorerStats{}, fmt.Errorf("query latest block: %w", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// handleStats handles GET /api/v2/stats
|
||||
func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
@@ -20,43 +74,12 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Get total blocks
|
||||
var totalBlocks int64
|
||||
err := s.db.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM blocks WHERE chain_id = $1`,
|
||||
s.chainID,
|
||||
).Scan(&totalBlocks)
|
||||
stats, err := loadExplorerStats(ctx, s.chainID, s.db.QueryRow)
|
||||
if err != nil {
|
||||
totalBlocks = 0
|
||||
}
|
||||
|
||||
// Get total transactions
|
||||
var totalTransactions int64
|
||||
err = s.db.QueryRow(ctx,
|
||||
`SELECT COUNT(*) FROM transactions WHERE chain_id = $1`,
|
||||
s.chainID,
|
||||
).Scan(&totalTransactions)
|
||||
if err != nil {
|
||||
totalTransactions = 0
|
||||
}
|
||||
|
||||
// Get total addresses
|
||||
var totalAddresses int64
|
||||
err = s.db.QueryRow(ctx,
|
||||
`SELECT COUNT(DISTINCT from_address) + COUNT(DISTINCT to_address) FROM transactions WHERE chain_id = $1`,
|
||||
s.chainID,
|
||||
).Scan(&totalAddresses)
|
||||
if err != nil {
|
||||
totalAddresses = 0
|
||||
}
|
||||
|
||||
stats := map[string]interface{}{
|
||||
"total_blocks": totalBlocks,
|
||||
"total_transactions": totalTransactions,
|
||||
"total_addresses": totalAddresses,
|
||||
writeError(w, http.StatusServiceUnavailable, "service_unavailable", "explorer stats are temporarily unavailable")
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(stats)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user