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

299 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](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:
```bash
# 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:
```bash
./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:**
```bash
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](../04-configuration/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:
```bash
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
- [TOKEN_MAPPING_AND_MAINNET_ADDRESSES.md](TOKEN_MAPPING_AND_MAINNET_ADDRESSES.md) — Full token mapping (138↔Mainnet), relay-supported tokens, and recommendations.
- [SEND_ETH_TO_MAINNET_REVERT_TRACE.md](SEND_ETH_TO_MAINNET_REVERT_TRACE.md) — Revert history and deployed LINK/native-ETH bridges.
- [scripts/README.md §8](../../scripts/README.md) — Send command and env.
- [services/relay/README.md](../../smom-dbis-138/services/relay/README.md) — Relay service deployment and config.
- [CONTRACT_ADDRESSES_REFERENCE.md](../11-references/CONTRACT_ADDRESSES_REFERENCE.md) — Chain 138 addresses.