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:
@@ -1,17 +1,22 @@
|
||||
package track1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/explorer/backend/libs/go-rpc-gateway"
|
||||
)
|
||||
|
||||
var track1HashPattern = regexp.MustCompile(`^0x[a-fA-F0-9]{64}$`)
|
||||
|
||||
// Server handles Track 1 endpoints (uses RPC gateway from lib)
|
||||
type Server struct {
|
||||
rpcGateway *gateway.RPCGateway
|
||||
@@ -173,7 +178,12 @@ func (s *Server) HandleBlockDetail(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track1/block/")
|
||||
blockNumStr := fmt.Sprintf("0x%x", parseBlockNumber(path))
|
||||
blockNumber, err := strconv.ParseInt(strings.TrimSpace(path), 10, 64)
|
||||
if err != nil || blockNumber < 0 {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", "Invalid block number")
|
||||
return
|
||||
}
|
||||
blockNumStr := fmt.Sprintf("0x%x", blockNumber)
|
||||
|
||||
blockResp, err := s.rpcGateway.GetBlockByNumber(r.Context(), blockNumStr, false)
|
||||
if err != nil {
|
||||
@@ -203,7 +213,11 @@ func (s *Server) HandleTransactionDetail(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
path := strings.TrimPrefix(r.URL.Path, "/api/v1/track1/tx/")
|
||||
txHash := path
|
||||
txHash := strings.TrimSpace(path)
|
||||
if !track1HashPattern.MatchString(txHash) {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", "Invalid transaction hash")
|
||||
return
|
||||
}
|
||||
|
||||
txResp, err := s.rpcGateway.GetTransactionByHash(r.Context(), txHash)
|
||||
if err != nil {
|
||||
@@ -239,7 +253,11 @@ func (s *Server) HandleAddressBalance(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
address := parts[0]
|
||||
address := strings.TrimSpace(parts[0])
|
||||
if !common.IsHexAddress(address) {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", "Invalid address")
|
||||
return
|
||||
}
|
||||
balanceResp, err := s.rpcGateway.GetBalance(r.Context(), address, "latest")
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "rpc_error", err.Error())
|
||||
@@ -278,31 +296,25 @@ func (s *Server) HandleBridgeStatus(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Return bridge status (simplified - in production, query bridge contracts)
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 12*time.Second)
|
||||
defer cancel()
|
||||
|
||||
data := s.BuildBridgeStatusData(ctx)
|
||||
response := map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"status": "operational",
|
||||
"chains": map[string]interface{}{
|
||||
"138": map[string]interface{}{
|
||||
"name": "Defi Oracle Meta Mainnet",
|
||||
"status": "operational",
|
||||
"last_sync": time.Now().UTC().Format(time.RFC3339),
|
||||
},
|
||||
"1": map[string]interface{}{
|
||||
"name": "Ethereum Mainnet",
|
||||
"status": "operational",
|
||||
"last_sync": time.Now().UTC().Format(time.RFC3339),
|
||||
},
|
||||
},
|
||||
"total_transfers_24h": 150,
|
||||
"total_volume_24h": "5000000000000000000000",
|
||||
},
|
||||
"data": data,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func chainStatusFromProbe(p RPCProbeResult) string {
|
||||
if p.OK {
|
||||
return "operational"
|
||||
}
|
||||
return "unreachable"
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func writeError(w http.ResponseWriter, statusCode int, code, message string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -320,14 +332,6 @@ func hexToInt(hex string) (int64, error) {
|
||||
return strconv.ParseInt(hex, 16, 64)
|
||||
}
|
||||
|
||||
func parseBlockNumber(s string) int64 {
|
||||
num, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func transformBlock(blockData map[string]interface{}) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"number": parseHexField(blockData["number"]),
|
||||
|
||||
Reference in New Issue
Block a user