- Institutional / JVMTM / reserve-provenance / GRU transport + standards JSON - Validation and verify scripts (Blockscout labels, x402, GRU preflight, P1 local path) - Wormhole wiring in AGENTS, MCP_SETUP, MASTER_INDEX, 04-configuration README - Meta docs, integration gaps, live verification log, architecture updates - CI validate-config workflow updates Operator/LAN items, submodule working trees, and public token-aggregation edge routes remain follow-up (see TODOS_CONSOLIDATED P1). Made-with: Cursor
16 KiB
CCIP Bridge ↔ Ethereum Mainnet Connection
Last Updated: 2026-03-29
Status: Active
Overview
Chain 138 does not use Chainlink’s public CCIP network (custom chain). Cross-chain sends from Chain 138 to Ethereum mainnet use:
- Chain 138: Custom router + WETH9 bridge (emits
MessageSent). - Mainnet: Deployed CCIPRelayRouter and CCIPRelayBridge that accept relayed messages.
- Relay service: Off-chain process that watches Chain 138 for
MessageSentand calls mainnet relay router to deliver.
Mainnet Contracts (Ethereum)
| Contract | Address | Role |
|---|---|---|
| CCIPRelayRouter | 0xAd9A228CcEB4cbB612cD165FFB72fE090ff10Afb |
Receives relayed messages; calls bridge ccipReceive. Relayer must have RELAYER_ROLE. |
| CCIPRelayBridge | 0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939 |
Holds WETH; releases to recipient when relay router calls ccipReceive. Must be funded with WETH for payouts. WETH9-only — no other tokens accepted. |
| WETH9 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
Canonical mainnet WETH. |
Token mapping: Chain 138 → Mainnet address mapping and which tokens the relay bridge supports are in TOKEN_MAPPING_AND_MAINNET_ADDRESSES.md. Source of truth: config/token-mapping.json.
Chain 138 Setup
| Role | Address | Notes |
|---|---|---|
| Router (LINK fee) | 0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 |
Emits MessageSent; relay service listens here. |
| Bridge (LINK fee) | 0xcacfd227A040002e49e2e01626363071324f820a |
Pay fee in Chain 138 LINK. Default in CCIPWETH9_BRIDGE_CHAIN138. |
| Bridge (native ETH fee) | 0x63cbeE010D64ab7F1760ad84482D6cC380435ab5 |
Pay fee in native ETH. |
Both bridges have mainnet destination set to CCIPRelayBridge (0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939), so all 138→mainnet sends are delivered via the relay.
End-to-End Flow
- User on Chain 138 calls bridge
sendCrossChain(mainnetSelector, recipient, amount)(e.g. viascripts/bridge/run-send-cross-chain.sh). - Bridge pulls WETH from user, calls router
ccipSend(...)withreceiver = abi.encode(CCIPRelayBridge). - Router emits
MessageSent(no Chainlink relayer). - Relay service (Node) watches the Chain 138 router for
MessageSent, buildsAny2EVMMessage, and calls mainnet CCIPRelayRouter.relayMessage(CCIPRelayBridge, message). - Relay router calls CCIPRelayBridge.ccipReceive(message); bridge transfers WETH to
recipienton mainnet.
Running the Relay Service
-
Fund mainnet CCIPRelayBridge with WETH so it can pay recipients:
# Option A: Script (transfers deployer's full WETH balance by default) ./scripts/bridge/fund-mainnet-relay-bridge.sh # Option B: Specific amount (wei) ./scripts/bridge/fund-mainnet-relay-bridge.sh 1000000000000000000 # Or manually: cast send 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \ "transfer(address,uint256)" \ 0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939 \ <amount_wei> \ --rpc-url $ETHEREUM_MAINNET_RPC --private-key $PRIVATE_KEY --legacyIf the default RPC rate-limits (429), set
ETHEREUM_MAINNET_RPCto Infura or Alchemy insmom-dbis-138/.env. -
Grant relayer role (if not already): The relay tx will revert with "transaction execution reverted" (no revert data) until the relayer address has
RELAYER_ROLEon the mainnet router. As the router's admin (deployer), run:./scripts/bridge/grant-relayer-role-mainnet.shOr manually:
cast send 0xAd9A228CcEB4cbB612cD165FFB72fE090ff10Afb "grantRelayerRole(address)" 0x4A666F96fC8764181194447A7dFdb7d471b301C8 --rpc-url $ETHEREUM_MAINNET_RPC --private-key $PRIVATE_KEY --legacy -
Start the relay service:
cd smom-dbis-138/services/relay # .env: RPC_URL_138, RPC_URL_MAINNET or ETHEREUM_MAINNET_RPC (Infura/Alchemy recommended to avoid 429), PRIVATE_KEY (relayer), CCIP_RELAY_* npm startFor mainnet RPC, set
RPC_URL_MAINNETinservices/relay/.envorETHEREUM_MAINNET_RPCinsmom-dbis-138/.env. Prefer Infura (https://mainnet.infura.io/v3/<PROJECT_ID>) or Alchemy; see RPC_ENDPOINTS_MASTER.md.
Config defaults in services/relay/src/config.js point to the router and bridges above; override with env vars if needed.
If relay tx reverts with "transaction execution reverted"
- Relayer role: Ensure the relayer has
RELAYER_ROLE: run./scripts/bridge/grant-relayer-role-mainnet.sh(useRPC_URL_MAINNET=https://ethereum.publicnode.comif Infura returns 403). - Bridge WETH: The mainnet CCIPRelayBridge must hold at least the amount being relayed. If the bridge balance is lower than the transfer amount, fund it:
(1e15 wei = 0.001 WETH.)
RPC_URL_MAINNET=https://ethereum.publicnode.com ./scripts/bridge/fund-mainnet-relay-bridge.sh 1000000000000000
Live Execution Evidence
2026-03-29 — Chain 138 to Ethereum mainnet test send
Source-chain send
- Chain 138 bridge:
0xcacfd227A040002e49e2e01626363071324f820a - Source router:
0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 - Source tx hash:
0x5c4aab3d425c8d85b5f64eba595f6a107e1034009ae74a8e5647ad6639032566 - Chain 138 block:
3403748 - Message ID:
0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc - Recipient:
0x4A666F96fC8764181194447A7dFdb7d471b301C8 - Amount:
10000000000000000wei (0.01 WETH) - Status: source-chain tx
success
Verified source evidence
MessageSentwas emitted by the Chain 138 router in source tx0x5c4aab3d425c8d85b5f64eba595f6a107e1034009ae74a8e5647ad6639032566.- The router event encoded the expected destination bridge
0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939, recipient0x4A666F96fC8764181194447A7dFdb7d471b301C8, and amount0.01 WETH.
Initial destination-chain verification
- Destination bridge checked:
0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939 - Ethereum receiver bridge has live code and reports
weth9() = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2. processedTransfers(0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc)remainedfalseacross repeated checks at:2026-03-30T02:44:48Z2026-03-30T02:45:48Z2026-03-30T02:46:50Z2026-03-30T02:47:52Z2026-03-30T02:48:53Z
- No
CrossChainTransferCompletedlogs were found on the destination bridge over Ethereum block range24765000..latestduring the initial verification window.
Relay repair and replay execution
- Host repaired:
r630-01 - Local repo relay implementation was newer than the deployed host version:
- replaced deployed
services/relay/src/config.js - replaced deployed
services/relay/src/RelayService.js
- replaced deployed
- Disabled stale relay override file on the host:
- prior
services/relay/.env.localwas pointing at an old Chain 138 router and bridge - relay now uses the current source router
0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 - relay now uses the current source bridge
0xcacfd227A040002e49e2e01626363071324f820a
- prior
- Replay window used for repair:
- temporarily set
START_BLOCK=3403747 - restarted
ccip-relay.service
- temporarily set
- Verified relay recovery from journal:
- relay re-detected the historical
MessageSent - relay queued message ID
0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc - relay submitted destination tx
0x87e3d401c498781fabb1289be283af2244add3ab768dfca00027e9d4e270318d
- relay re-detected the historical
Destination replay transaction result
- Destination relay tx:
0x87e3d401c498781fabb1289be283af2244add3ab768dfca00027e9d4e270318d - Ethereum block:
24767607 - Status:
0 (failed) - Gas used:
65823 - Router-level revert surfaced by receipt:
CCIPRelayRouter: relay failed
- After the failed replay:
processedTransfers(0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc)stillfalse- relay bridge WETH balance remained
2634280582011289wei (0.002634280582011289 WETH) - no
CrossChainTransferCompletedevent was emitted
SwapRouter / EnhancedSwapRouter / pool findings
SwapRouter(0xC2FA05F12a75Ac84ea778AF9D6935cA807275E55)- WETH balance:
0 - ETH balance:
0
- WETH balance:
EnhancedSwapRouter(0x53Bb0218483A189eBd6AE8Ec87139aeb93423E00)- WETH balance:
0 - ETH balance:
0
- WETH balance:
LiquidityPoolETH(0x603e078eb5Cca4F5c817A2F76D073f924D7272d3) showed accounting drift on Ethereum mainnet:- actual native ETH balance:
0 - actual WETH balance:
500125031257814wei (0.000500125031257814 WETH) - contract accounting still reported:
- ETH available liquidity:
15000000000000000wei (0.015 ETH) - WETH available liquidity:
1000000000000000wei (0.001 WETH)
- ETH available liquidity:
- actual native ETH balance:
- The relayer address
0x4A666F96fC8764181194447A7dFdb7d471b301C8is recorded as the LP for those pool balances, but direct withdrawals proved the accounting drift is real:- ETH-side withdrawal reverted with
LiquidityPoolETH: ETH transfer failed - WETH-side withdrawal reverted with
FailedInnerCall
- ETH-side withdrawal reverted with
- Operational consequence:
- neither
SwapRouternorEnhancedSwapRouteris a funding source for this payout - the trustless pool path cannot currently self-fund the missing relay-bridge WETH because the live balances do not match the pool’s own accounting
- neither
Verified blockers
-
Insufficient relay-bridge liquidity on Ethereum mainnet
- Mainnet relay bridge WETH balance at verification time:
2634280582011289wei (0.002634280582011289 WETH)
- Required payout for this message:
10000000000000000wei (0.01 WETH)
- Shortfall:
7365719417988711wei (0.007365719417988711 WETH)
- Deployer liquidity on mainnet at verification time:
- WETH balance:
0 - ETH balance:
3345428710812742wei (0.003345428710812742 ETH)
- WETH balance:
- Result: the message cannot be paid out on Ethereum mainnet until the relay bridge is funded by another mainnet wallet.
- Mainnet relay bridge WETH balance at verification time:
-
Relay service source polling instability
- Host:
r630-01 - Service:
ccip-relay.service - Status during verification:
active (running) - Journal showed repeated source filter errors:
eth_getFilterChanges ... Filter not found
- This was repaired on
2026-03-29: the deployed relay code was updated, the stale host.env.localoverride was disabled, and the message was successfully replayed into a destination tx. - The source-side relay issue is no longer the active blocker for this message.
- Host:
-
Destination-side execution is still blocked after relay repair
- The repaired relay successfully replayed the historical message and submitted destination tx
0x87e3d401c498781fabb1289be283af2244add3ab768dfca00027e9d4e270318d. - That tx mined and reverted in block
24767607with router-level revertCCIPRelayRouter: relay failed. - Because the relay bridge balance stayed unchanged and
processedTransfers(messageId)remainedfalse, the live failure is still downstream of source detection and upstream of payout completion.
- The repaired relay successfully replayed the historical message and submitted destination tx
Operational conclusion
- The Chain 138 send is verified.
- The original Ethereum receive was blocked by destination execution + liquidity/accounting, not by the source send.
2026-03-29 — Recovery path and successful completion
Bootstrap funding path that was actually used
138 -> Gnosiswas tested first but is not a live lane because Chain 138 emits through the custom router0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817while the native Gnosis bridge expects the public Chainlink router.138 -> Avalancheand138 -> BSCnative-bridge onward paths were evaluated next; Avalanche and BSC native CCIP continuation were not usable for direct onward mainnet forwarding because fee quoting on the official bridge path reverted.- The working recovery path was:
- send WETH from Chain 138 to the BSC relay-backed receiver
- unwrap received WETH into native BNB
- bridge BNB to Ethereum mainnet using LiFi / Across
- wrap enough ETH into WETH on mainnet
- fund the mainnet relay bridge
- manually replay the original historical message into
CCIPRelayRouter
Chain 138 to BSC recovery send
- Chain 138 WETH approve:
0xba583659911a3cc1a59bea74b5b80bdfb7298532a755cd8e9ac358abd220f11d - Chain 138 LINK approve:
0x5d285fa6e00dbdcb85ae31acfd99fd46f521a6b4939d5bfc65d4ee0f922dd7f5 - Chain 138 send tx:
0xe129f55a6c39988938fcb33e670bfe35d9ee8d8c46d9c5ffea4264db04587b23 - Chain 138 block:
3407959 - Recovery message ID on the BSC lane:
0x55a733ad50c86cb835726bcd77b9e8a8d8bae373bb9acf86f3aa0f2b9776b1fe - Send amount:
8000000000000000wei (0.008 WETH) - BSC destination completion tx on the relay bridge:
0xed9aee1eb6b2d7b8cd469cc462ce596b567430506fdee210106766c3c0313b6c - BSC destination block:
89548802 - Post-delivery verification:
processedTransfers(0x55a733ad50c86cb835726bcd77b9e8a8d8bae373bb9acf86f3aa0f2b9776b1fe) = true- BSC deployer wrapped balance became
8000000000000000
BSC unwrap and external bridge to Ethereum mainnet
- BSC unwrap tx:
0x55f2ae4804a958c0c66277c5f68f54ea7cc67d97f17eacd375dec2c63b853257 - BSC unwrap block:
89549047 - Unwrapped amount:
8000000000000000wei into native BNB - External bridge route used: LiFi
AcrossV4 - LiFi route id:
7ea2e28a-e54e-4575-8682-5f6ffb99ed73:0 - BSC external bridge tx:
0x5b55384778e40b4a603c9fe827d4fd49931ce5347835f5869c62d64a7f49c9f4 - BSC external bridge block:
89549501 - Bridged value:
15000000000000000wei native BNB - Quoted mainnet receive:
4485724498682976wei ETH
Mainnet relay-bridge funding
- Mainnet WETH wrap tx:
0x7d78e415ab876263100039d77475ccdfae71c06cbcaece3fbfae63f53248637d - Mainnet relay-bridge funding tx:
0x604a999ccf95ab915caa7b3d2175d5b61391f915441b388a6fe33a67c77ba841 - Mainnet funding blocks:
24768164and24768165 - Exact shortfall funded:
7365719417988711wei (0.007365719417988711 WETH) - Relay bridge balance after funding:
10000000000000000wei (0.01 WETH)
Manual replay that completed the original stuck message
- Manual replay tx:
0x7d1302d1e63c6e5957e3476e370a071797c4d5870cfbc81f2a55f4cf83dcb07d - Ethereum block:
24768179 - Status:
1 (success) - Gas used:
93643 - Transfer path observed in logs:
- WETH transferred from
0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939 - WETH received by
0x4A666F96fC8764181194447A7dFdb7d471b301C8 CrossChainTransferCompletedemitted by the relay bridgeMessageRelayedemitted by the relay router
- WETH transferred from
Final completion checks
processedTransfers(0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc) = true- Recipient mainnet WETH balance:
10000000000000000wei (0.01 WETH) - Relay bridge WETH balance after payout:
0 - Mainnet recipient ETH balance remained positive for gas after completion.
Operational conclusion
- The original Chain 138
0.01 WETHsend to Ethereum mainnet is now fully completed. - The failure mode was recoverable by sourcing missing liquidity from a relay-supported public-chain lane and manually replaying the original message after funding.
- For future incidents of this specific class, the fastest working runbook is:
- verify the original message ID is still unprocessed on mainnet
- source missing WETH through a relay-supported public-chain route
- fund the mainnet relay bridge with the exact shortfall
- replay the historical message directly into
CCIPRelayRouter.relayMessage(...)
References
- TOKEN_MAPPING_AND_MAINNET_ADDRESSES.md — Full token mapping (138↔Mainnet), relay-supported tokens, and recommendations.
- SEND_ETH_TO_MAINNET_REVERT_TRACE.md — Revert history and deployed LINK/native-ETH bridges.
- scripts/README.md §8 — Send command and env.
- services/relay/README.md — Relay service deployment and config.
- CONTRACT_ADDRESSES_REFERENCE.md — Chain 138 addresses.