FABRKNT
Cross-Chain Bridges — From CCIP to Light Clients
Reading Real Bridges
Lesson 3 of 7·CONTENT18 min45 XP

Treat this page as a workbench, not a blog post. The goal is to extract a reusable mental model from the source and carry it into the rest of the Fabrknt stack.

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 (~10 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 (~10 minutes). User calls L1StandardBridge.depositETHTo(...) → contract emits DepositInitiatedOptimismPortal queues a deposit transaction → OP sequencer reads the L1 log → mints corresponding tokens on L2.
  • Withdrawal path (7 days). User calls L2StandardBridge.withdraw(...) → contract burns tokens on L2 → after the state root is published on L1, user proves their withdrawal → challenge window opens → after 7 days, finalise on L1.
  • The 7-day window is the security model. Anyone can challenge a fraudulent state root during the window. If a fraud proof succeeds, the bad state root is rolled back. After 7 days, the state root is final.
  • OptimismPortal is the trust anchor. The contract that holds all locked ETH. A bug there is catastrophic — the contract itself is the trust anchor.
  • 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 (Across / Hop) 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 or stand up a validator set; they all use the rollup's own consensus as the bridge's security 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 Inbox as OptimismPortal (Ethereum)
    participant L2 as L2 chain
    participant L2Bridge as L2StandardBridge (Optimism)

    User->>L1Bridge: depositERC20(token, amount)
    L1Bridge->>Inbox: depositTransaction(...)
    Note over Inbox: Emit DepositInitiated event
    L2->>L2: Sequencer reads L1 events
    L2->>L2Bridge: finalizeBridgeERC20(user, token, amount)
    L2Bridge->>User: Mint wrapped token to user

Key insight: deposits are forced inclusions. The L1 contract emits an event; the L2 sequencer must process it within a deadline (e.g., ~1 hour). If not, anyone can force the inclusion via the L2 inbox.

This means deposits are trustless — the rollup's own consensus rules enforce inclusion. No multisig.

2. The withdrawal flow

L2→L1 (withdrawal) is much harder:

sequenceDiagram
    participant User
    participant L2Bridge as L2StandardBridge (Optimism)
    participant Output as L2OutputOracle (Ethereum)
    participant L1Bridge as L1StandardBridge (Ethereum)

    User->>L2Bridge: withdraw(token, amount)
    L2Bridge->>L2Bridge: Burn user's wrapped token
    Note over L2Bridge: Emit WithdrawalInitiated event
    L2->>Output: Submit state root (every ~1 hour)
    Note over Output: Wait 7-day challenge period
    User->>L1Bridge: proveWithdrawal(proof, output)
    L1Bridge->>L1Bridge: Verify Merkle proof of withdrawal
    L1Bridge->>L1Bridge: Wait challenge period
    User->>L1Bridge: finalizeWithdrawal()
    L1Bridge->>User: Transfer L1 tokens

Three things make this slow:

  1. State root submission: every ~1 hour (configurable)
  2. Challenge period: 7 days (so fraud proofs can be submitted)
  3. Two-phase finalization: prove + finalize (separate transactions)

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

The challenge period exists so anyone can submit a fraud proof if the sequencer lies about the L2 state. Without it, the sequencer could submit fraudulent state roots and the L1 contract would trust them.

3. Reading the real contracts

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

ContractRole
L1StandardBridge.solL1 entry point for users (deposit) and exit point (withdraw)
L2StandardBridge.solL2 mirror — burn wrapped tokens on withdrawal
OptimismPortal.solThe actual L1 inbox/outbox for cross-domain messages
L2OutputOracle.solStores L2 state root commitments on L1
L1CrossDomainMessenger.solGeneric message passing (not just tokens)

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 L1 contract certain the deposit is on L2?

It's certain when the L1 transaction lands. The L2 sequencer is forced (by protocol rules) to include the deposit. The trust assumption is: the rollup's consensus enforces sequencer behavior, and if the sequencer cheats, the rollup forks (fraud proof).

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. Markets like Across, Hop, and Connext 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

OP Stack has both:

  • Standard Bridge: maps ERC20s — for arbitrary tokens
  • Native Bridge: handles ETH (and OP token) directly

For tokens to be bridged via Standard Bridge, they need to be registered — both the L1 and L2 token addresses must be paired. Otherwise the bridge doesn't know what L2 representation to mint.

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. L1StandardBridge.sol — read the deposit function in full
  2. OptimismPortal.sol — find where L2 → L1 messages are received
  3. L2OutputOracle.sol — find the function that submits state roots
  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 your Building tier (L2 — Reorg-Aware Indexer), 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 watches every block, decodes bridge events, and stores them in its own 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 + fraud proofs," re-read §2.

Summary (3 lines)

  • OP bridge = L1StandardBridge + L2StandardBridge. Deposit ~10 min (fast); withdrawal 7 days (slow — the optimistic challenge window is the security model).
  • Trust anchor = OptimismPortal contract; bug there is catastrophic. CrossDomainMessenger is the general primitive; StandardBridge is a specialisation for ETH/ERC-20.
  • Liquidity-provider bridges (Across / Hop) 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.