Co-authored-by: Cursor <cursoragent@cursor.com>
5.6 KiB
CCIP Bridge ↔ Ethereum Mainnet Connection
Last Updated: 2026-02-12
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
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.