Arbitrum/Ethereum Differences
Arbitrum is designed to be as compatible and consistent with Ethereum as possible, from its high-level RPCs to its low-level bytecode and everything in between. Dapp developers with experience building on Ethereum will likely find that little-to-no new L2-specific knowledge is required to build on Arbitrum.
This document presents an overview of some of the minor differences, perks, and gotchas that devs are advised to be aware of.
Block and Transaction Properties
Blocks and Time
Time in L2 is tricky; the timing assumptions one is used to making about L1 blocks don't exactly carry over into the timing of Arbitrum blocks. For details, see Block Numbers and Time.
Block hashes and randomness
Arbitrum's L2 block hashes should not be relied on as a secure source of randomness (see 'blockhash(x);)
L1 Fees
The L2 fees an Arbitrum transaction pays essentially work identically to gas fees on Ethereum. Arbitrum transactions must also, however, pay an L1-fee component to cover the cost of their calldata. (See L1 pricing.)
L1 to L2 Messages
Arbitrum chains support arbitrary L1 to L2 message passing; developers using this functionality should familiarize themselves with how they work (see L1 to L2 Messaging). Of particular note:
- The result of a successful initial/"auto"-execution of an L1 to L2 message will be an unsigned L2 transaction receipt.
- The
msg.sender
of the L2 side of an L1 to L2 message will be not the initiating L1 address, but rather its address alias. - Using the special
ethDeposit
method will not result in an L2 contract's fallback function getting triggered.
Etc.
Precompiles
Arbitrum chains include a number of special precompiles not present on Ethereum; see Common Precompiles / All Precompiles.
Of particular note is the ArbAddressTable, which allows contracts to map addresses to integers, saving calldata / fees for addresses expected to be reused as parameters; see Arb Address Table tutorial for example usage.
Solidity
You can deploy Solidity contracts onto Arbitrum just like you do Ethereum; there are only a few minor differences in behavior. See Solidity Support for details.
JSON-RPC Comparisons
This section covers the differences in response body fields you'll find when using the Arbitrum JSON-RPC vs Ethereum mainnet.
Comprehensive documentation on all generally available JSON-RPC methods for Ethereum can be found at ethereum.org. As Arbitrum has go-ethereum
at its core, most of the documented methods there can be used with no modifications.
eth_syncing
RPC Method
Calling eth_syncing
returns false if not synchronizing (just like on Ethereum). However, if the node is still syncing, eth_syncing
returns an object with data about the synchronization status. The returned object is different from what one would get on Ethereum, see here for more details.
Blocks
When calling eth_getBlockByHash
or eth_getBlockByNumber
, Arbitrum includes a few additional fields and leverages some existing fields in different ways than mainnet Ethereum.
Additional Fields
l1BlockNumber
: An approximate L1 block number that occurred before this L2 block. See Block Numbers and Time for more info.sendCount
: The number of L2-to-L1 messages since Nitro genesissendRoot
: The Merkle root of the Outbox tree state
Existing Fields With Different Behavior
extraData
: This field is equivalent tosendRoot
mixHash
: First 8 bytes is equivalent tosendCount
, second 8 bytes is equivalent tol1BlockNumber
difficulty
: Fixed at0x1
gasLimit
: Value is fixed at0x4000000000000
, but it's important to note that Arbitrum currently has a 32M gas limit per block
See full list of mainnet block fields for additional reference.
Transactions
Transaction Types
In addition to the three transaction types currently supported on mainnet, Arbitrum adds additional types listed below and documented in full detail here.
On RPC calls that return transactions, such as eth_getTransactionByHash
, the type
field will reflect the custom codes where applicable.
100
-ArbitrumDepositTxType
: A deposit of ETH from L1 to L2 via the Arbitrum bridge.101
-ArbitrumUnsignedTxType
: An L1 user can use to call an L2 contract via the bridge.102
-ArbitrumContractTxType
: Allows an L1 contract to call an L2 contract method via the bridge.104
-ArbitrumRetryTxType
: Used to redeem a retryable ticket on L2, which finalizes a retryable that failed to execute automatically (usually due to low gas).105
-ArbitrumSubmitRetryableTxType
: Retryable tickets are submitted via the L1 bridge and allow arbitrary L1 to L2 messages to be created and executed on L2.106
-ArbitrumInternalTxType
: Internal transactions created by the ArbOS itself for certain state updates, like L1 base fee and block number.
Additional Fields
requestId
: Added to L1-to-L2 transactions to indicate position in theInbox
queue.
Existing Fields With Different Behavior
from
: For L1-to-L2 related transactions, will be the aliased version of the L1'smsg.sender
. See L1 to L2 Messaging for more info.
See full list of mainnet transaction fields for additional reference.
Tx Receipts
Additional Fields
l1BlockNumber
: The L1 block number that would be used for block.number calls.gasUsedForL1
: Amount of gas spent on L1 calldata in units of L2 gas.
See full list of mainnet transaction receipt fields for additional reference.