Mainnet bridge router deploy — handoff
Session 2026-06-24 deployed Hyperlane core on sanect mainnet but stopped before deploying the bridge routers (SanectHypAdapter + WSanectHyp) because of a compile-time blocker. This doc captures the state, the blocker, and the recipe to finish in a dedicated session.
State at handoff
✅ Hyperlane core deployed on sanect mainnet (chain 7628). Mailbox at 0x2CB7861CC3dA9148763538C75DdCf5772e5c03e1. Full address table in CLAUDE.md "Hyperlane core (mainnet…)" section. ProxyAdmin owned by protocol-governance EOA 0x4BE2d1eC…0912. Trusted-relayer ISM trusting deployer 0xF88e82c5…0321.
✅ Local registry at ~/.hyperlane/chains/sanect/ has mainnet metadata + addresses. The original testnet addresses.yaml was stale (cached from the testnet bridge work 10 days earlier under chain name "sanect") — we removed it before deploying core. If a future operator works on testnet bridge again, they'll need to re-create that file or use a different chain name.
⛔ Bridge router deploy blocked — forge script DeployHyperlaneBridge fails to compile because:
dex/foundry.tomlhasskip = ["lib/hyperlane-monorepo/**", "contracts/bridge/**"](added in commitae73f49, Jun 15 2026, after OZ got bumped to v5 and Hyperlane's vendored contracts started failing because they importAddress.isContractwhich v5 removed).- The
@hyperlane-xyz/core/remapping isn't index/foundry.tomlat all. dex/lib/hyperlane-monorepo/is gitignored — installed locally viaforge install hyperlane-xyz/hyperlane-monorepobut never committed. May or may not be present on a given checkout.
The testnet bridge "just worked" on Jun 14 2026 because at THAT moment forge install OpenZeppelin/openzeppelin-contracts pulled OZ v4 (latest at that point). Later a re-install pulled v5, broke the bridge compile, and the skip was added rather than reverting OZ.
Recipe (next session)
1. Install the missing libs (if not already present)
cd /mnt/c/Github/sanect-node/dex
# OZ v4 alongside the existing v5 — pinned tag, separate dir
test -d lib/openzeppelin-contracts-v4 || \
git clone --depth 1 -b v4.9.6 \
https://github.com/OpenZeppelin/openzeppelin-contracts.git \
lib/openzeppelin-contracts-v4
# Hyperlane monorepo (if absent)
test -d lib/hyperlane-monorepo || \
forge install hyperlane-xyz/hyperlane-monorepo --no-commit --no-git2. Add a [profile.bridge] to dex/foundry.toml
Don't touch the default profile — the dex tree relies on OZ v5. Add a separate profile that switches to v4 + adds the Hyperlane remapping + clears the skip list:
[profile.bridge]
src = "contracts"
out = "out-bridge"
libs = ["lib"]
script = "script"
solc_version = "0.8.24"
optimizer = true
optimizer_runs = 999999
via_ir = false
skip = []
remappings = [
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-v4/contracts/",
"@hyperlane-xyz/core/=lib/hyperlane-monorepo/solidity/",
]Build target separate (out-bridge/) so the dex profile's out/ artifacts stay clean.
3. Verify compile
cd /mnt/c/Github/sanect-node/dex
FOUNDRY_PROFILE=bridge forge build --contracts contracts/bridgeMust succeed before going near a hardware wallet.
4. Deploy (assumes core mainnet addresses from current session)
export RPC_URL=https://rpc.sanect.com
export ETH_RPC_URL=https://ethereum-rpc.publicnode.com # or Alchemy
export DEPLOYER_PRIVATE_KEY=0x<role-EOA hex>
export SANECT_MAILBOX_ADDR=0x2CB7861CC3dA9148763538C75DdCf5772e5c03e1
export ETH_MAILBOX_ADDR=0xc005dc82818d67AF737725bD4bf75435d065D239
export WSNCT=0x4f35BD61FEd919aA9933C6333e388b3B7E1108Dc
cd /mnt/c/Github/sanect-node/dex
# 4a. SanectHypAdapter on sanect (~0.005 SNCT)
HYP_MAILBOX=$SANECT_MAILBOX_ADDR MODE=adapter WSNCT=$WSNCT \
FOUNDRY_PROFILE=bridge \
forge script script/DeployHyperlaneBridge.s.sol:DeployHyperlaneBridge \
--rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY -vvv
# Capture: ADAPTER=<deployed addr>
# 4b. WSanectHyp synthetic on Ethereum (~0.0006 ETH at 0.2 gwei)
HYP_MAILBOX=$ETH_MAILBOX_ADDR MODE=synthetic \
FOUNDRY_PROFILE=bridge \
forge script script/DeployHyperlaneBridge.s.sol:DeployHyperlaneBridge \
--rpc-url $ETH_RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY -vvv
# Capture: SYNTHETIC=<deployed addr>
# 4c. Enroll on sanect side (~0.0001 SNCT)
cast send $ADAPTER "enrollRemoteRouter(uint32,bytes32)" \
1 $(cast --to-uint256 $SYNTHETIC) \
--rpc-url $RPC_URL --private-key $DEPLOYER_PRIVATE_KEY
# 4d. Enroll on Ethereum side (~0.0001 ETH)
cast send $SYNTHETIC "enrollRemoteRouter(uint32,bytes32)" \
7628 $(cast --to-uint256 $ADAPTER) \
--rpc-url $ETH_RPC_URL --private-key $DEPLOYER_PRIVATE_KEY5. Stand up relayer
docs/mainnet-bridge-setup.md Step 3 (Docker container) — unchanged from the existing runbook. Pre-fund relayer wallet with ~0.02 ETH on Ethereum (covers ~300 inbound deliveries at current gas) and ~5 SNCT on sanect.
6. Round-trip test
docs/mainnet-bridge-setup.md Step 4 — 1-wei round trip.
7. Update CLAUDE.md
Add bridge router addresses to the "Hyperlane core (mainnet…)" section.
Notes for the next operator
- Deployer balance at handoff: 9.94 SNCT + 0.0647 ETH. Bridge router deploy + enrollment burns ~0.006 SNCT + ~0.001 ETH at current gas. Plenty of headroom.
- Stale
~/.hyperlane/chains/sanect/addresses.yamlwas deleted during this session. The current file has mainnet addresses — don't let testnet work overwrite it. - Verification on Blockscout: Hyperlane CLI failed to auto-verify every deployed contract because Blockscout returns HTML at
/apiinstead of Etherscan-shape JSON. Contracts work regardless. If we want the "Verified" badge onscan.sanect.com, paste source via Blockscout UI manually (Hyperlane contracts are all public source on the github monorepo). - Ethereum L1 canonical Mailbox (
0xc005dc…D239) hasn't changed in the Hyperlane ecosystem; safe to reuse. - Day-7 ISM upgrade: testnet uses single TrustedRelayerIsm. Mainnet should upgrade to
MessageIdMultisigIsm+ multi-validator set before meaningful bridge volume. See existingdocs/mainnet-bridge-setup.mdStep "Day-7 follow-up".