OP Mainnet
Ethereum <> OP Mainnet

Differences between Ethereum and OP Mainnet

It's important to note that there are various minor discrepancies between the behavior of OP Mainnet and Ethereum. You should be aware of these discrepancies when building apps on top of OP Mainnet.

Opcode Differences

OpcodeSolidity equivalentBehavior
COINBASEblock.coinbaseUndefined
DIFFICULTYblock.difficultyRandom value. As this value is set by the sequencer, it is not as reliably random as the L1 equivalent. You can use an oracle for randomness.
NUMBERblock.numberL2 block number
TIMESTAMPblock.timestampTimestamp of the L2 block
ORIGINtx.originIf the transaction is an L1 ⇒ L2 transaction, then tx.origin is set to the aliased address of the address that triggered the L1 ⇒ L2 transaction. Otherwise, this opcode behaves normally.
CALLERmsg.senderIf the transaction is an L1 ⇒ L2 transaction, and this is the initial call (rather than an internal transaction from one contract to another), the same address aliasing behavior applies.
PUSH0 (opens in a new tab)N/AOpcode not supported yet (will be added in a hardfork)

tx.origin == msg.sender: On L1 Ethereum tx.origin is equal to msg.sender only when the smart contract was called directly from an externally owned account (EOA). However, on OP Mainnet tx.origin is the origin on OP Mainnet. It could be an EOA. However, in the case of messages from L1, it is possible for a message from a smart contract on L1 to appear on L2 with tx.origin == msg.sender. This is unlikely to make a significant difference, because an L1 smart contract cannot directly manipulate the L2 state. However, there could be edge cases we did not think about where this matters.

Accessing L1 information

If you need the equivalent information from the latest L1 block, you can get it from the L1Block contract (opens in a new tab). This contract is a predeploy at address 0x4200000000000000000000000000000000000015 (opens in a new tab). You can use the getter functions (opens in a new tab) to get these parameters:

  • number: The latest L1 block number known to L2
  • timestamp: The timestamp of the latest L1 block
  • basefee: The base fee of the latest L1 block
  • hash: The hash of the latest L1 block
  • sequenceNumber: The number of the L2 block within the epoch (the epoch changes when there is a new L1 block)

Address Aliasing

Because of the behavior of the CREATE opcode, it is possible for a user to create a contract on L1 and on L2 that share the same address but have different bytecode. This can break trust assumptions, because one contract may be trusted and another be untrusted (see below). To prevent this problem the behavior of the ORIGIN and CALLER opcodes (tx.origin and msg.sender) differs slightly between L1 and L2.

The value of tx.origin is determined as follows:

Call sourcetx.origin
L2 user (Externally Owned Account)The user's address (same as in Ethereum)
L1 user (Externally Owned Account)The user's address (same as in Ethereum)
L1 contract (using CanonicalTransactionChain.enqueue)L1_contract_address + 0x1111000000000000000000000000000000001111

The value of msg.sender at the top-level (the very first contract being called) is always equal to tx.origin. Therefore, if the value of tx.origin is affected by the rules defined above, the top-level value of msg.sender will also be impacted.

Note that in general, tx.origin should not be used for authorization (opens in a new tab). However, that is a separate issue from address aliasing because address aliasing also affects msg.sender.

Why is address aliasing an issue?

The problem with two identical source addresses (the L1 contract and the L2 contract) is that we extend trust based on the address. It is possible that we will want to trust one of the contracts, but not the other.

  1. Helena Hacker forks Uniswap (opens in a new tab) to create her own exchange (on L2), called Hackswap.

    Note: There are actually multiple contracts in Uniswap, so this explanation is a bit simplified. See here if you want additional details (opens in a new tab).

  2. Helena Hacker provides Hackswap with liquidity that appears to allow for profitable arbitrage opportunities. For example, she can make it so that you can spend 1 DAI (opens in a new tab) to buy 1.1 USDT (opens in a new tab). Both of those coins are supposed to be worth exactly $1.

  3. Nimrod Naive knows that if something looks too good to be true it probably is. However, he checks the Hackswap contract's bytecode and verifies it is 100% identical to Uniswap. He decides this means the contract can be trusted to behave exactly as Uniswap does.

  4. Nimrod approves an allowance of 1000 DAI for the Hackswap contract. Nimrod expects to call the swap function on Hackswap and receive back nearly 1100 USDT.

  5. Before Nimrod's swap transaction is sent to the blockchain, Helena Hacker sends a transaction from an L1 contract with the same address as Hackswap. This transaction transfers 1000 DAI from Nimrod's address to Helena Hacker's address. If this transaction were to come from the same address as Hackswap on L2, it would be able to transfer the 1000 DAI because of the allowance Nimrod had to give Hackswap in the previous step to swap tokens.

    Nimrod, despite his naivete, is protected because OP Mainnet modified the transaction's tx.origin (which is also the initial msg.sender). That transaction comes from a different address, one that does not have the allowance.

Note: It is simple to create two different contracts on the same address in different chains. But it is nearly impossible to create two that are different by a specified amount, so Helena Hacker can't do that.

Transactions

Transaction costs

Transaction costs on OP Mainnet include an L2 execution fee and an L1 data fee.

EIP-1559

The L2 execution fee is calculated using EIP-1559 (opens in a new tab). The cost of a unit of gas is composed of two components:

  • Base fee: This fee is the same for all transactions in a block. It varies between blocks based on the difference between the actual size of the blocks (which depends on the demand for block space) and the target block size. When the block uses more gas than the target block size the base fee goes up to discourage demand. When the block uses less gas than the target block size the base fee goes down to encourage demand.
  • Priority fee: This fee is specified in the transaction itself and varies between transactions. Block proposers are expected to select the transactions that offer them the highest priority fees first.

The EIP-1559 parameters are different:

ParameterOP Mainnet valueEthereum value (for reference)
Block gas limit30,000,000 gas30,000,000 gas
Block gas target5,000,000 gas15,000,000 gas
EIP-1559 elasticity multiplier62
EIP-1559 denominator508
Maximum base fee increase (per block)10%12.5%
Maximum base fee decrease (per block)2%12.5%
Block time in seconds212

Transaction pool (a.k.a. mempool)

As in L1 Ethereum, transactions are stored in a pool until they can be included in a block. To minimize MEV, Bedrock's mempool is private. To submit transactions, you will need to configure op-geth to forward transactions to the sequencer. This may change in the future.

The sequencer processes transactions in the mempool in order of their base and priority fees.

Blocks

There are several differences in the way blocks are produced between L1 Ethereum and OP Mainnet.

ParameterL1 EthereumOptimism Bedrock
Time between blocks12 seconds12 seconds
Block target size15,000,000 gas5,000,000 gas
Block maximum size30,000,000 gas30,000,000 gas

(1) This is the ideal. If any blocks are missed, it could be an integer multiple such as 24 seconds, 36 seconds, etc.

Note: The L1 Ethereum parameter values are taken from ethereum.org (opens in a new tab).

Network specifications

JSON-RPC differences

OP Mainnet uses the same JSON-RPC API (opens in a new tab) as Ethereum. Some additional OP Mainnet specific methods have been introduced. See the full list of custom JSON-RPC methods for more information.

Contract addresses

The addresses in which various infrastructure contracts are installed are different between L1 Ethereum and OP Mainnet. For example, WETH9 (opens in a new tab) is installed on L1 Ethereum on address 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 (opens in a new tab). On OP Mainnet the same contract is installed on address 0x4200000000000000000000000000000000000006 (opens in a new tab).