Skip to content

bridges:bridge

Send tokens from the current chain to another chain, approving the bridge automatically when needed. The adapter defaults to CCTPv2 for native USDC, Across for other tokens, and the canonical bridge between an L2 and mainnet.

bridges:bridge <amount> <token> <to> <destChain>
NameTypeDescription
amountnumberAmount to bridge, in base units (wei)
tokenaddressToken to bridge (use @token(SYM); the native token resolves to the zero address)
tocommandKeyword to
destChainchainDestination chain name or id (e.g. optimism, base, 8453)
NameTypeDescription
--receiveraddressRecipient on the destination chain (defaults to the connected account)
--usingbridge-adapterAdapter: CCTPv2, Across, NativeBridge, LayerZero or CCIP (default: the best adapter for the token and lane)
--max-feenumberAbort when the bridge fee, in base units of , exceeds this bound
--remote-tokenaddressDestination-chain address of (NativeBridge ERC-20 transfers only)
--no-approveboolSkip the automatic allowance check and approve action
# Bridge 100 USDC from Ethereum to Base over CCTP
load bridges
switch mainnet
bridges:bridge 100e6 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 to base --using CCTPv2
# Bridge 1000 DAI to Optimism, paying out to someone else
load bridges
switch mainnet
bridges:bridge 1000e18 0x6B175474E89094C44Da98b954EedeAC495271d0F to optimism --receiver 0x59c2de8db2d1516bd9354ca31a58fea25eb37ba9
# Move 1 ETH to Optimism through the canonical bridge
load bridges
switch mainnet
bridges:bridge 1e18 0x0000000000000000000000000000000000000000 to optimism --using NativeBridge
AdapterCarriesDestination leg
CCTPv2Native USDC (burn/mint, no liquidity risk)Two-step: bridges:claim once Circle attests
AcrossERC-20s across L2s and mainnet, fastFilled by a relayer, nothing to claim
NativeBridgeThe canonical OP Stack / Arbitrum bridgesDeposits arrive automatically; withdrawals take ~7 days and need bridges:claim
LayerZeroTokens with an OFT or OFT adapter (pass the OFT as <token>)Delivered by an executor
CCIPTokens with a Chainlink CCIP poolExecuted by the DON

LayerZero and CCIP are never chosen implicitly — whether a given token is an OFT or has a CCIP pool is only knowable at runtime — so request them with --using.

Bridges are asynchronous, and the transfer id is the source-chain transaction hash. Once the bridge transaction has executed, poll it and finalize two-step bridges on the destination chain:

load bridges
load std
set $transferId 0x1234567890123456789012345678901234567890123456789012345678901234
print @bridges:status($transferId)

Inside sim:fork, switch moves between per-chain forks and bridge transfers are auto-relayed: when the script switches to the destination chain, the simulation executes the destination leg (a mocked Circle attestation for CCTP, an impersonated relayer fill for Across, and so on). No claim is needed — and the whole round trip is verifiable with sim:expect:

load bridges
load sim
sim:fork --using anvil (
bridges:bridge 100e6 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 to base --using CCTPv2
switch base
set $balance @get(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 "balanceOf(address)(uint256)" @me)
sim:expect @bool($balance > 0)
)

A transfer whose destination chain the script never switches to is reported at the end of the fork, so a forgotten switch doesn't pass silently.