Table of Contents


Background

A number of teams across the Optimism Collective have been working together on the developments of a “fault proof” system for the OP Stack. Fault proof mechanisms allow parties to create and dispute claims about the result of some linear computation. In the case of the OP Stack, this fault proof is designed to allow users to permissionlessly create and dispute claims about the state of an L2 blockchain that can be used to send messages from the L2 to the L1 blockchain.

In practice, one of the most important use-cases for sending messages from L2 to L1 is user withdrawals of ETH and other ERC-20 tokens from an OP Stack chain’s bridge. The new systems and smart contracts introduced with the fault proof mechanism modify the process by which users prove and finalize withdrawal transactions. Given the critical nature of securing the tokens held within such a bridge, any changes to bridge smart contracts must be carefully scrutinized. Analyzing the security of these bridge changes is the primary focus of this engagement.

Let’s quickly run through the existing withdrawal process and understand how this process is changing with the introduction of fault proofs.

Existing Contracts

graph LR
    subgraph "External Contracts"
        ExternalERC20(External ERC20 Contracts)
        ExternalERC721(External ERC721 Contracts)
    end
    
    subgraph "L1 Smart Contracts"
        L1StandardBridge(L1StandardBridge)
        L1ERC721Bridge(L1ERC721Bridge)
        L1CrossDomainMessenger(L1CrossDomainMessenger)
        L2OutputOracle(L2OutputOracle)
        OptimismPortal(OptimismPortal)
        SuperchainConfig(SuperchainConfig)
        SystemConfig(SystemConfig)
    end
    
    subgraph "User Interactions"
        Users(Users)
    end
    
    subgraph "System Interactions"
        Proposer(PROPOSER)
        Guardian(CHALLENGER)
    end
    
    click L1StandardBridge "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1StandardBridge.sol>" "L1StandardBridge.sol"
	  click L1ERC721Bridge "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol>" "L1ERC721Bridge.sol"
	  click L1CrossDomainMessenger "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol>" "L1CrossDomainMessenger.sol"
	  click L2OutputOracle "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L2OutputOracle.sol>" "L2OutputOracle.sol"
	  click OptimismPortal "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol>" "OptimismPortal.sol"
	  click SuperchainConfig "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/SuperchainConfig.sol>" "SuperchainConfig.sol"
	  click SystemConfig "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/SystemConfig.sol>" "SystemConfig.sol"
    
    Proposer -->|propose state outputs| L2OutputOracle
    Guardian -->|remove invalid state outputs| L2OutputOracle
    
    ExternalERC20 <-->|mint/burn/transfer| L1StandardBridge
    ExternalERC721 <-->|mint/burn/transfer| L1ERC721Bridge
    
    L1StandardBridge <-->|send/receive message| L1CrossDomainMessenger
    L1ERC721Bridge <-->|send/receive message| L1CrossDomainMessenger
    L1CrossDomainMessenger <-->|package/send/receive message| OptimismPortal
    L1StandardBridge -.->|query pause state| SuperchainConfig
    L1ERC721Bridge -.->|query pause state| SuperchainConfig
    L1CrossDomainMessenger -.->|query pause state| SuperchainConfig
    OptimismPortal -.->|query pause state| SuperchainConfig
    OptimismPortal -.->|query config| SystemConfig
    OptimismPortal -.->|query proposed states| L2OutputOracle
    
    Users <-->|deposit/withdraw ETH/ERC20| L1StandardBridge
    Users <-->|deposit/withdraw ERC721| L1ERC721Bridge
    Users -->|prove/execute withdrawal transactions| OptimismPortal
    Users -->|replay messages| L1CrossDomainMessenger
    
    classDef extContracts stroke:#ff9,stroke-width:2px;
    classDef l1Contracts stroke:#bbf,stroke-width:2px;
    classDef l1EOA stroke:#bbb,stroke-width:2px;
    classDef userInt stroke:#f9a,stroke-width:2px;
    classDef systemUser stroke:#f9a,stroke-width:2px;
    classDef critical stroke:#0f0,stroke-width:2px;
    class ExternalERC20,ExternalERC721 extContracts;
    class L1StandardBridge,L1ERC721Bridge,L1CrossDomainMessenger,SuperchainConfig,SystemConfig l1Contracts;
    class Users userInt;
    class Batcher,Proposer,Guardian systemUser;
    class OptimismPortal,L2OutputOracle critical;

<aside> 💡 An editable and zoomable version of the above diagram is available here.

</aside>

Above is a diagram that illustrates the relationships between various OP Stack bridge smart contracts before the introduction of fault proofs. Most of these smart contracts are not changing during this update. Contracts highlighted in GREEN are being modified. The OptimismPortal contract located at the heart of the bridging system is on the receiving end of some of the largest necessary changes.

Before fault proofs, the OptimismPortal contract retrieves claims about the state of the L2 from the L2OutputOracle contract. Claims can only be published by the designated PROPOSER role and invalid claims can be removed by the designated CHALLENGER role. Users prove withdrawals to the OptimismPortal against these claims and can finalize their withdrawals as long as their selected claim was not later removed. In a moment we’ll take a look at how this process changes after fault proofs.

Fault Proof Contracts

graph LR
    subgraph "External Contracts"
        ExternalERC20(External ERC20 Contracts)
        ExternalERC721(External ERC721 Contracts)
    end
   
    subgraph "L1 Smart Contracts"
        L1StandardBridge(L1StandardBridge)
        L1ERC721Bridge(L1ERC721Bridge)
        L1CrossDomainMessenger(L1CrossDomainMessenger)
        OptimismPortal(OptimismPortal)
        SuperchainConfig(SuperchainConfig)
        SystemConfig(SystemConfig)
        DisputeGameFactory(DisputeGameFactory)
        FaultDisputeGame(FaultDisputeGame)
        DelayedWETH(DelayedWETH)
    end
    
    subgraph "User Interactions"
        Users(Users)
    end
    
    subgraph "System Interactions"
        Guardian(GUARDIAN)
    end
    
    click L1StandardBridge "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1StandardBridge.sol>" "L1StandardBridge.sol"
	  click L1ERC721Bridge "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol>" "L1ERC721Bridge.sol"
	  click L1CrossDomainMessenger "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol>" "L1CrossDomainMessenger.sol"
	  click L2OutputOracle "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/L2OutputOracle.sol>" "L2OutputOracle.sol"
	  click OptimismPortal "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol>" "OptimismPortal.sol"
	  click SuperchainConfig "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/SuperchainConfig.sol>" "SuperchainConfig.sol"
	  click SystemConfig "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/L1/SystemConfig.sol>" "SystemConfig.sol"
    click DisputeGameFactory "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol>" "DisputeGameFactory.sol"
    click FaultDisputeGame "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol>" "FaultDisputeGame.sol"
		click DelayedWETH "<https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock/src/dispute/weth/DelayedWETH.sol>" "DelayedWETH.sol"

    ExternalERC20 <-->|mint/burn/transfer| L1StandardBridge
    ExternalERC721 <-->|mint/burn/transfer| L1ERC721Bridge
    
    L1StandardBridge <-->|send/receive message| L1CrossDomainMessenger
    L1ERC721Bridge <-->|send/receive message| L1CrossDomainMessenger
    L1CrossDomainMessenger <-->|package/send/receive message| OptimismPortal
    L1StandardBridge -.->|query pause state| SuperchainConfig
    L1ERC721Bridge -.->|query pause state| SuperchainConfig
    L1CrossDomainMessenger -.->|query pause state| SuperchainConfig
    OptimismPortal -.->|query pause state| SuperchainConfig
    OptimismPortal -.->|query config| SystemConfig
    OptimismPortal -.->|query game address| DisputeGameFactory
    OptimismPortal -.->|query game state| FaultDisputeGame
    DisputeGameFactory -->|creates instances of| FaultDisputeGame
    FaultDisputeGame -->|store/retrieve bonds| DelayedWETH
    
    Users <-->|deposit/withdraw ETH/ERC20| L1StandardBridge
    Users <-->|deposit/withdraw ERC721| L1ERC721Bridge
    Users -->|prove/execute withdrawal transactions| OptimismPortal
    Users -->|create dispute games| DisputeGameFactory
    Users -->|participate in dispute games| FaultDisputeGame
    Users -->|replay messages| L1CrossDomainMessenger
    
		Guardian -->|blacklist dispute games| OptimismPortal
		Guardian -->|change respected game type| OptimismPortal
 
    classDef extContracts stroke:#ff9,stroke-width:2px;
    classDef l1Contracts stroke:#bbf,stroke-width:2px;
    classDef l1EOA stroke:#bbb,stroke-width:2px;
    classDef userInt stroke:#f9a,stroke-width:2px;
    classDef systemUser stroke:#f9a,stroke-width:2px;
    classDef critical stroke:#0f0,stroke-width:2px;
    class ExternalERC20,ExternalERC721 extContracts;
    class L1StandardBridge,L1ERC721Bridge,L1CrossDomainMessenger,SuperchainConfig,SystemConfig l1Contracts;
    class Users userInt;
    class Batcher,Proposer,Guardian systemUser;
    class OptimismPortal,DisputeGameFactory,FaultDisputeGame,DelayedWETH critical;

<aside> 💡 An editable and zoomable version of this diagram is available here.

</aside>

Above is a diagram that illustrates the relationships between various OP Stack bridge smart contracts after the introduction of fault proofs. Contracts that have been modified are highlighted in GREEN.

Most notable in these changes is the removal of the L2OutputOracle contract and the introduction of the DisputeGameFactory in its place. Unlike the L2OutputOracle, the DisputeGameFactory does not place limitations on which addresses can create claims about the state of the L2. Any user can trigger the DisputeGameFactory to create a new claim and a corresponding FaultDisputeGame so that any other user may argue about the validity of that claim.

Instead of proving withdrawals against proposals in the L2OutputOracle, users now prove withdrawals against claims that exist as FaultDisputeGame contracts. FaultDisputeGame instances resolve either for or against the claim after a certain amount of time. If a game resolves in favor of a claim, then that claim can be used to finalize withdrawals inside of the OptimismPortal.

Additionally, the permissionless nature of the FaultDisputeGame contract necessitates the existence of a bonding system that requires users to stake ETH against the claims that they make. Bonds make the FaultDisputeGame contract “incentive compatible” such that participants should always be incentivized to “play the game” correctly. As bond sizes can become large relatively quickly, the DelayedWETH contract is introduced to act as a bond escrow contract that delays bond withdrawals to allow for recovery in case of an emergency.