owned this note
owned this note
Published
Linked with GitHub
# Optimism Transaction Trails Behind the Scenes, Current Version vs Bedrock
While Optimism has been operating since Nov of 2021 (see ["An Introduction to Optimism’s Optimistic Rollup" by Kyle Charbonnet](https://medium.com/privacy-scaling-explorations/an-introduction-to-optimisms-optimistic-rollup-8450f22629e8)), the Optimism team is hard at work for next major release of the Optimism network called "Bedrock". As the release is planned for the last quarter of 2022, it is time to take a look at its solidity contracts and understand transaction trails as well as security measures around them.
## Current Optimism
From [various articles](https://research.paradigm.xyz/optimism) describing how current Optimism works and [past audits](https://github.com/ethereum-optimism/optimism/tree/develop/technical-documents/audits), it seems the current version of Optimism had gone through a few major iterations, and the final deployed version has a much smaller scope than Optimism set out to do. Most notably, order of transactions in `CanonicalTransactionChain` is not guaranteed and fraud proof procedure is not currently in operation, despite its on-chain [vestige](https://etherscan.io/address/0x042065416C5c665dc196076745326Af3Cd840D15). This is quite understandable, given the current highly centralized stage of the project.
### Transaction flows
A user of Optimism interacts with the system through three different types of transactions: L1 to L2, L2 to L1 and L2 transactions.
Based on [current contracts](https://github.com/ethereum-optimism/optimism/tree/ffc906269c309fab847360af092bc1e82280be1c), the following diagram depicts how messages are passed through different components.
![Current Version](https://raw.githubusercontent.com/0xDatapunk/optimism/develop/packages/figures/OP-current%20OP.drawio.svg)
### L1 to L2 transactions
The most common L1 to L2 transactions are user deposits.
Note although it is claimed ["As of the OVM 2.0 update (Nov. 2021), the process of using ETH on L2 is identical to the process of using ETH in Ethereum."](https://community.optimism.io/docs/developers/build/differences/#using-eth-in-contracts), the current version actually still represents ETH as an ERC20 token at this L2 address `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000`.
A user can only deposit ETH or ERC20 tokens through the canonical bridge route, starting with a call to the `L1StandardBridge` proxy contract. The `L1StandardBridge` proxy contract acts as an escrow account for both ETH and ERC20 tokens and sends a `finalizeDeposit` message to the `L1CrossDomainMessenger` contract. The `L1CrossDomainMessenger` contract wraps the message inside `relayMessage`, then enqueues the data to `CanonicalTransactionChain`. The Optimism sequencer then picks up the data and sends it as calldata to `appendSequencerBatch(...)`. The rollup node sees the emitted `SequencerBatchAppended` event, finds the transaction in `appendSequencerBatch`'s calldata, unwraps the message and relays it to `L2CrossDomainMessenger` as if from aliased `L1CrossDomainMessenger` address. The `L2CrossDomainMessenger` forwards it to `L2StandardBridge`, which then mints new ERC20 tokens and finalizes the transfer to specified L2 user account.
To make things clear, let's follow [one ETH deposit transaction](https://etherscan.io/tx/0x564504f74e33e4d8e6c6a4a5e95edde639b566793fd51eb4bbcabd691b8cbc23) with the [corresponding L2 transaction](https://optimistic.etherscan.io/tx/0x08c6c6d3171df4ed4575cb37497126354bcbed1f4b157d11d32d83ca1f3db833) and see how the `L1StandardBridge` depositETH's calldata
```clike
0xb1a1a882 // L1StandardBridge: depositETH{value: 800000000000000000}(uint32,bytes)
0000000000000000000000000000000000000000000000000000000000030d40 // _l2Gas:
0000000000000000000000000000000000000000000000000000000000000040 // _data: start location, used as a tag for easy identification
0000000000000000000000000000000000000000000000000000000000000000 // _data: length
```
gets transformed into enqueued message through `L1CrossDomainMessanger` and received by `CanonicalTransactionChain`:
```clike
0x6fee07e0 // CanonicalTransactionChain: enqueue(address, uint256, bytes)
0000000000000000000000004200000000000000000000000000000000000007 // _target: L2_CROSS_DOMAIN_MESSENGER
0000000000000000000000000000000000000000000000000000000000030d40 // _gasLimit
0000000000000000000000000000000000000000000000000000000000000060 // _data: start location
00000000000000000000000000000000000000000000000000000000000001a4 // _data: length
cbd4ece9 // L2CrossDomainTransaction: relayMessage(address, address, bytes memory _message, uint256 _messageNonce)
0000000000000000000000004200000000000000000000000000000000000010 // _target: L2_STANDARD_BRIDGE
00000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1 // _sender: Proxy__OVM_L1StandardBridge
0000000000000000000000000000000000000000000000000000000000000080 // _message: start position
0000000000000000000000000000000000000000000000000000000000024f73 // _messageNonce: 151411
00000000000000000000000000000000000000000000000000000000000000e4 _message: length
662a633a // L2StandardBridge: finalizeDeposit(address,address,address,address,uint256,bytes)
0000000000000000000000000000000000000000000000000000000000000000 // _l1Token
000000000000000000000000deaddeaddeaddeaddeaddeaddeaddeaddead0000 // _l2Token: OVM_ETH
0000000000000000000000003fea91ee988f1ca7a627ba758af6fa7b78c259e3 // _from
0000000000000000000000003fea91ee988f1ca7a627ba758af6fa7b78c259e3 // _to
0000000000000000000000000000000000000000000000000b1a2bc2ec500000 // _amount: 800000000000000000
00000000000000000000000000000000000000000000000000000000000000c0 // _extraData: start location
0000000000000000000000000000000000000000000000000000000000000000 // _extraData: length
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
```
There are many checks along this route to ensure integrity of deposit transactions and prevent fake ones being inserted somewhere. Following money backwards from exit to initiation is a better way to discover any misses, so let's do just that (**CD** here stands for "**C**urrent version **D**eposit").
**CD.1** `L2StandardERC20` tokens can [only be minted by `L2StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/standards/L2StandardERC20.sol#L42).
**CD.2** Minting happens in [`finalizeDeposit(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141), which can only be called by [`L2CrossDomainMessanger`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol#L42) and requires `xDomainMessageSender` to be [`L1StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol#L47).
**CD.3** In case there is [a mismatch between L1 and L2 tokens](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L151), `finalizeDeposit(...)` issues a [withdraw transaction](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L171) to refund users on L1.
Before passing the `finalizeDeposit` along, [`relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce
)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol#L92) in `L2CrossDomainMessanger` conducts a series of verifications:
**CD.4.1** The `msg.sender` needs to be [aliased`L1CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol#L102) . This avoids contract address collision between L1 and L2. As an added benefit, it also prevents reentrancy.
**CD.4.2** Ensure the same deposit has [not been relayed before](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L187).
**CD.4.3** [`_target` is not `L2_TO_L1_MESSAGE_PASSER`](https://github.com/ethereum-optimism/optimism/blob/ffc906269c309fab847360af092bc1e82280be1c/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol#L123), so the call is not a circular call back to L1.
**CD.5** The `relayMessage` is called as if from [aliased`L1CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/ffc906269c309fab847360af092bc1e82280be1c/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol#L101) by rollup node, controlled by Optimism until decentralization, which monitors `SequencerBatchAppended(...)` emitted by `appendSequencerBatch()` in `CanonicalTransactionChain` for new deposits, then relays the message as if from the `from` in the calldata.
**CD.6** Enqueued messages are [appended into sequencerBatch](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/rollup/CanonicalTransactionChain.sol#L286), by sequencer, an centralized entity controlled by Optimism, which monitors `TransactionEnqueued(...)` emitted by `enqueue(...)`.
**CD.7** `enqueue(...)` can only be called by `sendMessage(...)` from `L1CrossDomainMessenger` to fulfill **CD.4.1**.
**CD.8** `L1CrossDomainMessanger` has a [`replayMessage(...)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L231) callable by anyone, in case not enough gas is supplied to the initial deposit. However, since it is required to reuse the old [nonce](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L231), no double deposits will happen as result.
**CD.9** `sendMessage(...)` needs to be called by various `deposit` functions from `L1StandardBridge(...)`, as `L1CrossDomainMessenger` [fills in the `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L148), which is then checked by **CD.2**.
**CD.10** The various [`deposit` functions](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol#L83) fill in `msg.sender` as `_from` when crafting [`finalizeDeposit`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol#L116) message.
Users can also send arbitrary L1 to L2 transactions by enqueueing them into `CanonicalTransactionChain`. However, `enqueue(...)` is not payable and **CD4.1** cannot be fulfilled on L2, so ETH deposits are not feasible through this route. As a matter of fact, this route is seldomly traversed, among the 18500+ enqueue calls since Optimism's first operation, there are only 9 direct user calls. Although these calls are experimental in nature, it is still interesting to see how an user can send arbitrary transactions to L2. As an example, [this L1 transaction](https://etherscan.io/tx/0xda35d8aac29a5fdbe00568540e6389c1d55ed952484f91374fd23b917d944659) triggered a [set(bool) on L2](https://optimistic.etherscan.io/tx/0xe393d5dae42783d12c76cd7fe54d655317eeccc0a8e786ad21454681dc5031ac).
### L2 to L1 transactions
L2 to L1 transactions are the reverse actions of the above, where a user issues a withdrawal through `L2StandardBridge`, which goes through `L1CrossDomainMessenger`, and is passed to `OVM_L2ToL1MessagePasser`. A sequencer then picks it up and adds it to a `CanonicalTransactionChain`. After a pre-determined fraud-proof period and verifying with the `StateCommitmentChain`, anyone will be able to relay the message and finalize the withdrawal on L1.
Similarly, let's see how a [withdraw transaction](https://optimistic.etherscan.io/tx/0x356ea817e4780f0502f9c8677e0c26edee07f5e1c4278093bce640296697746e) started from `L2StandardBridge`
```clike
0x32b7006d // L2StandardBridge: withdraw(address _l2Token, uint256 _amount, uint32 _l1Gas, bytes _data)
0000000000000000000000009bcef72be871e61ed4fbbc7630889bee758eb81d // _l2Token
0000000000000000000000000000000000000000000000125f5ca99045274425 // _amount: 338912946895333049381
0000000000000000000000000000000000000000000000000000000000000000 // _l1Gas
0000000000000000000000000000000000000000000000000000000000000080 // _data: star location
0000000000000000000000000000000000000000000000000000000000000000 // _data: length
```
is transformed, wrapped and added to `OVM_L2ToL1MessagePasser` through `passMessageToL1`.
```clike
0xcbd4ece9 // L1CrossDomainMessager: relayMessage(address,address,bytes,uint256)
00000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1 // _target: Proxy__OVM_L1StandardBridge
0000000000000000000000004200000000000000000000000000000000000010 // _sender: L2_STANDARD_BRIDGE
0000000000000000000000000000000000000000000000000000000000000080 // _message: start location
000000000000000000000000000000000000000000000000000000000001b2dc // _messageNonce: 111324
00000000000000000000000000000000000000000000000000000000000000e4
a9f9e675 // L1StandardBridge: finalizeERC20Withdrawal(address,address,address,address,uint256,bytes)
000000000000000000000000ae78736cd615f374d3085123a210448e74fc6393 // _l1Token
0000000000000000000000009bcef72be871e61ed4fbbc7630889bee758eb81d // _l2Token
0000000000000000000000002c362fd5bd900b73c4bf140b7cd6875a56b0e7b6 // _from
0000000000000000000000002c362fd5bd900b73c4bf140b7cd6875a56b0e7b6 // _to
0000000000000000000000000000000000000000000000125f5ca99045274425 // _amount: 338912946895333049381
00000000000000000000000000000000000000000000000000000000000000c0 // _data: star location
0000000000000000000000000000000000000000000000000000000000000000 // _data: length
00000000000000000000000000000000000000000000000000000000
```
Something interesting actually happens when a relayer wants to initiate the withdrawal finalization on L1, the relayer has to first transform the above message, since `Proxy__OVM_L1CrossDomainMessenger` is expect a different relay signature:
```
relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
)
```
This sounds hacky, but kind of makes sense since in crafting the message on L2 side, the proof at withdrawal time is not known yet.
Now, let's examine the various checks in place in the withdrawal process that mitigate security concerns (**CW** means **C**urrent version **W**ithdrawal).
**CW.1** Both ETH and ERC20 tokens are escrowed in [L1StandardBridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol).
**CW.2** To withdraw, [`finalizeETHWithdrawal(...)`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol#L218) and [`finalizeERC20Withdrawal(...)`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol#L235) need to [be triggered by `L1CrossDomainMessager`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol#L42) and [`xDomainMessageSender` needs to be `L2StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol#L47).
In `L1CrossDomainMessager`, [`relayMessage(
address _target,
address _sender,
bytes memory _message,
uint256 _messageNonce,
L2MessageInclusionProof memory _proof
)`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L165) conducts a series of checks before passing the withdrawal to `L1StandardBridge`:
**CW.3.1** `relayMessage(...)` is callable by anyone, thus retriable.
**CW.3.2** `relayMessage(...)` has [`nonReentrant` guard](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L171), this prevents recursive withdrawals embedded in one withdrawal
**CW.3.3** `relayMessage(...)` is pausable (https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L171), this add extra security measure. However, an `unpause()` function is missing in the contract.
**CW.3.4** `_proof` is [outside of `FRAUD_PROOF_WINDOW`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L304)
**CW.3.5** `_proof.stateRootBatchHeader` [exists in `StateCommitmentChain` storage](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/rollup/StateCommitmentChain.sol#L299), thus is valid
**CW.3.6** [`verifyStateCommitment()`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/rollup/StateCommitmentChain.sol#L138) verifies the consistency between `_proof.stateRoot`, `_proof.stateRootBatchHeader`, `_proof.stateRootProof`
**CW.3.7** Verify the withdrawal message is sent by [`L2CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L328), through [`L2_TO_L1_MESSAGE_PASSER`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L335) and is [consistent with `_proof`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L319).
**CW.3.8** Ensure the same withdrawal has [not been relayed before](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L187).
**CW.3.9** Ensure this withdrawal has [not been blocked by Optimism](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L192).
**CW.3.10** [_target is not `CanonicalTransactionChain`](https://github.com/ethereum-optimism/optimism/blob/cdc1fa9846228121982605132b37f99df035e863/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L197), so that the L2->L1 message cannot be sent back to L2
**CW.4** Although currently no fraud-proof procedure is in operation, Optimism can be upgraded within the 7 day challenge window ("fast upgrade keys").
**CW.5** The `relayMessage(...)` is appended to `SequencerBatch` by Optimism's sequencer by retrieving calldata from `passMessageToL1()` in `OVM_L2ToL1MessagePasser`
**CW.6** Withdrawal messages cannot be created through to`OVM_L2ToL1MessagePasser` from `L2CrossDomainMessenger`, since `passMessageToL1` [fills in `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol#L34), which is checked in **CW.3.7**.
**CW.7** Withdrawal messages cannot be created through `L2CrossDomainMessenger`, since `sendMessage` [fills in `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol#L67), which is checked in **CW.2**.
**CW.8** The various [`withdraw` functions](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L54) fill in `msg.sender` as `_from` when crafting [`finalizeETHWithdrawal`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L107) message and emit [`WithdrawalInitiated`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L131).
**CW.9** Burning token is a [privilege belonging only to `L2StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/standards/L2StandardERC20.sol#L49).
### L2 transactions
With funds deposited, users can start transacting on L2. The sequencer accepts these transactions and builds blocks out of them. For each such block, the sequencer creates a corresponding sequencer batch, submits each batch to `CanonicalTransactionChain` as calldata, which are then executed and recorded in `StateCommitmentChain`.
Since only transactions sent to `passMessageToL1` in `OVM_L2ToL1MessagePasser` are passed to L1, there is no concern of mistreated regular L2 transactions.
## Bedrock
### Transaction flows
As Bedrock is expected to hit mainnet toward the end of 2022, its contracts are under intense updates. While the diagram here is based on recent commit [`1471911`](https://github.com/ethereum-optimism/optimism/tree/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc), it is subject to further changes.
![bedrock](https://raw.githubusercontent.com/0xDatapunk/optimism/develop/packages/figures/OP-bedrock%20%2B%20cannon.drawio.svg)
One immediately notices the similarity of transaction trails between Bedrock and the current version. As a matter of fact, the deposits and withdrawals input data go through pretty much the same transformation. One big difference is now Bedrock natively supports ETH. The same ETH deposit function is transformed to:
```clike
e9e05c42 // L1 OptimismPortal: depositTransaction(address,uint256,uint64,bool,bytes)
0000000000000000000000004200000000000000000000000000000000000007 // _target: Proxy_L2_CROSS_DOMAIN_MESSENGER
0000000000000000000000000000000000000000000000000b1a2bc2ec500000 // _value: 800000000000000000
0000000000000000000000000000000000000000000000000000000000030d40 // _gasLimit
0000000000000000000000000000000000000000000000000000000000000000 // _isCreation
00000000000000000000000000000000000000000000000000000000000000a0 // _data: start location
00000000000000000000000000000000000000000000000000000000000001a4 // _data: length
d764ad0b // L2 CrossDomainMessanger: relayMessage(uint256,address,address,uint256,uint256,bytes)
0000000000000000000000000000000000000000000000000000000000024f73 // _nonce: 151411
00000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be1 // _sender: Proxy__L1StandardBridge
0000000000000000000000004200000000000000000000000000000000000010 // _target: Proxy_L2_STANDARD_BRIDGE
0000000000000000000000000000000000000000000000000b1a2bc2ec500000 // _value: 800000000000000000
0000000000000000000000000000000000000000000000000000000000030d40 // _minGasLimit
00000000000000000000000000000000000000000000000000000000000000c0 // _message: start position
00000000000000000000000000000000000000000000000000000000000000e4 _message: length
1635f5fd // L2 StandardBridge: finalizeBridgeETH(address,address,uint256,bytes)
0000000000000000000000003fea91ee988f1ca7a627ba758af6fa7b78c259e3 // _from
0000000000000000000000003fea91ee988f1ca7a627ba758af6fa7b78c259e3 // _to
0000000000000000000000000000000000000000000000000b1a2bc2ec500000 // _amount: 800000000000000000
0000000000000000000000000000000000000000000000000000000000000080 // _extraData: start location
0000000000000000000000000000000000000000000000000000000000000000 // _extraData: length
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
```
### Noticeable Differences Between Current Contracts and Bedrock's Contracts
The native support of ETH allows more symmetry between L1 and L2, from ETH transfer to gas consumption, thus having a huge impact on how the contracts can be structured and modularized:
**A.** In current version, L1 and L2 have clearly distinct business logic for their own `StandardBridge` and `CrossDomainMessenger` contracts respectively. Bedrock, on the other hand, unifies the logic under common abstract `StandardBridge` and `CrossDomainMessenger` contracts. The specific L1 and L2 `StandardBridge` simply add legacy function interfaces (not shown in the diagram) to remain backward-compatible, while the differences between L1 and L2 `CrossDomainMessenger` are kept minimal.
**B.** The most notable changes are the removals of `CanonicalTransactionChain`, `StateCommitmentChain` and their respective storage contracts. With `CanonicalTransactionChain` replaced by an ordinary L1 account at `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0001`, and `StateCommitmentChain` replaced by a much simpler `L2OutputOracle` contract, Bedrock does very little bookkeeping, thus minimizing L1 gas expense.
**C.** Currently, predeployed accounts on L2 have fixed contract code. Under Bedrock, they are all proxies to upgradable implementations, thus making the upgrade process much more transparent.
**D.** To relay withdrawals on L1 side, Bedrock relayer needs to call `finalizeWithdrawalTransaction` in `OptimismPortal`, which checks inputs against L2 proposal retrieved through `L2_ORACLE.getL2Output(_l2BlockNumber)` for correctness and finalization. This eliminates the need for the hacky, non-transparent transformation a relayer has to apply to the message as mentioned previously.
**E.** Since current version represents ETH as an ERC20 token internally, ETH is escrowed in `L1StandardBridge`, while the token is minted from OVM_ETH `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` on L2 side. Bedrock handles ETH natively. Any ETH passed to Bedrock `StandardBridge` is forwarded to and escrowed in `OptimismPortal` on L1. On L2 side, withdrawn ETH is eventually burned by `L2ToL1MessagePasser`.
**F.** Because ETH is native to Bedrock, user can now deposit or withdraw through either the canonical bridge route or direct calling to `OptimismPortal`'s `depositTransaction(...)`, and `L2ToL1MessagePasser`'s `initiateWithdrawal(...)`. These lower-level calls will not take advantage of `CrossDomainMessenger`'s retry mechanism, but allow for customized app-specific retry schemes.
**G.** In current version, when wrong l2token is passed in for a l1token deposits, an ERC20 withdrawal call is kicked off to refund the deposit to L1. Bedrock removed this functionality due to its rare usage and added complexity.
### L1 to L2 transactions
Now, let's examine the various security measures Bedrock implements for deposit transactions (**BD** here stands for "**B**edrock **D**eposit").
**BD.1** L2 tokens are only [mintable by L2's `StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts/contracts/standards/L2StandardERC20.sol#L42). Users receive ETH or ERC20 deposits with `finalizeBridgeETH(...)` and `finalizeBridgeERC20(...)` calls in L2's `StandardBridge`. These functions need to [be triggered by `CrossDomainMessenger` on the same side, while the `xDomainMessageSender` needs to be the other bridge](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L146).
**BD.2** When l1 token and l2 token mismatch, these calls simply revert, without refunding l1 token, recognized by **G.** This means users need to make sure L1/L2 tokens match, otherwise L1 tokens will be lost.
[`relayMessage(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _minGasLimit,
bytes calldata _message
)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L263) in `CrossDomainMessenger` does checks below before making an external call, strictly following CEI pattern.
**BD.3.1** [Reentrancy not allowed](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L270).
**BD.3.2** [It is pausable](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L270).
**BD.3.3** It is [sent by aliased L1's `CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol#L65), in which case `msg.value` needs to be the same as _value, or if it is a replay, msg.value needs to be 0 and it has been recorded by `receivedMessages[versionedHash]`.
**BD.3.4** `_target` [cannot be `address(this)` nor `address(L2_TO_L1_MESSAGE_PASSER)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol#L72), to prevent circular calls.
**BD.3.5** ensure the same transfer has [not succeeded before](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L312).
**BD.3.6** enough gas provided.
**BD.4** The `relayMessage` is sent by a rollup node, which monitors [`TransactionDeposited(address indexed from, address indexed to, uint256 indexed version, bytes opaqueData)` events emitted in `OptimismPortal`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L269) and sends the transaction as if from the `from` in the log. Rollup nodes are controlled by Optimism until decentralization, where fraud-proof enforces the correctness.
**BD.5** If sent by L1's `CrossDomainMessenger`, `depositTransaction` in `OptimismPortal`[aliases `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L255) to satisfy **BD.3.3**.
**BD.6** The deposit message needs to be passed into L1's `CrossDomainMessenger` from L1's `StandardBridge`, since `sendMessage` [fills in `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L235), which is checked in **BD.1**.
**BD.7** The various [`deposit` functions](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L203) fill in `msg.sender` as `_from` when crafting [`finalizeBridgeERC20`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L408) message.
`depositTransaction` in `OptimismPortal` does not require the `msg.sender` to be L1's `CrossDomainMessenger`. It can be initiated by [sending ETH](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L108) to `OptimismPortal` or [called directly](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L98). Per **BD.4**, this deposit will be picked up by rollup, since `depositTransaction` emits [`TransactionDeposited`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L269). Note the user needs to take care of retries on L2 in this case.
### L2 to L1 transactions
Withdrawals have similar checks in place (**BW** is for "**B**edrock **W**ithdraw").:
**BW.1** On L1, ETH is escrowed in `OptimismPortal`, while ERC20 is escrowed in L1's `StandardBridge`.
Both ETH and ERC20 tokens can be withdrawn through the canonical bridge similar to current version:
**BW.2** On L1, `finalizeBridgeXXX(...)` in `StandardBridge` needs to [be triggered by `CrossDomainMessenger` on the same side, while the `xDomainMessageSender` needs to be the other bridge](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L146).
[`relayMessage(
uint256 _nonce,
address _sender,
address _target,
uint256 _value,
uint256 _minGasLimit,
bytes calldata _message
)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L263) in `CrossDomainMessenger` checks that:
**BW.3.1** [Reentrancy not allowed](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L270).
**BW.3.2** [It is pausable](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L270).
**BW.3.3** It is [sent by `OptimismPortal` and the l2Sender is L2's `CrossDomainMessenger`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol#L57), in which case `msg.value` needs to be the same as `_value`, or if it is a replay, msg.value needs to be 0 and it has been recorded by `receivedMessages[versionedHash]`.
**BW.3.4** `_target` [cannot be `address(this)` nor `address(portal)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/L1CrossDomainMessenger.sol#L64), to prevent circular calls.
**BW.3.5** ensure the same transfer has [not succeeded before](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L312).
**BW.3.6** enough gas provided.
Per **BW.3.3** the first `relayMessage` needs to be triggered by `finalizeWithdrawalTransaction` callable by anyone in `OptimismPortal`, which checks:
**BW.4.1** No reentrancy, as [`require(l2Sender==DEFAULT_L2_SENDER)`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L128) ensures only one withdrawal per transaction.*
**BW.4.2** No embedded withdrawal in current withdrawal with [`require(_tx.target!=address(this))`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L137).
**BW.4.3** Can [retrieve `proposal`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L143) from `L2OutputOracle`, which can only be updated by the sequencer.
**BW.4.4** Proposal has passed [`FINALIZATION_PERIOD_SECONDS`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L149).
**BW.4.5** Establish the [validity of `_outputRootProof` by checking against `proposal`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L152).
**BW.4.6** Verify [withdrawal is included in `_outputRootProof`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L164). Together with **BW.4.5**, this shows the authenticity of the current withdrawal.
**BW.4.7** Check current withdrawal [has not finalized](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L174).
**BW.4.8** Enough gas provided.
**BW.5** Sequencer, a centralized entity by Optimism, monitors [`MessagePassed` events emitted in `L2ToL1MessagePasser`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L116), creates and inserts L2 Blocks in `L2OutputOracle` for **BW.4.3**.
**BW.6** Withdrawn L2 ETH are accumulated in `L2ToL1MessagePasser` and can be [burned](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L85) periodically.
**BW.7** The withdrawal message needs to be passed into `L2ToL1MessagePasser` from L2's `CrossDomainMessenger`, since `initiateWithdrawal` [fills in `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L106), which is checked in **BW.3.3**.
**BW.8** The withdrawal message needs to be passed into L2's `CrossDomainMessenger` from L2's `StandardBridge`, since `sendMessage` [fills in `msg.sender`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol#L235), which is checked in **BW.2**.
**BW.9** The various [`withdraw` functions](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L203) fill in `msg.sender` as `_from` when crafting [`finalizeBridgeERC20`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/StandardBridge.sol#L408) message.
**BW.10** Burning L2 ERC20 tokens is a [privilege belonging to L2's `StandardBridge`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/universal/OptimismMintableERC20.sol#L88).
`finalizeWithdrawalTransaction` in `OptimismPortal` does not require the `_tx.target` to be L1's `CrossDomainMessenger`. Such calls can be initiated by [sending ETH](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L75) to `L2_TO_L1_MESSAGE_PASSER` or [calling `initiateWithdrawal` directly](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L98) in `L2ToL1MessagePasser`. Per **BW.5**, this withdrawal will be picked up by Sequencer, since `L2ToL1MessagePasser` emits [`MessagePassed`](https://github.com/ethereum-optimism/optimism/blob/14719116294e3cbeba8dc8594d3f8c06ff0f3bbc/packages/contracts-bedrock/contracts/L2/L2ToL1MessagePasser.sol#L116). Note the user needs to take care of retries in this case.
``
## How do Optimism's security measures help address common bridge attack vectors
In their [talk](https://www.youtube.com/watch?v=wpei-pVJCCc), Quantstamp classified common bridge attack surfaces into [five common categories](https://drive.google.com/file/d/1N_BWDDm1YELMkD5WZEEFQ0sD2opAkCgn/view) (see below for breakdown). Let's take a look at how these potential issues are addressed in Optimism's contracts through the use of different Solidity language features, such as access control modifier, function visibility/accessibility as well as require statements.
### 1. The Custodian
**a. Incorrect asset amount released with respect to the burnt tokens**
Current: Assets can only be released from `L1StandardBridge`, through the canonical bridge, where correct tokens amounts are released and burnt. See **CW.1**, **CW.2**, **CW.3.2**, **CW.3.4**, **CW.3.5**, **CW.3.6**, **CW.3.7**, **CW.3.8**.
Bedrock: Both ETH and ERC20 can be released through the canonical bridge, whose correctness is ensured by **BW.1**, **BW.2**, **BW.3.1**, **BW.3.3**, **BW.3.4**, **BW.3.5**, **BW.4**, **BW.5**, **BW.6**, **BW.7**. ETH can also be released by direct calls to `OptimismPortal`, with corresponding amount burnt in `L2_TO_L1_MESSAGE_PASSER`. The latter requires users to take care of retries.
**b. Assets released despite the debt token has not been burnt**
Current: same as for **a.**
Bedrock: same as for **a.**
**c. Asset transaction replay for a single burn transaction**
Current: **CW.3.2** avoid reentry, **CW.3.8** prevents replay
Bedrock: **BW.3.1** avoid reentry, **BW.3.5** prevents replay
### 2. The Debt Issuer
**a. Incorrect amount of debt issued with respect to the deposited assets**
Current: Assets can only be issued by `L2StandardBridge`. The correct amount is ensured by the canonical bridge, through **CD**
Bedrock: Both ETH and ERC20 deposits can be finalized through the canonical bridge, whose correctness is ensured by **BD**. ETH can also be released by direct calls to `OptimismPortal`, with corresponding amount issued on L2, users need to take care of retries.
**b. Debt token issued although the actual verification did not take place**
Current: Verification happens in **CD.4**
Bedrock: Verification happens in **BD.3**. An explicit trust on Optimism is needed due to **BD.4**
**c. Anybody can issue debt tokens**
Current: Tokens can only be issued in `L2StandardBridge`, through canonical bridge route. **CD.1**, **CD.2**
Bedrock: **BD.1**. ETH can also be sent to L2 directly through `depositTransaction`. Trusting Optimism rollup to transmit amount correctly.
### 3. The Communicator
**Issues debt tokens although no assets have been deposited**
Current: Minting on L2 can only be initiate by `L1StandardBridge`, through **CD** trail
Bedrock: see **BD**.
**Issues no debt tokens although assets have been deposited**
Current: **CD.5** allows for retries
Bedrock: **BD.4** allows for retries
**Accepts fraudulent messages from a fake custodian or a debt issuer**
Current: see **CD.1**, **CD.2**, **CW.1**, **CW.2**
Bedrock: see **BD.1**, **BW.1**, **BW.2**
**Does not relay messages**
Current: see **CD.5**, **CD.8**, **CW.3.1**
Bedrock: see **BD.3.3**, **BD.4**, **BW.3.3**, **BW.10**
**The source contract does not emit events upon deposit/withdrawal**
Current: see **CD.5**, **CD.6**, **CW.5** in place, events not depended for relay, **CW.8**
Bedrock: see **BD.4**, **BW.5**
### 4. The Interface (could be fixed with "revoke approval")
**Deposit from another account**
Current: see **CD.9**, **CD.10**, **CW.6**, **CW.7**
Bedrock: see **BD.6**, **BD.7**, **BW.7**, **BW.8**
**Execute any calls from any contract**
L2->L1 transactions can be relayed by anyone, but there are enough checks in place.
Current: see **CD.3**, **CW.3**
Bedrock: see **BD.3**, **BW.3**, **BW.4**
### 5. The Network
**51% attack**
At current stage, sequencer is controlled by Optimism and requires explicit trust. Once fraud proof is in effect and decentralization is realized, a single honest fraud-proof verfier is all we need.
## Some other important differences as they relate to transactions
| | Current OP | Bedrock |
| --------------- |:-------------------- |:-------------------- |
| L2 Block Size | One tx per block | Multiple txs per block |
| L2 Block Time | Variable | Fixed 2 sec per block, regardless of whether there are transactions or not |
| Access to L1 Info | L1BlockNumber tracks blocknumber | L2Block, also contains timestamp, basefee, hash, and sequenceNumber |
| Fees | Transactions submitted to CanonicalTransactionChain's queue lack a method for paying gas fees to the Sequencer. Gas is burned on L1 in an ad-hoc way to avoid spamming | A metering mechanism compliant with EIP1559 |
| Deposit speed | 10-20 minutes | 2.5 minutes or less |
| Withdrawal | Recorded in CanonicalTransactionChain and StateCommitmentChain in the same way as other transactions | A separate withdrawal root which allows withdrawal Merkle proofs to be 60% cheaper (in L1 gas) |
| Data structure | In CanonicalTransactionChain, a batch consists of header, batch contexts and transactions, with sequencer submitted ones followed by enqueued txs | Within a block, the first tx is an L1 attributes deposit tx, which sets the parameters of L1Block contract, followed by user deposit transactions in the first block of an epoch (this first L2 block produced after an L1 block), and at last normal L2 transactions |
| Fraud-proof | No fraud proof in operation | [Cannon](https://github.com/ethereum-optimism/cannon) is half Geth and half MIPS, which conducts bisection game to identify single instruction for dispute based on pre-supplied preimage |
## Conclusion
There has been [a lot of excitement](https://dev.optimism.io/introducing-optimism-bedrock/) about Bedrock coming out of the Optimism team, since May 2022. The native support of ETH created a condition for more modularized and common functions across L1 and L2 contracts, thus reducing potential attack surface. With better security and cheaper fees, Optimism users will have a lot to look forward to.
If you’re interested in reading more on Optimism’s optimistic rollup, their documentation/code can be found in:
[Optimism Documentation](https://community.optimism.io/)
[Optimism Bedrock specs](https://github.com/ethereum-optimism/optimism/tree/develop/specs)
[Contracts for Current Optimism](https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts)
[Contracts for Bedrock](https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock)