Files
explorer-monorepo/backend/api/rest/stats.go
defiQUG bdae5a9f6e 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
2026-04-07 23:22:12 -07:00

86 lines
2.2 KiB
Go

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 {
writeMethodNotAllowed(w)
return
}
if !s.requireDB(w) {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
stats, err := loadExplorerStats(ctx, s.chainID, s.db.QueryRow)
if err != nil {
writeError(w, http.StatusServiceUnavailable, "service_unavailable", "explorer stats are temporarily unavailable")
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(stats)
}