Building Bridges

Overview

For the high-level flow of how Facet derives state and how canonical roots are exposed on L1 for bridges, see the Architecture Overview.

Facet has no canonical bridge, but anyone can build and deploy a bridge. This guide covers the complete process from understanding bridge mechanics to deploying production-ready contracts.

How Bridging Works

Architecture Overview

Bridges on Facet consist of two main components:

  1. L1 Contract: Handles deposits and withdrawals on Ethereum

  2. L2 Contract: Manages the L2 token representation

Depositing Assets (L1 → Facet)

1

Submit Deposit

User deposits assets (e.g., USDC, WETH) into an L1 bridge contract.

2

Bridge Emits Event

The bridge contract emits an event with the Facet event signature:

0x00000000000000000000000000000000000000000000000000000000000face7

This event contains the deposit details in RLP-encoded format.

3

Facet Nodes Process

Facet nodes monitor L1 for these events and convert them into L2 transactions from the L1 bridge contract to the L2 bridge contract.

4

Assets Available on Facet

The L2 bridge contract credits the user's L2 account with the bridged assets when it receives the L2 transaction.

Withdrawing Assets (Facet → L1)

Withdrawals use Facet's ZK Fault Proof system:

1

Initialize Withdrawal

User burns L2 assets through the bridge's L2 contract, which calls L2ToL1MessagePasser predeploy.

2

Wait for State Root

The withdrawal is included in a Facet state root. Proposers post roots to Rollup.sol.

3

Prove Withdrawal

Once the root is accepted, user submits a proof that their withdrawal exists.

4

Claim Assets

After any bridge-specific delays, user claims assets from the L1 bridge contract.

Implementation Guide

This section walks through the reference ETH bridge implementation in zk-fault-proofs. Source:

  • L1: https://github.com/0xFacet/zk-fault-proofs/blob/facet/contracts/src/L1Bridge.sol

  • L2: https://github.com/0xFacet/zk-fault-proofs/blob/facet/contracts/src/L2Bridge.sol

1) L1Bridge: Deposits, Proofs, Finalization

Key capabilities:

  • Deposits ETH to L2 with replay support if L2 blocks are full

  • Optional training wheels (pause, withdrawal delay, root blacklist)

  • Fork handling via setRollup (owner-controlled or renounced)

Core deposit data and send path:

EOA convenience path:

Training wheels and fork handling:

Withdrawal verification and finalization:

Trust model note: With ownership renounced, the bridge becomes human-free but permanently locked to one Rollup contract (fork). With ownership active, the operator can switch Rollup to follow new rules—introducing operator trust but preserving flexibility.

2) L2Bridge: Finalize Deposits, Initiate Withdrawals

Core responsibilities:

  • Finalize deposits from L1 with nonce-based replay protection

  • Initiate withdrawals to L1 by burning and calling the message passer

3) Putting It Together

End-to-end flow:

  • Deposit (L1 → L2): Users send ETH to L1Bridge.initiateDeposit. The bridge records the deposit (nonce + hash) and sends a Facet transaction to L2Bridge.finalizeDeposit. If an L2 block is full, replayDeposit can resend the same deposit (same nonce/params).

  • Withdraw (L2 → L1): Users call L2Bridge.initiateWithdrawal to burn wrapped ETH and create a message in L2ToL1MessagePasser. On L1, users prove the withdrawal via L1Bridge.proveWithdrawal using the canonical root in Rollup.sol and finalize after any delay.

Security & safety gears:

  • Replay-safe deposits (nonce + hash) and single-use finalize on L2

  • Optional pause, withdrawal delay, and root blacklist

  • Fork handling via setRollup (trust vs flexibility)

Last updated