Skip to main content

L2 to L1 messaging

Arbitrum's outbox system allows for arbitrary child chain to parent chain contract calls, i.e., messages initiated from the child chain, which eventually resolve in execution on the parent chain. Child-to-parent chain messages (aka "outgoing" messages) bear some things in common with Arbitrum's parent chain to child chain messages, which are "in reverse" though with differences, which we'll explore in this section.

Protocol flow

Messages from child to parent are included in Arbitrum's Rollup state and finalized through the Rollup Protocol. The process follows these steps:

  1. Message creation on child chain:
    • To initiate a child-to-parent chain message, a user or contract calls the sendTxToL1 method on the ArbSys precompile.
  2. Message inclusion in an assertion:
    • The message is batched with other transactions and included in a Rollup assertion.
    • Assertions are submitted to the Rollup contract on the parent chain and enter the dispute window (~one week).
  3. Assertion confirmation:
    • If the assertion remains unchallenged after the dispute window, the Rollup contract finalizes the assertion.
    • The assertion's Merkle root gets posted to the outbox contract on the parent chain.
  4. Execution on the parent chain:
    • Once the assertion is confirmed, anyone can execute the message on the parent chain by proving its inclusion.
    • Execution is possible using Outbox.executeTransaction, which takes a Merkle proof proving the message exists in the finalized assertion.

Client flow (How to send and execute messages)

Sending a message from a child to parent chain

A contract or user sends a message from the child chain by calling:

ArbSys(100).sendTxToL1(destAddress, calldata);

Executing the message on the parent chain

After the seven-day Challenge period, if the assertion is confirmed, anyone can execute the message on the parent chain.

  1. Retrieve proof data:
    • Call the constructOutboxProof method from the NodeInterface contract to get proof data.
note

We refer to NodeInterface as a "virtual" contract; its methods are accessible via calls 0x00000000000000000000000000000000000000C8, but it doesn't live onchain. It isn't a precompile but behaves like a precompile that can't receive calls from other contracts. This approach is a cute trick that lets us provide Arbitrum-specific data without implementing a custom RPC.

  1. Execute on the parent chain;
    • Call Outbox.executeTransaction with the proof data to execute the message on the parent chain.

Protocol design details

Constant overhead for node confirmation

  • Calling confirmNode on the Rollup contract has constant gas overhead, regardless of the number of messages in the assertion.
  • This confirmation ensures malicious actors cannot grief the network by submitting assertions with many outgoing messages.

Why child to parent chain messages require manual execution

Unlike retryable tickets, which can execute automatically with pre-funded gas, child-to-parent chain message must undergo manual execution because Ethereum (parent chain) does not support scheduled execution.

However, applications can implement execution markets that allow third parties to execute messages for a fee.

Persistence and expiry

  • Child-to-parent chain messages: - Persist indefinitely on the parent chain once included in the outbox.

Parent-to-child chain message lifecycle

Each message progresses through three primary states:

StageDescription
Posted on child chainThe message is sent via ArbSys.sendTxToL1.
Waiting for finalizationThe assertion containing the message is in the challenge period (~6.4 days)
Confirmed and executable on the parent chainIf no fraud proof is submitted, the assertion is confirmed, and the message is available for execution in the outbox.

Withdrawing Ether

Withdrawing Ether can be done using the ArbSys precompile's withdrawEth method:

ArbSys(100).withdrawEth{ value: 2300000 }(destAddress)

Upon withdrawing, the Ether balance is burnt on the child chain side and will later be made available on the parent chain side.

ArbSys.withdrawEth is a convenience function equivalent to calling ArbSys.sendTxToL1 with empty calldataForL1. Like any other sendTxToL1 call, it will require an additional call to Outbox.executeTransaction on the parent chain after the dispute period elapses for the user to finalize claiming their funds on the parent chain. Once the withdraw executes (removed from the outbox), the user's Ether balance will receive credit on the parent chain.

The following diagram depicts the process that funds follow during a withdrawal operation.

Process funds follow during withdrawal operation

ERC-20 token withdrawal

Arbitrum has a canonical bridge design and architecture, which we explain in detail in the Token bridging section of Bridging from a parent chain to a child chain article. This section will explain how the Arbitrum canonical bridge works on the child-to-parent chain token bridging.

  1. Initiation of a child-to-parent chain transfer occurs via the L2GatewayRouter contract on the child chain.
  2. The token's gateway contract (L2ArbitrumGateway) on the child chain gets called.
  3. L2ArbitrumGateway finally communicates to its corresponding gateway contract on the parent chain using the L1ArbitrumGateway contract.
Withdrawal process using the gateway

For any given gateway pairing, calls have to be initiated through the corresponding router (L2GatewayRouter), and the gateways must conform to the TokenGateway interfaces; the TokenGateway interfaces should be flexible and extensible enough to support any bridging functionality a particular token may require.

Token withdrawals are the most common usage of child-to-parent chain messaging. The standard withdrawal happens as follows (for standard gateway):

  1. On the child chain, a user calls L2GatewayRouter.outBoundTransfer, which calls outBoundTransfer on arbSomeERC20Token's gateway (i.e., L2ERC20Gateway).
  2. This call burns arbSomeERC20Token tokens and calls ArbSys with an encoded message to L1ERC20Gateway.finalizeInboundTransfer, which will eventually execute on the parent chain.
  3. After the dispute window expires and the assertion with the user's transaction receives confirmation, a user can call Outbox.executeTransaction, which calls the encoded L1ERC20Gateway.finalizeInboundTransfer message, releasing the user's tokens from the L1ERC20Gateway contract's escrow.