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:
defiQUG
2026-04-07 23:22:12 -07:00
parent d931be8e19
commit 6eef6b07f6
224 changed files with 19671 additions and 3291 deletions

View File

@@ -1,14 +1,20 @@
package track2
import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/jackc/pgx/v5/pgxpool"
)
var track2HashPattern = regexp.MustCompile(`^0x[0-9a-fA-F]{64}$`)
// Server handles Track 2 endpoints
type Server struct {
db *pgxpool.Pool
@@ -29,6 +35,9 @@ func (s *Server) HandleAddressTransactions(w http.ResponseWriter, r *http.Reques
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
return
}
if !s.requireDB(w) {
return
}
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track2/address/")
parts := strings.Split(path, "/")
@@ -37,7 +46,11 @@ func (s *Server) HandleAddressTransactions(w http.ResponseWriter, r *http.Reques
return
}
address := strings.ToLower(parts[0])
address, err := normalizeTrack2Address(parts[0])
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
page = 1
@@ -51,7 +64,7 @@ func (s *Server) HandleAddressTransactions(w http.ResponseWriter, r *http.Reques
query := `
SELECT hash, from_address, to_address, value, block_number, timestamp, status
FROM transactions
WHERE chain_id = $1 AND (from_address = $2 OR to_address = $2)
WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)
ORDER BY block_number DESC, timestamp DESC
LIMIT $3 OFFSET $4
`
@@ -92,7 +105,7 @@ func (s *Server) HandleAddressTransactions(w http.ResponseWriter, r *http.Reques
// Get total count
var total int
countQuery := `SELECT COUNT(*) FROM transactions WHERE chain_id = $1 AND (from_address = $2 OR to_address = $2)`
countQuery := `SELECT COUNT(*) FROM transactions WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)`
s.db.QueryRow(r.Context(), countQuery, s.chainID, address).Scan(&total)
response := map[string]interface{}{
@@ -115,6 +128,9 @@ func (s *Server) HandleAddressTokens(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
return
}
if !s.requireDB(w) {
return
}
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track2/address/")
parts := strings.Split(path, "/")
@@ -123,12 +139,16 @@ func (s *Server) HandleAddressTokens(w http.ResponseWriter, r *http.Request) {
return
}
address := strings.ToLower(parts[0])
address, err := normalizeTrack2Address(parts[0])
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
query := `
SELECT token_contract, balance, last_updated_timestamp
FROM token_balances
WHERE address = $1 AND chain_id = $2 AND balance > 0
WHERE LOWER(address) = $1 AND chain_id = $2 AND balance > 0
ORDER BY balance DESC
`
@@ -151,7 +171,7 @@ func (s *Server) HandleAddressTokens(w http.ResponseWriter, r *http.Request) {
tokens = append(tokens, map[string]interface{}{
"contract": contract,
"balance": balance,
"balance_formatted": balance, // TODO: Format with decimals
"balance_formatted": nil,
"last_updated": lastUpdated,
})
}
@@ -174,14 +194,40 @@ func (s *Server) HandleTokenInfo(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
return
}
if !s.requireDB(w) {
return
}
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track2/token/")
contract := strings.ToLower(path)
contract, err := normalizeTrack2Address(path)
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
// Get token info from token_transfers
query := `
SELECT
COUNT(DISTINCT from_address) + COUNT(DISTINCT to_address) as holders,
(
SELECT COUNT(*)
FROM (
SELECT from_address AS address
FROM token_transfers
WHERE token_contract = $1
AND chain_id = $2
AND timestamp >= NOW() - INTERVAL '24 hours'
AND from_address IS NOT NULL
AND from_address <> ''
UNION
SELECT to_address AS address
FROM token_transfers
WHERE token_contract = $1
AND chain_id = $2
AND timestamp >= NOW() - INTERVAL '24 hours'
AND to_address IS NOT NULL
AND to_address <> ''
) holder_addresses
) as holders,
COUNT(*) as transfers_24h,
SUM(value) as volume_24h
FROM token_transfers
@@ -191,7 +237,7 @@ func (s *Server) HandleTokenInfo(w http.ResponseWriter, r *http.Request) {
var holders, transfers24h int
var volume24h string
err := s.db.QueryRow(r.Context(), query, contract, s.chainID).Scan(&holders, &transfers24h, &volume24h)
err = s.db.QueryRow(r.Context(), query, contract, s.chainID).Scan(&holders, &transfers24h, &volume24h)
if err != nil {
writeError(w, http.StatusNotFound, "not_found", "Token not found")
return
@@ -216,15 +262,16 @@ func (s *Server) HandleSearch(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
return
}
if !s.requireDB(w) {
return
}
query := r.URL.Query().Get("q")
query := strings.TrimSpace(r.URL.Query().Get("q"))
if query == "" {
writeError(w, http.StatusBadRequest, "bad_request", "Query parameter 'q' is required")
return
}
query = strings.ToLower(strings.TrimPrefix(query, "0x"))
// Try to detect type and search
var result map[string]interface{}
@@ -241,13 +288,14 @@ func (s *Server) HandleSearch(w http.ResponseWriter, r *http.Request) {
},
}
}
} else if len(query) == 64 || len(query) == 40 {
// Could be address or transaction hash
fullQuery := "0x" + query
// Check transaction
} else if track2HashPattern.MatchString(query) {
hash, err := normalizeTrack2Hash(query)
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
var txHash string
err := s.db.QueryRow(r.Context(), `SELECT hash FROM transactions WHERE chain_id = $1 AND hash = $2`, s.chainID, fullQuery).Scan(&txHash)
err = s.db.QueryRow(r.Context(), `SELECT hash FROM transactions WHERE chain_id = $1 AND LOWER(hash) = $2`, s.chainID, hash).Scan(&txHash)
if err == nil {
result = map[string]interface{}{
"type": "transaction",
@@ -255,18 +303,44 @@ func (s *Server) HandleSearch(w http.ResponseWriter, r *http.Request) {
"hash": txHash,
},
}
} else {
// Check address
}
} else if common.IsHexAddress(query) {
address, err := normalizeTrack2Address(query)
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
var exists bool
existsQuery := `
SELECT EXISTS (
SELECT 1
FROM addresses
WHERE chain_id = $1 AND LOWER(address) = $2
UNION
SELECT 1
FROM transactions
WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)
UNION
SELECT 1
FROM token_balances
WHERE chain_id = $1 AND LOWER(address) = $2
)
`
err = s.db.QueryRow(r.Context(), existsQuery, s.chainID, address).Scan(&exists)
if err == nil && exists {
var balance string
err := s.db.QueryRow(r.Context(), `SELECT COALESCE(SUM(balance), '0') FROM token_balances WHERE address = $1 AND chain_id = $2`, fullQuery, s.chainID).Scan(&balance)
if err == nil {
result = map[string]interface{}{
"type": "address",
"result": map[string]interface{}{
"address": fullQuery,
"balance": balance,
},
}
err = s.db.QueryRow(r.Context(), `SELECT COALESCE(SUM(balance), '0') FROM token_balances WHERE LOWER(address) = $1 AND chain_id = $2`, address, s.chainID).Scan(&balance)
if err != nil {
balance = "0"
}
result = map[string]interface{}{
"type": "address",
"result": map[string]interface{}{
"address": address,
"balance": balance,
},
}
}
}
@@ -290,6 +364,9 @@ func (s *Server) HandleInternalTransactions(w http.ResponseWriter, r *http.Reque
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
return
}
if !s.requireDB(w) {
return
}
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track2/address/")
parts := strings.Split(path, "/")
@@ -298,7 +375,11 @@ func (s *Server) HandleInternalTransactions(w http.ResponseWriter, r *http.Reque
return
}
address := strings.ToLower(parts[0])
address, err := normalizeTrack2Address(parts[0])
if err != nil {
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
return
}
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
page = 1
@@ -312,7 +393,7 @@ func (s *Server) HandleInternalTransactions(w http.ResponseWriter, r *http.Reque
query := `
SELECT transaction_hash, from_address, to_address, value, block_number, timestamp
FROM internal_transactions
WHERE chain_id = $1 AND (from_address = $2 OR to_address = $2)
WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)
ORDER BY block_number DESC, timestamp DESC
LIMIT $3 OFFSET $4
`
@@ -345,7 +426,7 @@ func (s *Server) HandleInternalTransactions(w http.ResponseWriter, r *http.Reque
}
var total int
countQuery := `SELECT COUNT(*) FROM internal_transactions WHERE chain_id = $1 AND (from_address = $2 OR to_address = $2)`
countQuery := `SELECT COUNT(*) FROM internal_transactions WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)`
s.db.QueryRow(r.Context(), countQuery, s.chainID, address).Scan(&total)
response := map[string]interface{}{
@@ -372,3 +453,30 @@ func writeError(w http.ResponseWriter, statusCode int, code, message string) {
},
})
}
func (s *Server) requireDB(w http.ResponseWriter) bool {
if s.db == nil {
writeError(w, http.StatusServiceUnavailable, "service_unavailable", "database not configured")
return false
}
return true
}
func normalizeTrack2Address(value string) (string, error) {
trimmed := strings.TrimSpace(value)
if !common.IsHexAddress(trimmed) {
return "", fmt.Errorf("invalid address format")
}
return strings.ToLower(common.HexToAddress(trimmed).Hex()), nil
}
func normalizeTrack2Hash(value string) (string, error) {
trimmed := strings.TrimSpace(value)
if !track2HashPattern.MatchString(trimmed) {
return "", fmt.Errorf("invalid transaction hash")
}
if _, err := hex.DecodeString(trimmed[2:]); err != nil {
return "", fmt.Errorf("invalid transaction hash")
}
return strings.ToLower(trimmed), nil
}