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:
L1 Contract: Handles deposits and withdrawals on Ethereum
L2 Contract: Manages the L2 token representation
Depositing Assets (L1 → Facet)
Submit Deposit
User deposits assets (e.g., USDC, WETH) into an L1 bridge contract.
Bridge Emits Event
The bridge contract emits an event with the Facet event signature:
0x00000000000000000000000000000000000000000000000000000000000face7This event contains the deposit details in RLP-encoded format.
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.
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:
Initialize Withdrawal
User burns L2 assets through the bridge's L2 contract, which calls L2ToL1MessagePasser predeploy.
Wait for State Root
The withdrawal is included in a Facet state root. Proposers post roots to Rollup.sol.
Prove Withdrawal
Once the root is accepted, user submits a proof that their withdrawal exists.
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 toL2Bridge.finalizeDeposit. If an L2 block is full,replayDepositcan resend the same deposit (same nonce/params).Withdraw (L2 → L1): Users call
L2Bridge.initiateWithdrawalto burn wrapped ETH and create a message inL2ToL1MessagePasser. On L1, users prove the withdrawal viaL1Bridge.proveWithdrawalusing the canonical root inRollup.soland 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