Files
explorer-monorepo/backend/api/track1/bridge_lanes.go
defiQUG 228fa0eef6
Some checks failed
Deploy Explorer Live / deploy (push) Failing after 13s
Validate Explorer / frontend (push) Failing after 21s
Validate Explorer / smoke-e2e (push) Has been skipped
Add bridge lane health API and config-ready lane UI for Tier A Week 3.
Probe LINK balances on CCIP bridge contracts, expose proof-transfer metadata on bridge status, and render funded/unfunded lane health on /bridge with extended smoke coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 04:21:44 -07:00

256 lines
6.8 KiB
Go

package track1
import (
"context"
_ "embed"
"encoding/json"
"math/big"
"net/http"
"os"
"strconv"
"strings"
"time"
)
//go:embed bridge_lanes_default.json
var defaultBridgeLanesJSON []byte
type bridgeLaneDefinition struct {
Key string `json:"key"`
ChainName string `json:"chain_name"`
ChainID int64 `json:"chain_id"`
ConfigReady bool `json:"config_ready"`
RPCEnvs []string `json:"rpc_envs"`
RPCDefault string `json:"rpc_default"`
LinkToken string `json:"link_token"`
WETH9Bridge string `json:"weth9_bridge"`
WETH10Bridge string `json:"weth10_bridge"`
}
type bridgeLanesConfig struct {
Updated string `json:"updated"`
MinLinkWei string `json:"min_link_wei"`
Lanes []bridgeLaneDefinition `json:"lanes"`
}
func loadBridgeLanesConfig() bridgeLanesConfig {
path := strings.TrimSpace(os.Getenv("MISSION_CONTROL_BRIDGE_LANES_JSON"))
if path != "" {
if b, err := os.ReadFile(path); err == nil && len(b) > 0 {
var cfg bridgeLanesConfig
if json.Unmarshal(b, &cfg) == nil && len(cfg.Lanes) > 0 {
return cfg
}
}
}
var cfg bridgeLanesConfig
_ = json.Unmarshal(defaultBridgeLanesJSON, &cfg)
return cfg
}
func resolveLaneRPC(def bridgeLaneDefinition) string {
for _, key := range def.RPCEnvs {
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
return value
}
}
for _, row := range ParseExtraRPCProbes() {
chainKey := row[2]
if chainKey == strconv.FormatInt(def.ChainID, 10) {
return row[1]
}
}
return strings.TrimSpace(def.RPCDefault)
}
func erc20BalanceOf(ctx context.Context, rpcURL, tokenAddress, holderAddress string) (string, error) {
tokenAddress = strings.ToLower(strings.TrimSpace(tokenAddress))
holderAddress = strings.ToLower(strings.TrimPrefix(strings.TrimSpace(holderAddress), "0x"))
if len(holderAddress) != 40 {
return "0", nil
}
data := "0x70a08231" + strings.Repeat("0", 24) + holderAddress
raw, _, err := postJSONRPC(ctx, bridgeLaneHTTPClient(), rpcURL, "eth_call", []interface{}{
map[string]interface{}{
"to": tokenAddress,
"data": data,
},
"latest",
})
if err != nil {
return "", err
}
var hex string
if err := json.Unmarshal(raw, &hex); err != nil {
return "", err
}
hex = strings.TrimSpace(hex)
if hex == "" || hex == "0x" {
return "0", nil
}
value := new(big.Int)
if _, ok := value.SetString(strings.TrimPrefix(hex, "0x"), 16); !ok {
return "0", nil
}
return value.String(), nil
}
func bridgeLaneHTTPClient() *http.Client {
return &http.Client{Timeout: 6 * time.Second}
}
func bridgeFundingStatus(linkBalanceWei, minLinkWei string) string {
balance, okBalance := new(big.Int).SetString(strings.TrimSpace(linkBalanceWei), 10)
minimum, okMin := new(big.Int).SetString(strings.TrimSpace(minLinkWei), 10)
if !okBalance || !okMin {
return "unknown"
}
if balance.Cmp(minimum) >= 0 {
return "funded"
}
if balance.Sign() > 0 {
return "degraded"
}
return "unfunded"
}
func proofStatusForLane(key string, proofs map[string]interface{}) string {
if proofs == nil {
return "proof-pending"
}
laneProofs, ok := proofs[key].([]interface{})
if !ok || len(laneProofs) == 0 {
return "proof-pending"
}
for _, item := range laneProofs {
row, ok := item.(map[string]interface{})
if !ok {
continue
}
if tx, ok := row["tx_hash"].(string); ok && strings.TrimSpace(tx) != "" {
return "proof-recorded"
}
}
return "proof-pending"
}
func readProofTransfersJSON() map[string]interface{} {
path := strings.TrimSpace(os.Getenv("MISSION_CONTROL_PROOF_TRANSFERS_JSON"))
if path == "" {
return nil
}
b, err := os.ReadFile(path)
if err != nil || len(b) == 0 {
return map[string]interface{}{"error": "unreadable or empty", "path": path}
}
if len(b) > 512*1024 {
return map[string]interface{}{"error": "file too large", "path": path}
}
var payload map[string]interface{}
if err := json.Unmarshal(b, &payload); err != nil {
return map[string]interface{}{"error": err.Error(), "path": path}
}
return payload
}
func probeBridgeContract(ctx context.Context, rpcURL, linkToken, bridgeAddress, minLinkWei string) map[string]interface{} {
result := map[string]interface{}{
"bridge": strings.TrimSpace(bridgeAddress),
}
if rpcURL == "" {
result["status"] = "unknown"
result["error"] = "rpc unavailable"
return result
}
if linkToken == "" || bridgeAddress == "" {
result["status"] = "unknown"
result["error"] = "missing link token or bridge address"
return result
}
balance, err := erc20BalanceOf(ctx, rpcURL, linkToken, bridgeAddress)
if err != nil {
result["status"] = "unknown"
result["error"] = err.Error()
return result
}
result["link_balance_wei"] = balance
result["status"] = bridgeFundingStatus(balance, minLinkWei)
return result
}
func aggregateLaneStatus(weth9Status, weth10Status, proofStatus string) string {
statuses := []string{weth9Status, weth10Status}
hasUnfunded := false
hasDegraded := false
hasUnknown := false
for _, status := range statuses {
switch status {
case "unfunded":
hasUnfunded = true
case "degraded":
hasDegraded = true
case "unknown":
hasUnknown = true
}
}
if hasUnfunded {
return "unfunded"
}
if hasDegraded {
return "degraded"
}
if hasUnknown {
return "unknown"
}
if proofStatus == "proof-pending" {
return "proof-pending"
}
return "funded"
}
func BuildBridgeLaneHealth(ctx context.Context) (map[string]interface{}, map[string]interface{}) {
cfg := loadBridgeLanesConfig()
minLinkWei := strings.TrimSpace(cfg.MinLinkWei)
if minLinkWei == "" {
minLinkWei = "1000000000000000000"
}
proofPayload := readProofTransfersJSON()
proofByLane := map[string]interface{}{}
if proofPayload != nil {
if lanes, ok := proofPayload["lanes"].(map[string]interface{}); ok {
proofByLane = lanes
}
}
lanes := make([]map[string]interface{}, 0, len(cfg.Lanes))
for _, def := range cfg.Lanes {
rpcURL := resolveLaneRPC(def)
weth9 := probeBridgeContract(ctx, rpcURL, def.LinkToken, def.WETH9Bridge, minLinkWei)
weth10 := probeBridgeContract(ctx, rpcURL, def.LinkToken, def.WETH10Bridge, minLinkWei)
weth9Status, _ := weth9["status"].(string)
weth10Status, _ := weth10["status"].(string)
proofStatus := proofStatusForLane(def.Key, proofByLane)
lanes = append(lanes, map[string]interface{}{
"key": def.Key,
"chain_name": def.ChainName,
"chain_id": def.ChainID,
"config_ready": def.ConfigReady,
"link_token": def.LinkToken,
"status": aggregateLaneStatus(weth9Status, weth10Status, proofStatus),
"proof_status": proofStatus,
"weth9": weth9,
"weth10": weth10,
"rpc_endpoint": redactRPCOrigin(rpcURL),
})
}
laneHealth := map[string]interface{}{
"updated_at": time.Now().UTC().Format(time.RFC3339),
"min_link_wei": minLinkWei,
"lanes": lanes,
}
return laneHealth, proofPayload
}