L2CrossDomainMessenger
Table of Contents
Overview
The L2CrossDomainMessenger provides a high-level interface for sending messages between L2 and L1. It handles message passing in both directions, manages nonces for message ordering, provides replay protection for failed messages, and ensures messages can only be relayed by the paired L1CrossDomainMessenger contract.
Definitions
Cross-Domain Message
A message sent from one domain (L1 or L2) to another, containing a target address, calldata, gas limit, and optional ETH value. Messages are encoded with metadata including sender address and nonce for authentication and replay protection.
Message Replay
The ability to re-execute a failed cross-domain message. Messages that fail during initial relay (due to insufficient gas or target revert) are marked as failed and can be replayed by anyone with sufficient gas.
Assumptions
a01-001: L1CrossDomainMessenger Authentication
The L1CrossDomainMessenger contract is trusted to only relay valid messages that were actually sent from L1. The address aliasing mechanism correctly identifies messages from the L1CrossDomainMessenger.
Mitigations
- Address aliasing is a deterministic protocol-level mechanism
- L1CrossDomainMessenger address is set at initialization and cannot be changed
a01-002: L2ToL1MessagePasser Availability
The L2ToL1MessagePasser predeploy contract at address 0x4200000000000000000000000000000000000016 is available and correctly implements the initiateWithdrawal function for sending messages to L1.
Mitigations
- L2ToL1MessagePasser is a protocol predeploy at a fixed address
- Contract is part of the core L2 system and cannot be modified
Invariants
i01-001: Message Replay Protection
Successfully relayed messages cannot be replayed. Each message hash can only be marked as successful once, and attempting to relay an already-successful message will revert.
Impact
Severity: Critical
Without replay protection, an attacker could replay messages multiple times, potentially draining funds or causing unauthorized state changes on the target contract.
i01-002: Cross-Chain Message Integrity
Messages can only be relayed if they originate from the paired L1CrossDomainMessenger or are being replayed after an initial failure. Unauthorized parties cannot inject or forge Cross-Domain Messages.
Impact
Severity: Critical
If message authentication fails, attackers could execute arbitrary calls on L2 with spoofed sender addresses, completely compromising the security of cross-chain applications.
i01-003: Failed Message Recovery
Messages that fail during relay (due to insufficient gas or target revert) are marked as failed and can be replayed by any party. Failed messages cannot be marked as successful, and successful messages cannot be marked as failed.
Impact
Severity: High
Without proper failed message handling, users could lose funds or have messages permanently stuck if initial relay attempts fail. The replay mechanism ensures messages can eventually be executed with sufficient gas.
i01-004: Relayed Call Fidelity
Any relayed message executes a call whose target, calldata bytes, and ETH value are exactly those committed by the initiating message, and during that call the xDomainMessageSender observed by the target equals the committed sender. This holds for the initial relay and any replay.
Impact
Severity: Critical
Tampering with calldata, value, or sender would enable arbitrary execution or fund loss.
i01-005: L1→L2 Delivery Liveness
All valid sent messages from L1 to L2 are received within a bounded amount of time less than 1 day. This time bound is provided by the derivation pipeline and sequencing window, while the messenger processes messages as they arrive.
Impact
Severity: High
Without timely message delivery, users could experience indefinite delays in cross-chain operations, potentially leading to fund loss, failed time-sensitive transactions, or denial of service for critical bridge functionality.
Function Specification
initialize
Initializes the L2CrossDomainMessenger with the address of the paired L1CrossDomainMessenger contract.
Parameters:
_l1CrossDomainMessenger: Address of the L1CrossDomainMessenger contract on L1
Behavior:
- MUST only be callable once due to initializer modifier
- MUST set the otherMessenger to the provided L1CrossDomainMessenger address
- MUST initialize xDomainMsgSender to the default value if not already set
- MUST disable further initialization attempts
sendMessage
Sends a message from L2 to L1 by encoding it and passing it to the L2ToL1MessagePasser predeploy.
Parameters:
_target: Target contract or wallet address on L1_message: Calldata to send to the target address_minGasLimit: Minimum gas limit for executing the message on L1
Behavior:
- MUST accept ETH value via msg.value to be sent with the message
- MUST compute the full gas limit using baseGas function
- MUST encode the message with relayMessage selector, current nonce, sender, target, value, minGasLimit, and message data
- MUST call L2ToL1MessagePasser.initiateWithdrawal with the encoded message and ETH value
- MUST emit SentMessage event with target, sender, message, nonce, and minGasLimit
- MUST emit SentMessageExtension1 event with sender and value
- MUST increment the message nonce after sending
relayMessage
Relays a message that was sent from L1 to L2. Can be called by the L1CrossDomainMessenger (via deposit transaction) or by anyone replaying a previously failed message.
Parameters:
_nonce: Nonce of the message being relayed_sender: Address of the user who sent the message on L1_target: Target address on L2_value: ETH value to send with the message_minGasLimit: Minimum gas limit for executing the message_message: Calldata to send to the target
Behavior:
- MUST revert if the contract is paused (always returns false on L2)
- MUST revert if message version is greater than 1
- MUST check legacy version 0 message hash is not already relayed for migrated withdrawals
- MUST compute version 1 message hash from all parameters
- MUST verify msg.value equals _value and message is not already failed when called by L1CrossDomainMessenger
- MUST verify msg.value is zero and message is marked as failed when called by anyone else (replay)
- MUST revert if target is an unsafe system address (self or L2ToL1MessagePasser)
- MUST revert if message hash is already marked as successful
- MUST mark message as failed if insufficient gas remains or reentrancy is detected
- MUST set xDomainMsgSender to _sender before external call
- MUST execute external call to target with remaining gas minus reserved gas
- MUST reset xDomainMsgSender to default value after external call
- MUST mark message as successful and emit RelayedMessage event if call succeeds
- MUST mark message as failed and emit FailedRelayedMessage event if call fails
- MUST revert if called from estimation address and message fails
xDomainMessageSender
Returns the address of the sender of the currently executing Cross-Domain Message.
Behavior:
- MUST revert if no message is currently being executed (xDomainMsgSender is default value)
- MUST return the sender address of the currently executing message
messageNonce
Returns the nonce that will be used for the next sent message, with the message version encoded in the upper 2 bytes.
Behavior:
- MUST return the current msgNonce with MESSAGE_VERSION (1) encoded in the upper 2 bytes
- MUST be a view function with no state changes
baseGas
Computes the minimum gas required to guarantee a message will be received on L1 without running out of gas.
Parameters:
_message: Message calldata_minGasLimit: Minimum desired gas limit for the target call
Behavior:
- MUST compute execution gas including relay overhead, call overhead, reserved gas, gas check buffer, and adjusted minGasLimit accounting for EIP-150
- MUST compute total message size including encoding overhead
- MUST return the maximum of execution gas plus calldata gas cost and the EIP-7623 calldata floor cost
- MUST be a pure function with no state access
l1CrossDomainMessenger
Legacy getter for the paired L1CrossDomainMessenger address.
Behavior:
- MUST return the otherMessenger address
- MUST be a view function with no state changes
paused
Returns whether the contract is paused. Always returns false on L2.
Behavior:
- MUST return false
- MUST be a view function with no state changes