Skip to content
← Cross-Chain Bridges — From CCIP to Light Clients
Reading Real Bridges
Lesson 3 of 7·CONTENT18 min45 XP
Course
Cross-Chain Bridges — From CCIP to Light Clients
Lesson role
CONTENT
Sequence
3 / 7

Lesson 3 — OP Standard Bridge — the canonical L2 deposit/withdrawal pattern

Question

OP Mainnet processes billions of dollars through its standard bridge. The deposit path is fast (~1–3 min); the withdrawal path takes 7 days. That asymmetry is forced by the optimistic rollup trust model. Read the actual contracts and trace exactly why.

Principle (minimum model)

  • OP bridge has two contracts. L1StandardBridge on Ethereum L1, L2StandardBridge on OP L2. Deposit goes L1 → L2 (fast); withdrawal goes L2 → L1 (slow, 7-day challenge window).
  • Deposit path (~1–3 minutes). User calls L1StandardBridge.depositETHTo(...) → bridge emits ETHDepositInitiated + ETHBridgeInitiated and calls L1CrossDomainMessenger → messenger calls OptimismPortal2.depositTransaction, which emits TransactionDeposited → the rollup node derives an L2 deposit transaction from that log → on L2, L2CrossDomainMessenger relays the message to L2StandardBridge, which credits ETH (or mints the L2 token).
  • Withdrawal path (7 days). User calls L2StandardBridge.withdraw(...) (legacy) or bridgeETHTo / bridgeERC20To → bridge burns the L2 token (for bridged ERC-20s) → L2ToL1MessagePasser.initiateWithdrawal records the withdrawal → a proposer posts an output root to L1 as a dispute game → user calls OptimismPortal2.proveWithdrawalTransaction against that game → wait 7 days → finalizeWithdrawalTransaction on L1.
  • The 7-day window is the security model. Anyone can challenge an invalid output root through the fault dispute game. If the challenger wins, withdrawals proven against that game can't be finalized. On OP Mainnet a proven withdrawal waits proofMaturityDelaySeconds = 604800 (7 days), and the game must also have resolved and passed disputeGameFinalityDelaySeconds = 302400 (3.5 days) (read from the OP Mainnet portal, Oct 2026).
  • OptimismPortal is the trust anchor. It decides which withdrawals are valid. On OP Mainnet the deposited ETH sits in ETHLockbox (~181k ETH, Oct 2026), which only the portal can unlock; bridged ERC-20s sit in L1StandardBridge. A bug in the portal is catastrophic.
  • The messagePassing abstraction. OP's CrossDomainMessenger is the general-purpose primitive; StandardBridge is a specialisation for ETH/ERC-20. Custom L2 apps use CrossDomainMessenger directly.
  • Liquidity-provider bridges (e.g. Across) skip the 7-day wait. They front the user funds on L1 and reclaim from the canonical bridge after 7 days. Trade-off: a small fee, but instant withdrawal.

Worked example + steps

OP Standard Bridge — the canonical L2 deposit/withdrawal pattern

Every OP Stack chain — Optimism, Base, Mode, Worldchain, Zora — runs the same bridge. They don't each pick a multisig to attest messages or stand up a validator set; they all use the rollup's own state commitments and fault proofs as the bridge's security model. (Caveat: the contracts are upgradeable — on OP Mainnet by a 2-of-2 Safe, the ProxyAdminOwner in the superchain-registry — and a Guardian Safe can pause withdrawals. Those keys are part of the real trust model.) The bridge is just an interface to the rollup, and the rollup is the trust anchor. This is the canonical "trustless L1↔L2" reference, and reading it once teaches you a pattern you'll see (with variations) in every native L2 bridge you encounter.

1. The deposit flow

L1→L2 (deposit) is the easy direction:

sequenceDiagram
    participant User
    participant L1Bridge as L1StandardBridge (Ethereum)
    participant L1Msg as L1CrossDomainMessenger (Ethereum)
    participant Portal as OptimismPortal2 (Ethereum)
    participant L2Msg as L2CrossDomainMessenger (OP)
    participant L2Bridge as L2StandardBridge (OP)

    User->>L1Bridge: depositERC20(l1Token, l2Token, amount, ...)
    Note over L1Bridge: Lock ERC-20, emit ERC20DepositInitiated
    L1Bridge->>L1Msg: sendMessage(...)
    L1Msg->>Portal: depositTransaction(...)
    Note over Portal: Emit TransactionDeposited
    Note over L2Msg: Rollup node derives the deposit tx from the L1 log
    L2Msg->>L2Bridge: relayMessage → finalizeBridgeERC20(...)
    L2Bridge->>User: Mint OptimismMintableERC20 to user

Key insight: deposits are forced inclusions. The portal emits TransactionDeposited; the derivation rules make every L2 node include that deposit within the sequencing window (seq_window_size = 3600 L1 blocks, ~12 hours, on OP Mainnet per the superchain-registry). A sequencer that skips it produces blocks honest nodes reject. Any user can also call OptimismPortal2.depositTransaction directly to get an arbitrary L2 transaction in past a censoring sequencer.

This means deposits are trustless — the rollup's own derivation rules enforce inclusion. No multisig signs off on deposits.

2. The withdrawal flow

L2→L1 (withdrawal) is much harder:

sequenceDiagram
    participant User
    participant L2Bridge as L2StandardBridge (OP)
    participant Passer as L2ToL1MessagePasser (OP)
    participant DGF as DisputeGameFactory (Ethereum)
    participant Portal as OptimismPortal2 (Ethereum)
    participant L1Bridge as L1StandardBridge (Ethereum)

    User->>L2Bridge: withdraw(...) / bridgeERC20To(...)
    L2Bridge->>L2Bridge: Burn user's OptimismMintableERC20
    Note over L2Bridge: Emit WithdrawalInitiated
    L2Bridge->>Passer: (via L2CrossDomainMessenger) initiateWithdrawal(...)
    Note over DGF: Proposer creates a dispute game for an output root (~hourly)
    User->>Portal: proveWithdrawalTransaction(tx, gameIndex, outputRootProof, withdrawalProof)
    Note over Portal: Wait 7 days + game resolved and past finality delay
    User->>Portal: finalizeWithdrawalTransaction(tx)
    Portal->>L1Bridge: (via L1CrossDomainMessenger) finalizeBridgeERC20(...)
    L1Bridge->>User: Release locked L1 tokens

Three things make this slow:

  1. Output root proposal: a new dispute game roughly every hour on OP Mainnet (the last 94 games in its DisputeGameFactory span ~94 hours, Oct 2026)
  2. Proof maturity: 7 days after proving, and the dispute game must have resolved in the proposer's favour (so fault proofs can be played)
  3. Two-phase finalization: prove + finalize (separate transactions)

Total: ~7 days from withdrawal initiation to L1 settlement.

The waiting period exists so anyone can win a fault proof dispute game against a proposer who lies about the L2 state. Without it, a proposer could post a fake output root and the L1 contract would trust it.

3. Reading the real contracts

The canonical OP bridge code lives in ethereum-optimism/optimism, packages/contracts-bedrock/. Key files:

ContractRole
src/L1/L1StandardBridge.solL1 entry point for users (deposit) and exit point (withdraw); holds bridged ERC-20s
src/L2/L2StandardBridge.solL2 mirror — burns bridged tokens on withdrawal
src/L1/OptimismPortal2.solThe actual L1 inbox/outbox: depositTransaction, proveWithdrawalTransaction, finalizeWithdrawalTransaction
src/L1/ETHLockbox.solHolds deposited ETH on chains that use it (OP Mainnet does)
src/dispute/DisputeGameFactory.solOutput-root commitments on L1, as dispute games (replaced the old L2OutputOracle)
src/L1/L1CrossDomainMessenger.solGeneric message passing (not just tokens)
src/L2/L2ToL1MessagePasser.solL2 side of withdrawals: stores the withdrawal hashes that get proven on L1

The bridge is just the asset interface. Underneath, there's a generic cross-domain messenger that handles any calldata.

🔍 Find in repo. Open L1StandardBridge.sol. Trace what happens when depositERC20 is called. At what point is the deposit guaranteed to happen on L2?

Once the L1 transaction is final. The derivation rules force every L2 node to include the deposit (within the sequencing window), so a sequencer that drops it produces a chain honest nodes reject. The L1 contract itself never learns anything about L2 for a deposit; fault proofs matter only for the other direction.

4. The fast withdrawal market

7-day withdrawal is unusable for many use cases. The market response: third-party fast withdrawals.

A liquidity provider:

  1. Sees your withdrawal initiated on L2
  2. Sends you L1 tokens immediately (minus a fee)
  3. Waits 7 days
  4. Claims your L1 withdrawal when the period expires

The LP is taking on withdrawal risk (in case L1 state proof fails) in exchange for fee income. Intent-based bridges such as Across do this at scale.

This is not a bridge in the trust sense — it's a financial product layered on top of the trustless bridge. The trust split:

  • Bridge itself: trustless (rollup consensus)
  • Fast withdrawal LP: capital risk (no trust on user, just market efficiency)

5. The Standard Bridge vs Native Bridge

There is no separate "native bridge": the same L1StandardBridge / L2StandardBridge pair handles both.

  • ETH: depositETH / bridgeETHTo. On L2 it arrives as the native gas token (the deposit tx mints it).
  • ERC-20s: depositERC20 / bridgeERC20To. The L1 token is locked in L1StandardBridge; on L2 the bridge mints an OptimismMintableERC20 (usually created via OptimismMintableERC20Factory).

There is no registry to sign up for. Instead, the L2 token itself names its L1 counterpart (remoteToken), and the bridge checks the pair on every transfer (_isCorrectTokenPair in StandardBridge.sol). If the pair doesn't match, the bridge refuses. (The OP token is native to OP Mainnet; it isn't bridged in.)

For Tempo, this matters: if Tempo has a stablecoin native to its chain, and you want it on Ethereum, you'd need a Standard Bridge equivalent with the Tempo-Ethereum token pair registered.

6. Tempo↔Ethereum via OP Standard Bridge?

Tempo is not an OP Stack chain (it's a standalone L1). So the OP Standard Bridge doesn't directly apply. But the pattern does:

For Tempo↔Ethereum, the equivalent would be:

  • Tempo Standard Bridge (Solidity contracts on both sides)
  • Light client of Ethereum on Tempo
  • Light client of Tempo on Ethereum (the hard one)
  • Withdrawal challenge period (longer if no light client yet)

Until ZK light clients ship, this is CCIP territory — which we cover next lesson.

7. Reading exercise

In ethereum-optimism/optimism/packages/contracts-bedrock:

  1. src/L1/L1StandardBridge.sol — read the deposit function in full
  2. src/L1/OptimismPortal2.sol — find where L2 → L1 messages are proven and finalized
  3. src/dispute/DisputeGameFactory.sol — find the function proposers call to post an output root (create)
  4. Calculate: how many L1 transactions does a withdrawal require? Why?

Three answers: proveWithdrawalTransaction + wait + finalizeWithdrawalTransaction. Two L1 txs minimum.

8. The op-bridge ExEx pattern

From the Building tier's reorg-aware indexer lab, the op-bridge ExEx example is a real-world indexer that watches L1StandardBridge events:

sol!(L1StandardBridge, "l1_standard_bridge_abi.json");
use crate::L1StandardBridge::{
    ETHBridgeFinalized, ETHBridgeInitiated, L1StandardBridgeEvents,
};

This is how indexers (and bridges) consume cross-chain events. The ExEx runs inside an L1 Reth node, watches every block, decodes bridge events, and stores them in its own SQLite database. A bridge contract on the other side can query this index.

The same pattern works for Tempo: an ExEx on Tempo that watches CCIP bridge events would feed the merchant treasury system.

9. Reading list

Final check: in one sentence, why is the OP Standard Bridge "trustless" but requires a 7-day withdrawal delay? If your answer doesn't reference "optimistic security + fault proofs," re-read §2.

Summary (3 lines)

  • OP bridge = L1StandardBridge + L2StandardBridge over the cross-domain messengers. Deposit ~1–3 min (fast); withdrawal 7 days (slow — the fault-proof window is the security model).
  • Trust anchor = OptimismPortal2 (ETH in ETHLockbox on OP Mainnet); bug there is catastrophic. Upgrade and pause keys are part of the trust model. CrossDomainMessenger is the general primitive; StandardBridge is a specialisation for ETH/ERC-20.
  • Liquidity-provider bridges (e.g. Across) front user funds for instant withdrawal and reclaim from the canonical bridge after 7 days — fee in exchange for speed. Next lesson reads CCIP, Chainlink's cross-chain rail.