diff --git a/benchmarks/bridge-execution-latency.yml b/benchmarks/bridge-execution-latency.yml index b110e8817..07d7d9860 100644 --- a/benchmarks/bridge-execution-latency.yml +++ b/benchmarks/bridge-execution-latency.yml @@ -50,7 +50,7 @@ abstract: | from the latency distribution and counted against the success rate. methodology: - - "Route: a self-conserving USDC triangle. Solana USDC to Base USDC, Base USDC to Arbitrum USDT, Arbitrum USDT to Solana USDC. Each executing bridge completes the full triangle before the next starts, so inventory returns to its origin and only fees burn." + - "Route: a self-conserving all-USDC triangle. Solana USDC to Base USDC, Base USDC to Arbitrum USDC, Arbitrum USDC to Solana USDC. Each executing bridge completes the full triangle before the next starts, so inventory returns to its origin and only fees burn." - "Ticket sizes: $3 daily and $30 twice weekly, on fixed UTC schedules. Amounts are deliberately small: this measures settlement reliability, not large-ticket cost." - "Latency: execution latency is wall-clock from broadcast to the destination credit; end-to-end latency adds the quote round-trip. Histogram buckets 1s, 5s, 10s, 30s, 60s, 120s, 300s, 600s; p50, p90 and p99 via histogram_quantile over 24h." - "Outcome classification: settled (funds received), reverted or refunded (capital returned to source), or errored (never broadcast). Only settled transactions contribute to the latency distribution; reverts and refunds count against the success rate." diff --git a/benchmarks/bridge-fee.yml b/benchmarks/bridge-fee.yml index bb609b206..7c8708225 100644 --- a/benchmarks/bridge-fee.yml +++ b/benchmarks/bridge-fee.yml @@ -63,7 +63,7 @@ methodology: - "Reference notional: $300 USDC. Smaller sizes ($5, $50) are captured by the harness but excluded from this report." - "Cost: `bridge_cost_percent{amount_usd=\"300\"}`, in percent of notional. Includes fees + slippage + destination gas." - "Cadence: full sweep every 5 minutes for 24 hours." - - "Aggregation: the headline number is the average of per-corridor 24h medians over the corridors a provider actually quotes. The TRUMP to BRETT calibration route is excluded. Near Intents has no USDT legs, so its aggregate covers the Solana to Base and Arbitrum to HyperCore corridors only. deBridge does not quote the Arbitrum to HyperCore corridor, so its aggregate covers the remaining three. Across also skips Arbitrum to HyperCore, so its aggregate covers the same three corridors as deBridge." + - "Aggregation: the headline number is the average of per-corridor 24h medians over the corridors a provider actually quotes. The TRUMP to BRETT calibration route is excluded. deBridge does not quote the Arbitrum to HyperCore corridor, so its aggregate covers the remaining three. Across also skips Arbitrum to HyperCore, so its aggregate covers the same three corridors as deBridge." - "Normalization: cost is quoted USD in minus quoted USD delivered, the same all-in definition for every provider. Near Intents bundles the destination chain withdrawal fee into its quoted amountOut (the 1Click API documents withdrawFee as already deducted), so no extra gas term is added on top." - "Regions: eu-west only at present. The us-east and sgp probes are temporarily paused, so the live figures reflect the EU-West (Paris) origin; the other two tabs return when those probes resume." - "Failures (quote_failed, unsupported route) excluded from cost aggregates and counted toward success rate." @@ -85,7 +85,7 @@ findings: - "{{name:mobula}} returns {{p50:mobula}} (p50, 24 h). Intent layers compress fees, slippage and destination gas into a single spread, so the effective number reads lower than aggregators that route through a paid underlying bridge." - "{{name:lifi}} sits at {{p50:lifi}} (p50, 24 h). Aggregators pay the underlying bridge plus a thin markup, so their effective cost trails intent layers on liquid corridors where multiple solvers compete on the same route." - "{{name:debridge}} clocks {{p50:debridge}} (p50, 24 h). Direct protocols front-load cost into a native-token fee, which dominates on $300 trades because the fixed component does not scale with notional." - - "{{name:across}} posts {{p50:across}} (p50, 24 h). Optimistic relayer fills price mostly relayer capital plus destination gas, so the number stays tight on same-asset USDC corridors and widens on the cross-asset USDT legs, where the quote wraps a DEX swap around the bridge and swap impact enters the all-in cost." + - "{{name:across}} posts {{p50:across}} (p50, 24 h). Optimistic relayer fills price mostly relayer capital plus destination gas, so the number stays tight across the same-asset USDC corridors of the triangle." - "{{worst_name}} trails the cross-corridor aggregate at {{worst_p50}} (p50, 24 h). On a $300 trade the gap between leader and laggard is mostly the fixed-fee floor of the slower path, not relative spread efficiency, and the laggard on one corridor can be the leader on another once corridor dimensions are surfaced." faq: diff --git a/harnesses/bridge-monitor/cmd/monitor/across_bridge_test.go b/harnesses/bridge-monitor/cmd/monitor/across_bridge_test.go index 16edfe418..cfafe5266 100644 --- a/harnesses/bridge-monitor/cmd/monitor/across_bridge_test.go +++ b/harnesses/bridge-monitor/cmd/monitor/across_bridge_test.go @@ -8,8 +8,10 @@ import ( // Live response captured 2026-07-08 from // GET https://app.across.to/api/swap/approval?tradeType=exactInput&amount=300000000 -// &inputToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&originChainId=8453 -// &outputToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&destinationChainId=42161 +// +// &inputToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&originChainId=8453 +// &outputToken=0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9&destinationChainId=42161 +// // (Base USDC to Arbitrum USDT, the cross-asset corridor). Trimmed to the // fields the harness decodes; values are verbatim. const acrossLiveSwapResponse = `{"crossSwapType":"anyToBridgeable","fees":{"total":{"amount":"1099243","amountUsd":"1.099199999999999955","token":{"decimals":6,"symbol":"USDC","address":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","name":"USD Coin","chainId":8453},"pct":"3664146565862634","details":{"type":"total-breakdown","swapImpact":{"amount":"-158088","amountUsd":"-0.15808239547799996","token":{"decimals":6,"symbol":"USDC","address":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","name":"USD Coin","chainId":8453},"pct":"-526962396755870"},"app":{"amount":"0","amountUsd":"0.0","token":{"decimals":6,"symbol":"USDT","address":"0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9","name":"Tether USD","chainId":42161},"pct":"0"},"bridge":{"amount":"1258386","amountUsd":"1.257282395477999914","token":{"address":"0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2","decimals":6,"symbol":"USDT","chainId":8453},"pct":"4191108962618505","details":{"type":"across","lp":{"amount":"498437","amountUsd":"0.497999870750999996","token":{"address":"0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2","decimals":6,"symbol":"USDT","chainId":8453},"pct":"1660065971808872"},"relayerCapital":{"amount":"749162","amountUsd":"0.748504984925999928","token":{"address":"0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2","decimals":6,"symbol":"USDT","chainId":8453},"pct":"2495116421076843"},"destinationGas":{"amount":"6210800385526","amountUsd":"0.010777539801","token":{"chainId":42161,"address":"0x0000000000000000000000000000000000000000","decimals":18,"symbol":"ETH"},"pct":"35926569732789"}}}}}},"inputAmount":"300000000","expectedOutputAmount":"299156590","minOutputAmount":"298410564","expectedFillTime":2}` @@ -65,8 +67,8 @@ func TestAcrossRouteSupport(t *testing.T) { } for name, want := range map[string]bool{ "USDC_SOL_BASE": true, // Sol USDC to Base USDC - "USDC_BASE_USDT_ARB": true, // cross-asset via Swap API - "USDT_ARB_USDC_SOL": true, // cross-asset via Swap API + "USDC_BASE_ARB": true, // same-asset USDC via Swap API + "USDC_ARB_SOL": true, // same-asset USDC via Swap API "TRUMP_SOL_BRETT_BASE": false, // calibration route, excluded "USDC_ARB_HYPERCORE": false, // Across 999 is HyperEVM, not a HyperCore credit } { diff --git a/harnesses/bridge-monitor/cmd/monitor/balance.go b/harnesses/bridge-monitor/cmd/monitor/balance.go index 16de97af1..d81c174c3 100644 --- a/harnesses/bridge-monitor/cmd/monitor/balance.go +++ b/harnesses/bridge-monitor/cmd/monitor/balance.go @@ -194,11 +194,11 @@ func (bc *BalanceChecker) fillEVMFromChain(result map[string]map[string]float64, case "Base": reads = []tokenRead{{baseUSDCAddr, []string{"USDC"}}} case "Arbitrum": - // Both USDT0 and USDT symbols: cycle_sim reads "USDT0" (Mobula naming) - // while route helpers fall back to "USDT". + // Triangle is all-USDC now; USDC is the home leg. USDT0 kept as a + // read only to monitor any leftover after the USDT->USDC migration. reads = []tokenRead{ - {arbUSDTAddr, []string{"USDT0", "USDT"}}, {arbUSDCAddr, []string{"USDC"}}, + {arbUSDTAddr, []string{"USDT0", "USDT"}}, } default: return false diff --git a/harnesses/bridge-monitor/cmd/monitor/cycle_sim.go b/harnesses/bridge-monitor/cmd/monitor/cycle_sim.go index 648311927..822bbb567 100644 --- a/harnesses/bridge-monitor/cmd/monitor/cycle_sim.go +++ b/harnesses/bridge-monitor/cmd/monitor/cycle_sim.go @@ -30,7 +30,7 @@ func SimulateTriangleCycle(balances map[string]map[string]float64, tier float64) solUSDC := balances["Solana"]["USDC"] baseUSDC := balances["Base"]["USDC"] - arbUSDT := balances["Arbitrum"]["USDT0"] + arbUSDC := balances["Arbitrum"]["USDC"] // Per-bridge leg requirement is 1×tier (not 3×): one TX at a time, settlement // replenishes the next leg before it has to source. @@ -60,15 +60,15 @@ func SimulateTriangleCycle(balances map[string]map[string]float64, tier float64) return sim } - // R3: Arb USDT → Sol USDC. R2 of current bridge deposited ~tier × 0.98 on Arb. - arbEff := arbUSDT + need*netFactor + // R3: Arb USDC → Sol USDC. R2 of current bridge deposited ~tier × 0.98 on Arb. + arbEff := arbUSDC + need*netFactor if arbEff < need { - sim.BlockLeg = "R3 Arb USDT" + sim.BlockLeg = "R3 Arb USDC" sim.Needed = need sim.Available = arbEff - sim.Reason = fmt.Sprintf("R3 Arb→Sol blocked: need $%.2f USDT on Arbitrum (incl. R2 inflow ≈$%.2f), effective $%.2f", + sim.Reason = fmt.Sprintf("R3 Arb→Sol blocked: need $%.2f USDC on Arbitrum (incl. R2 inflow ≈$%.2f), effective $%.2f", need, need*netFactor, arbEff) - sim.RefillChain, sim.RefillToken, sim.RefillUSD = "Arbitrum", "USDT", need-arbEff + sim.RefillChain, sim.RefillToken, sim.RefillUSD = "Arbitrum", "USDC", need-arbEff return sim } diff --git a/harnesses/bridge-monitor/cmd/monitor/debridge_bridge.go b/harnesses/bridge-monitor/cmd/monitor/debridge_bridge.go index 031eb0c34..ccb6ed4c3 100644 --- a/harnesses/bridge-monitor/cmd/monitor/debridge_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/debridge_bridge.go @@ -35,8 +35,9 @@ func debridgeChainID(chain string) int64 { // Debridge protocol charges a fixed amount in native tokens that we convert to USD // using live spot prices (5min cache). Hardcoded fallbacks are intentionally // conservative so an API outage doesn't make Debridge look artificially cheap. -// Solana: 0.015 SOL native fix fee -// EVM: 0.001 ETH native fix fee +// +// Solana: 0.015 SOL native fix fee +// EVM: 0.001 ETH native fix fee func debridgeFixFeeUSD(fromChain string) float64 { switch strings.ToLower(fromChain) { case "solana": @@ -56,9 +57,9 @@ type DebridgeQuoteResponse struct { ApproximateUsdValue float64 `json:"approximateUsdValue"` } `json:"dstChainTokenOut"` } `json:"estimation"` - FixFee string `json:"fixFee"` - ProtocolFeeApproximateUsdValue float64 `json:"protocolFeeApproximateUsdValue"` - Order struct { + FixFee string `json:"fixFee"` + ProtocolFeeApproximateUsdValue float64 `json:"protocolFeeApproximateUsdValue"` + Order struct { ApproximateFulfillmentDelay int64 `json:"approximateFulfillmentDelay"` } `json:"order"` ErrorMessage string `json:"errorMessage,omitempty"` diff --git a/harnesses/bridge-monitor/cmd/monitor/gas_topup.go b/harnesses/bridge-monitor/cmd/monitor/gas_topup.go index 597dd7a3f..6e6f7d14d 100644 --- a/harnesses/bridge-monitor/cmd/monitor/gas_topup.go +++ b/harnesses/bridge-monitor/cmd/monitor/gas_topup.go @@ -27,7 +27,7 @@ const gasTierSafeBuffer = 1.20 const gasMinMeaningfulUSD = 5.0 type gasChain struct { - Chain string + Chain string NativeSym string // PrimaryStable is the stablecoin sold first for gas (contract address). PrimaryStable string diff --git a/harnesses/bridge-monitor/cmd/monitor/metrics.go b/harnesses/bridge-monitor/cmd/monitor/metrics.go index 94705b692..dca778afe 100644 --- a/harnesses/bridge-monitor/cmd/monitor/metrics.go +++ b/harnesses/bridge-monitor/cmd/monitor/metrics.go @@ -184,7 +184,7 @@ func initSelfHealingMetrics() { bridgeTierDowngraded.WithLabelValues("30", "3").Set(0) bridgeStrandedHours.WithLabelValues("Solana", "USDC").Set(0) bridgeStrandedHours.WithLabelValues("Base", "USDC").Set(0) - bridgeStrandedHours.WithLabelValues("Arbitrum", "USDT0").Set(0) + bridgeStrandedHours.WithLabelValues("Arbitrum", "USDC").Set(0) for _, chain := range []string{"Solana", "Base", "Arbitrum"} { for _, outcome := range []string{"attempted", "succeeded", "failed", "capped", "gated"} { bridgeGasTopup.WithLabelValues(chain, outcome).Add(0) diff --git a/harnesses/bridge-monitor/cmd/monitor/mobula_bridge.go b/harnesses/bridge-monitor/cmd/monitor/mobula_bridge.go index 51838c4e5..51c25d17c 100644 --- a/harnesses/bridge-monitor/cmd/monitor/mobula_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/mobula_bridge.go @@ -337,7 +337,7 @@ func GetTestRoutes() []TestRoute { // USDC Triangle (self-balancing): // R1: Solana USDC → Base USDC // R2: Base USDC → Arbitrum USDT - // R3: Arbitrum USDT → Solana USDC + // R3: Arbitrum USDC → Solana USDC // // Meme Route (separate, weekly only): // R4: TRUMP (Solana) → BRETT (Base) @@ -363,23 +363,23 @@ func GetTestRoutes() []TestRoute { IsSolanaSrc: true, WeeklyOnly: false, }, - // R2: Base USDC → Arbitrum USDT + // R2: Base USDC → Arbitrum USDC (all-USDC triangle) { - Name: "USDC_BASE_USDT_ARB", + Name: "USDC_BASE_ARB", FromChain: "Base", FromChainAPI: "evm:8453", FromToken: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", ToChain: "Arbitrum", ToChainAPI: "evm:42161", - ToToken: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + ToToken: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", Amounts: []float64{5, 50, 300}, UsdAmounts: []float64{5, 50, 300}, IsSolanaSrc: false, WeeklyOnly: false, }, - // R3: Arbitrum USDT → Solana USDC (completes the triangle) + // R3: Arbitrum USDC → Solana USDC (completes the all-USDC triangle) { - Name: "USDT_ARB_USDC_SOL", + Name: "USDC_ARB_SOL", FromChain: "Arbitrum", FromChainAPI: "evm:42161", - FromToken: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + FromToken: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", ToChain: "Solana", ToChainAPI: "solana:solana", ToToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", Amounts: []float64{5, 50, 300}, diff --git a/harnesses/bridge-monitor/cmd/monitor/nearintents_bridge.go b/harnesses/bridge-monitor/cmd/monitor/nearintents_bridge.go index 2958a8e65..9625b4daa 100644 --- a/harnesses/bridge-monitor/cmd/monitor/nearintents_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/nearintents_bridge.go @@ -92,12 +92,12 @@ type NearIntentsQuoteRequest struct { type NearIntentsQuoteResponse struct { Quote struct { - AmountIn string `json:"amountIn"` - AmountInFormatted string `json:"amountInFormatted"` - AmountInUsd string `json:"amountInUsd"` - AmountOut string `json:"amountOut"` + AmountIn string `json:"amountIn"` + AmountInFormatted string `json:"amountInFormatted"` + AmountInUsd string `json:"amountInUsd"` + AmountOut string `json:"amountOut"` AmountOutFormatted string `json:"amountOutFormatted"` - AmountOutUsd string `json:"amountOutUsd"` + AmountOutUsd string `json:"amountOutUsd"` // timeEstimate is the solver-reported settlement ETA in seconds. TimeEstimate float64 `json:"timeEstimate"` } `json:"quote"` diff --git a/harnesses/bridge-monitor/cmd/monitor/reaper.go b/harnesses/bridge-monitor/cmd/monitor/reaper.go index 509fe0291..42e5c4246 100644 --- a/harnesses/bridge-monitor/cmd/monitor/reaper.go +++ b/harnesses/bridge-monitor/cmd/monitor/reaper.go @@ -32,7 +32,7 @@ const reaperMaxTransferUSD = 30 * rebalanceBufferFactor var homeTokens = map[string]map[string]bool{ "Solana": {"USDC": true, "SOL": true, "TRUMP": true}, "Base": {"USDC": true, "ETH": true, "BRETT": true}, - "Arbitrum": {"USDT0": true, "USDT": true, "USDC": true, "ETH": true}, + "Arbitrum": {"USDC": true, "ETH": true}, } // Stranded tokens we know how to route home. Anything else is alert-only: @@ -156,11 +156,7 @@ func reaperTick(fetch func() (map[string]map[string]float64, bool, error), rebal // Home legs always report 0 so alert expressions have a baseline series. for _, leg := range triangleLegs { - sym := leg.Token - if leg.Chain == "Arbitrum" { - sym = "USDT0" - } - bridgeStrandedHours.WithLabelValues(leg.Chain, sym).Set(0) + bridgeStrandedHours.WithLabelValues(leg.Chain, leg.Token).Set(0) } for k, h := range hours { bridgeStrandedHours.WithLabelValues(k.Chain, k.Token).Set(h) diff --git a/harnesses/bridge-monitor/cmd/monitor/rebalancer.go b/harnesses/bridge-monitor/cmd/monitor/rebalancer.go index 01e621d71..1d8539a8c 100644 --- a/harnesses/bridge-monitor/cmd/monitor/rebalancer.go +++ b/harnesses/bridge-monitor/cmd/monitor/rebalancer.go @@ -95,7 +95,7 @@ type legSpec struct { var triangleLegs = []legSpec{ {Chain: "Solana", ChainAPI: "solana:solana", Token: "USDC", TokenAddr: solanaUSDCMint}, {Chain: "Base", ChainAPI: "evm:8453", Token: "USDC", TokenAddr: baseUSDCAddr}, - {Chain: "Arbitrum", ChainAPI: "evm:42161", Token: "USDT", TokenAddr: arbUSDTAddr}, + {Chain: "Arbitrum", ChainAPI: "evm:42161", Token: "USDC", TokenAddr: arbUSDCAddr}, } func findLeg(chain string) (legSpec, bool) { diff --git a/harnesses/bridge-monitor/cmd/monitor/relay_bridge.go b/harnesses/bridge-monitor/cmd/monitor/relay_bridge.go index 7350d1324..0f4db0f3d 100644 --- a/harnesses/bridge-monitor/cmd/monitor/relay_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/relay_bridge.go @@ -78,8 +78,8 @@ type RelayStepData struct { Data string `json:"data"` Value string `json:"value"` // Solana fields - Instructions []RelaySolanaInstruction `json:"instructions"` - AddressLookupTableAddresses []string `json:"addressLookupTableAddresses"` + Instructions []RelaySolanaInstruction `json:"instructions"` + AddressLookupTableAddresses []string `json:"addressLookupTableAddresses"` } type RelayQuoteResponse struct { @@ -96,9 +96,15 @@ type RelayQuoteResponse struct { TimeEstimate float64 `json:"timeEstimate"` } `json:"details"` Fees struct { - Gas struct{ AmountUsd string `json:"amountUsd"` } `json:"gas"` - RelayerGas struct{ AmountUsd string `json:"amountUsd"` } `json:"relayerGas"` - RelayerService struct{ AmountUsd string `json:"amountUsd"` } `json:"relayerService"` + Gas struct { + AmountUsd string `json:"amountUsd"` + } `json:"gas"` + RelayerGas struct { + AmountUsd string `json:"amountUsd"` + } `json:"relayerGas"` + RelayerService struct { + AmountUsd string `json:"amountUsd"` + } `json:"relayerService"` } `json:"fees"` // Transaction steps for execution (EVM or Solana) Steps []struct { diff --git a/harnesses/bridge-monitor/cmd/monitor/selfheal_test.go b/harnesses/bridge-monitor/cmd/monitor/selfheal_test.go index 4bd47b4c3..71458b07b 100644 --- a/harnesses/bridge-monitor/cmd/monitor/selfheal_test.go +++ b/harnesses/bridge-monitor/cmd/monitor/selfheal_test.go @@ -11,7 +11,7 @@ func balancesSnapshot(sol, base, arb float64) map[string]map[string]float64 { return map[string]map[string]float64{ "Solana": {"USDC": sol, "SOL": 30}, "Base": {"USDC": base, "ETH": 40}, - "Arbitrum": {"USDT0": arb, "ETH": 25}, + "Arbitrum": {"USDC": arb, "ETH": 25}, } } @@ -225,8 +225,8 @@ func TestStrandedHoursComputation(t *testing.T) { if _, ok := hours[strandKey{"Solana", "USDC"}]; ok { t.Error("home leg Solana/USDC reported as stranded") } - if _, ok := hours[strandKey{"Arbitrum", "USDT0"}]; ok { - t.Error("home leg Arbitrum/USDT0 reported as stranded") + if _, ok := hours[strandKey{"Arbitrum", "USDC"}]; ok { + t.Error("home leg Arbitrum/USDC reported as stranded") } // 7 hours later the same balance is still there: above the 6h default. diff --git a/harnesses/bridge-monitor/cmd/monitor/socket_bridge.go b/harnesses/bridge-monitor/cmd/monitor/socket_bridge.go index 5b640ebfe..4be0ab9a6 100644 --- a/harnesses/bridge-monitor/cmd/monitor/socket_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/socket_bridge.go @@ -51,11 +51,11 @@ func socketChainID(chain string) string { } type socketRoute struct { - OutputValueInUsd float64 `json:"outputValueInUsd"` - InputValueInUsd float64 `json:"inputValueInUsd"` - TotalGasFeesInUsd float64 `json:"totalGasFeesInUsd"` - ServiceTime int `json:"serviceTime"` - BridgeRoute struct { + OutputValueInUsd float64 `json:"outputValueInUsd"` + InputValueInUsd float64 `json:"inputValueInUsd"` + TotalGasFeesInUsd float64 `json:"totalGasFeesInUsd"` + ServiceTime int `json:"serviceTime"` + BridgeRoute struct { BridgeFeeAmount string `json:"bridgeFeeAmount"` } `json:"bridgeRoute"` } diff --git a/harnesses/bridge-monitor/cmd/monitor/squid_bridge.go b/harnesses/bridge-monitor/cmd/monitor/squid_bridge.go index e5772f69e..6f006a26b 100644 --- a/harnesses/bridge-monitor/cmd/monitor/squid_bridge.go +++ b/harnesses/bridge-monitor/cmd/monitor/squid_bridge.go @@ -74,10 +74,10 @@ type squidGasCost struct { type squidRouteResponse struct { Route struct { Estimate struct { - FromAmountUSD string `json:"fromAmountUSD"` - ToAmountUSD string `json:"toAmountUSD"` - FeeCosts []squidFeeCost `json:"feeCosts"` - GasCosts []squidGasCost `json:"gasCosts"` + FromAmountUSD string `json:"fromAmountUSD"` + ToAmountUSD string `json:"toAmountUSD"` + FeeCosts []squidFeeCost `json:"feeCosts"` + GasCosts []squidGasCost `json:"gasCosts"` EstimatedRouteDuration int `json:"estimatedRouteDuration"` } `json:"estimate"` } `json:"route"` diff --git a/harnesses/bridge-monitor/cmd/monitor/wallet_snapshot.go b/harnesses/bridge-monitor/cmd/monitor/wallet_snapshot.go index f08cec9ee..20c2a1c97 100644 --- a/harnesses/bridge-monitor/cmd/monitor/wallet_snapshot.go +++ b/harnesses/bridge-monitor/cmd/monitor/wallet_snapshot.go @@ -220,7 +220,7 @@ func runDailyPnL(bc *BalanceChecker, slack *SlackNotifier) { // // R1 source (Sol USDC): initial ≥ 3 × amount (no preceding inflow) // R2 source (Base USDC): initial + R1_inflow ≥ 3×amount -// R3 source (Arb USDT): initial + R2_inflow ≥ 3×amount +// R3 source (Arb USDC): initial + R2_inflow ≥ 3×amount // // where inflow ≈ 3×amount × (1 - avg_fee). Using a conservative 2% cumulative loss. func formatTierHealth(balances map[string]map[string]float64) string { @@ -228,7 +228,7 @@ func formatTierHealth(balances map[string]map[string]float64) string { solUSDC := balances["Solana"]["USDC"] baseUSDC := balances["Base"]["USDC"] - arbUSDT := balances["Arbitrum"]["USDT0"] + arbUSDC := balances["Arbitrum"]["USDC"] tiers := []float64{3, 30} // Simulate the cycle for a given tier using the sequential per-bridge model @@ -254,10 +254,10 @@ func formatTierHealth(balances map[string]map[string]float64) string { return result{r1OK: true, blockLeg: "R2 Base USDC", blockNeed: need, blockHave: baseEffective} } r.r2OK = true - arbEffective := arbUSDT + need*netFactor // R2 inflow + arbEffective := arbUSDC + need*netFactor // R2 inflow if arbEffective < need { - return result{r1OK: true, r2OK: true, blockLeg: "R3 Arb USDT", blockNeed: need, blockHave: arbEffective} + return result{r1OK: true, r2OK: true, blockLeg: "R3 Arb USDC", blockNeed: need, blockHave: arbEffective} } r.r3OK = true return r @@ -273,7 +273,7 @@ func formatTierHealth(balances map[string]map[string]float64) string { }{ {"R1 Sol USDC ", func(r result) bool { return r.r1OK }}, {"R2 Base USDC", func(r result) bool { return r.r2OK }}, - {"R3 Arb USDT ", func(r result) bool { return r.r3OK }}, + {"R3 Arb USDC ", func(r result) bool { return r.r3OK }}, } results := make(map[float64]result) for _, t := range tiers { @@ -308,12 +308,12 @@ func formatTierHealth(balances map[string]map[string]float64) string { // Can we cover it from other legs? (for R1 Sol blocker only — R2/R3 fed by upstream) if strings.HasPrefix(r.blockLeg, "R1") { - totalStable := solUSDC + baseUSDC + arbUSDT + totalStable := solUSDC + baseUSDC + arbUSDC if totalStable < r.blockNeed { grid.WriteString(fmt.Sprintf(" ⚠️ total stable pool $%.2f < $%.2f required — tier not viable without external top-up of $%.0f\n", totalStable, r.blockNeed, r.blockNeed-totalStable)) } else { - grid.WriteString(fmt.Sprintf(" → pull from Base USDC ($%.0f) or Arb USDT ($%.0f) to Sol USDC\n", baseUSDC, arbUSDT)) + grid.WriteString(fmt.Sprintf(" → pull from Base USDC ($%.0f) or Arb USDC ($%.0f) to Sol USDC\n", baseUSDC, arbUSDC)) } } }