Files
proxmox/docs/07-ccip/CCIP_BRIDGE_MAINNET_CONNECTION.md
defiQUG 7ac74f432b chore: sync docs, config schemas, scripts, and meta task alignment
- 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
2026-03-31 22:31:39 -07:00

16 KiB
Raw Blame History

CCIP Bridge ↔ Ethereum Mainnet Connection

Last Updated: 2026-03-29
Status: Active


Overview

Chain 138 does not use Chainlinks public CCIP network (custom chain). Cross-chain sends from Chain 138 to Ethereum mainnet use:

  1. Chain 138: Custom router + WETH9 bridge (emits MessageSent).
  2. Mainnet: Deployed CCIPRelayRouter and CCIPRelayBridge that accept relayed messages.
  3. Relay service: Off-chain process that watches Chain 138 for MessageSent and 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

  1. User on Chain 138 calls bridge sendCrossChain(mainnetSelector, recipient, amount) (e.g. via scripts/bridge/run-send-cross-chain.sh).
  2. Bridge pulls WETH from user, calls router ccipSend(...) with receiver = abi.encode(CCIPRelayBridge).
  3. Router emits MessageSent (no Chainlink relayer).
  4. Relay service (Node) watches the Chain 138 router for MessageSent, builds Any2EVMMessage, and calls mainnet CCIPRelayRouter.relayMessage(CCIPRelayBridge, message).
  5. Relay router calls CCIPRelayBridge.ccipReceive(message); bridge transfers WETH to recipient on mainnet.

Running the Relay Service

  1. 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 --legacy
    

    If the default RPC rate-limits (429), set ETHEREUM_MAINNET_RPC to Infura or Alchemy in smom-dbis-138/.env.

  2. Grant relayer role (if not already): The relay tx will revert with "transaction execution reverted" (no revert data) until the relayer address has RELAYER_ROLE on the mainnet router. As the router's admin (deployer), run:

    ./scripts/bridge/grant-relayer-role-mainnet.sh
    

    Or manually: cast send 0xAd9A228CcEB4cbB612cD165FFB72fE090ff10Afb "grantRelayerRole(address)" 0x4A666F96fC8764181194447A7dFdb7d471b301C8 --rpc-url $ETHEREUM_MAINNET_RPC --private-key $PRIVATE_KEY --legacy

  3. 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 start
    

    For mainnet RPC, set RPC_URL_MAINNET in services/relay/.env or ETHEREUM_MAINNET_RPC in smom-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"

  1. Relayer role: Ensure the relayer has RELAYER_ROLE: run ./scripts/bridge/grant-relayer-role-mainnet.sh (use RPC_URL_MAINNET=https://ethereum.publicnode.com if Infura returns 403).
  2. 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:
    RPC_URL_MAINNET=https://ethereum.publicnode.com ./scripts/bridge/fund-mainnet-relay-bridge.sh 1000000000000000
    
    (1e15 wei = 0.001 WETH.)

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: 10000000000000000 wei (0.01 WETH)
  • Status: source-chain tx success

Verified source evidence

  • MessageSent was emitted by the Chain 138 router in source tx 0x5c4aab3d425c8d85b5f64eba595f6a107e1034009ae74a8e5647ad6639032566.
  • The router event encoded the expected destination bridge 0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939, recipient 0x4A666F96fC8764181194447A7dFdb7d471b301C8, and amount 0.01 WETH.

Initial destination-chain verification

  • Destination bridge checked: 0xF9A32F37099c582D28b4dE7Fca6eaC1e5259f939
  • Ethereum receiver bridge has live code and reports weth9() = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2.
  • processedTransfers(0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc) remained false across repeated checks at:
    • 2026-03-30T02:44:48Z
    • 2026-03-30T02:45:48Z
    • 2026-03-30T02:46:50Z
    • 2026-03-30T02:47:52Z
    • 2026-03-30T02:48:53Z
  • No CrossChainTransferCompleted logs were found on the destination bridge over Ethereum block range 24765000..latest during 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
  • Disabled stale relay override file on the host:
    • prior services/relay/.env.local was 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
  • Replay window used for repair:
    • temporarily set START_BLOCK=3403747
    • restarted ccip-relay.service
  • Verified relay recovery from journal:
    • relay re-detected the historical MessageSent
    • relay queued message ID 0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc
    • relay submitted destination tx 0x87e3d401c498781fabb1289be283af2244add3ab768dfca00027e9d4e270318d

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) still false
    • relay bridge WETH balance remained 2634280582011289 wei (0.002634280582011289 WETH)
    • no CrossChainTransferCompleted event was emitted

SwapRouter / EnhancedSwapRouter / pool findings

  • SwapRouter (0xC2FA05F12a75Ac84ea778AF9D6935cA807275E55)
    • WETH balance: 0
    • ETH balance: 0
  • EnhancedSwapRouter (0x53Bb0218483A189eBd6AE8Ec87139aeb93423E00)
    • WETH balance: 0
    • ETH balance: 0
  • LiquidityPoolETH (0x603e078eb5Cca4F5c817A2F76D073f924D7272d3) showed accounting drift on Ethereum mainnet:
    • actual native ETH balance: 0
    • actual WETH balance: 500125031257814 wei (0.000500125031257814 WETH)
    • contract accounting still reported:
      • ETH available liquidity: 15000000000000000 wei (0.015 ETH)
      • WETH available liquidity: 1000000000000000 wei (0.001 WETH)
  • The relayer address 0x4A666F96fC8764181194447A7dFdb7d471b301C8 is 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
  • Operational consequence:
    • neither SwapRouter nor EnhancedSwapRouter is 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 pools own accounting

Verified blockers

  1. Insufficient relay-bridge liquidity on Ethereum mainnet

    • Mainnet relay bridge WETH balance at verification time:
      • 2634280582011289 wei (0.002634280582011289 WETH)
    • Required payout for this message:
      • 10000000000000000 wei (0.01 WETH)
    • Shortfall:
      • 7365719417988711 wei (0.007365719417988711 WETH)
    • Deployer liquidity on mainnet at verification time:
      • WETH balance: 0
      • ETH balance: 3345428710812742 wei (0.003345428710812742 ETH)
    • Result: the message cannot be paid out on Ethereum mainnet until the relay bridge is funded by another mainnet wallet.
  2. 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.local override 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.
  3. 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 24767607 with router-level revert CCIPRelayRouter: relay failed.
    • Because the relay bridge balance stayed unchanged and processedTransfers(messageId) remained false, the live failure is still downstream of source detection and upstream of payout completion.

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

  1. 138 -> Gnosis was tested first but is not a live lane because Chain 138 emits through the custom router 0x42DAb7b888Dd382bD5Adcf9E038dBF1fD03b4817 while the native Gnosis bridge expects the public Chainlink router.
  2. 138 -> Avalanche and 138 -> BSC native-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.
  3. 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: 8000000000000000 wei (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: 8000000000000000 wei 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: 15000000000000000 wei native BNB
  • Quoted mainnet receive: 4485724498682976 wei ETH

Mainnet relay-bridge funding

  • Mainnet WETH wrap tx: 0x7d78e415ab876263100039d77475ccdfae71c06cbcaece3fbfae63f53248637d
  • Mainnet relay-bridge funding tx: 0x604a999ccf95ab915caa7b3d2175d5b61391f915441b388a6fe33a67c77ba841
  • Mainnet funding blocks: 24768164 and 24768165
  • Exact shortfall funded: 7365719417988711 wei (0.007365719417988711 WETH)
  • Relay bridge balance after funding: 10000000000000000 wei (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
    • CrossChainTransferCompleted emitted by the relay bridge
    • MessageRelayed emitted by the relay router

Final completion checks

  • processedTransfers(0x19656fe758fc0e36ce5ce16ad9101e76c9eae19e5ed6bea08335dfb664215edc) = true
  • Recipient mainnet WETH balance: 10000000000000000 wei (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 WETH send 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:
    1. verify the original message ID is still unprocessed on mainnet
    2. source missing WETH through a relay-supported public-chain route
    3. fund the mainnet relay bridge with the exact shortfall
    4. replay the historical message directly into CCIPRelayRouter.relayMessage(...)

References