Bridging between L2s with small size
Why I treat L2-to-L2 bridging as its own workflow
Bridging between L2s looks like a simple transfer, but the mechanics are different from a normal swap. The path you choose decides how long funds are locked, how many contracts you trust, and how many fees you pay on a small wallet.
This post covers the two main paths (canonical vs liquidity bridges), a quick fee-comparison script I use before sending anything, and the risk checks I run when the amount is under $1k.
How L2 bridges actually move funds
Canonical path: slow but minimal extra trust
Most optimistic rollups use a standard bridge that routes withdrawals back to Ethereum, then you deposit to the destination L2. On OP Stack chains, withdrawals through the Standard Bridge require a 7-day challenge period before finalization on L1. Optimism docs OP Stack withdrawals spec
Arbitrum’s official bridge has the same multi-day wait for withdrawals back to Ethereum, with the docs calling out a 7-8 day window before funds can be claimed. Arbitrum bridge docs
If you follow this path to move between L2s, you have at least two on-chain steps (withdraw to L1, then deposit to the destination). It is slower, but you are not depending on third-party liquidity to get paid out early.
Liquidity bridges: fast but more moving parts
Liquidity bridges front you funds on the destination chain and settle the accounting later. Hop does this with market makers ("Bonders") who front liquidity using hTokens that are swapped in an AMM. Hop docs
Across uses a relayer model: you deposit into a Spoke Pool on the source chain, relayers pay you on the destination chain, and the relayer is reimbursed from a Hub Pool after an optimistic oracle verifies the relay. Across overview
These paths are much faster, but you are adding smart-contract and liquidity-provider trust to skip the standard bridge delay.
flowchart LR
A["L2 A"] -->|"Withdraw to L1 (challenge period)"| B["Ethereum L1"]
B -->|"Deposit"| C["L2 B"]
A -->|"Liquidity bridge payout"| C
My small-size bridging workflow
- I choose the destination L2 and confirm I can pay gas on both sides before I bridge anything.
- I check the canonical bridge path first, then compare a liquidity bridge if I need speed.
- I cap the first transfer to a small test amount (usually $100-$250) and wait for full settlement.
- I record the source tx hash, the destination receive tx, and the total time so I can compare future runs.
- I only scale to a larger transfer after the small test finishes without surprises.
Code: compare bridge paths with fee math
I do a quick fee check before committing. This is not a bridge quote engine; it is just a small calculator to compare the fees and expected net received once you have numbers from the bridge UIs.
from dataclasses import dataclass
@dataclass
class BridgeQuote:
name: str
send_amount_usd: float
bridge_fee_usd: float
slippage_usd: float
source_gas_usd: float
destination_gas_usd: float
def net_received(self) -> float:
"""Return expected amount received after fees and slippage."""
return self.send_amount_usd - self.bridge_fee_usd - self.slippage_usd
def total_cost(self) -> float:
"""Return all-in cost including gas on both chains."""
return self.bridge_fee_usd + self.slippage_usd + self.source_gas_usd + self.destination_gas_usd
def compare(quotes: list[BridgeQuote]) -> None:
"""Print a quick comparison of net received and total cost."""
for quote in quotes:
print(
f"{quote.name}: net ${quote.net_received():.2f}, "
f"total cost ${quote.total_cost():.2f}"
)
if __name__ == "__main__":
# Replace these placeholder numbers with quotes from the bridge UIs.
quotes = [
BridgeQuote(
name="Canonical via L1",
send_amount_usd=500,
bridge_fee_usd=0.00,
slippage_usd=0.00,
source_gas_usd=1.25,
destination_gas_usd=3.50, # L1 finalize + L2 deposit gas
),
BridgeQuote(
name="Liquidity bridge",
send_amount_usd=500,
bridge_fee_usd=2.40,
slippage_usd=0.60,
source_gas_usd=0.90,
destination_gas_usd=0.90,
),
]
compare(quotes)
If the total cost difference is only a couple of dollars, I default to the path with fewer moving parts. For a sub-$1k wallet, simplicity is a feature.
Risk analysis: L2-to-L2 bridging
- Technical risk (bridge mechanics). Standard bridges for optimistic rollups include a 7-day challenge period for withdrawals, which can lock funds longer than expected. Optimism docs OP Stack withdrawals spec
- Technical risk (liquidity bridge design). Liquidity bridges rely on relayers or bonders to front funds and get reimbursed later, which adds smart-contract and counterparty risk. Hop docs Across overview
- Economic risk (liquidity and slippage). AMM-based bridging can see worse rates when liquidity is thin, which matters more at small sizes where a few dollars is a large percentage. Hop docs
- Operational risk (gas on both chains). You often need gas on the source and destination chains, and canonical withdrawals require an L1 finalization step that costs gas. OP Stack withdrawals spec
Practical takeaways for a lean wallet
- Run a tiny test transfer first and record time-to-receive before you scale size.
- Prefer the simpler path when fee differences are small.
- Keep a small gas buffer on both chains before initiating any bridge.
- Treat liquidity bridges as a convenience tradeoff, not a risk-free upgrade.
Sources worth keeping open
- OP Stack Standard Bridge delay notes
- OP Stack withdrawals specification
- Arbitrum bridge user docs
- Hop protocol overview
- Across protocol overview