ETH Interop
This is a high-level overview of how native ETH is transferred between OP Chains in the Superchain. For exact implementation details, see the SuperchainETHBridge spec.
ETH Interop enables seamless, trust-minimized transfers of native ETH across chains in an interop cluster. The bridging system is implemented using three predeploy contracts:
SuperchainETHBridge
: the entry point for initiating and executing ETH transfersETHLiquidity
: an internal mint/burn pool to maintain supply integrity across chainsL2ToL2CrossDomainMessenger
: handles message passing between chains
There are no wrapped assets. Bridging relies on native ETH balances and enforces strict mint/burn accounting guarantees.
How ETH Interop Works
The flow follows a two-step pattern:
- Initiating Message: A user sends ETH using
SuperchainETHBridge.sendETH(to, chainId)
, which burns the ETH locally by callingETHLiquidity.burn()
. A message is emitted and sent to the destination chain. - Executing Message: A relayer (e.g. autorelayer) calls
SuperchainETHBridge.relayETH()
on the destination chain. This mints ETH usingETHLiquidity.mint()
and delivers it to the recipient via a forced transfer (SafeSend
).
This mechanism avoids liquidity fragmentation while preserving ETH fungibility across all OP Chains.
SuperchainETHBridge
The SuperchainETHBridge
contract is the main interface for cross-chain ETH transfers. It uses the L2ToL2CrossDomainMessenger
to send and verify messages. As an overview:
-
function sendETH(address _to, uint256 _chainId) external payable returns (bytes32 msgHash_)
: Burns themsg.value
of ETH and sends a cross-chain message:- Reverts
if _to == address(0)
- Calls
ETHLiquidity.burn{ value: msg.value }()
- Sends
relayETH
message to the target chain - Emits
SendETH(from, to, amount, destination)
- Reverts
-
function relayETH(address _from, address _to, uint256 _amount) external
: Mints ETH on the destination chain and transfers it:- Can only be called by
L2ToL2CrossDomainMessenger
- Verifies the message sender is the remote
SuperchainETHBridge
- Calls
ETHLiquidity.mint(_amount)
- Uses
SafeSend{value: amount}
to force-send ETH to _to - Emits
RelayETH(from, to, amount, source)
- Can only be called by
We have a few security invariants that are:
- Only authorized messenger can call relayETH
- ETH must be burned on source before being minted on destination
- ETH cannot be sent to the zero address
ETHLiquidity
The ETHLiquidity
contract is a pre-funded mint/burn vault. It starts with a virtual balance of type(uint248).max
, allowing SuperchainETHBridge
to burn and mint without constraints.
Only the SuperchainETHBridge
can call burn()
and mint()
:
function burn() external payable
: Locks ETH into the contract. Emits LiquidityBurned(caller, value).function mint(uint256 _amount) external
: Sends ETH to the caller via SafeSend. EmitsLiquidityMinted(caller, amount)
.
Forced Transfers with SafeSend
ETH is transferred using new SafeSend{ value: amount }(to)
, a contract-based ETH send that disables fallback logic. This prevents reentrancy and unexpected side effects in recipient contracts.
new SafeSend{ value: _amount }(payable(_to))
: Unlike call{value:}
or transfer()
, SafeSend guarantees ETH delivery without executing recipient logic through selfdestruct
.
Native ETH Supply Guarantee
ETH in circulation across the Superchain is always backed by ETH locked in L1. The lockbox contract on L1 enforces mint/burn constraints:
- Minting ETH on L2 requires ETH to be locked on L1
- ETH is burned on L2 before being released from the lockbox
This model avoids inflation and maintains total supply consistency across L1 and L2.
Events
event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination);
event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source);
event LiquidityBurned(address indexed caller, uint256 value);
event LiquidityMinted(address indexed caller, uint256 value);
These events provide a verifiable record of the ETH bridging lifecycle and occur in the following order:
SendETH
— emitted on the source chain when a user initiates the bridge by callingsendETH
. This includes the destination chain and amount to be transferred.LiquidityBurned
— emitted immediately after, when ETH is burned usingETHLiquidity.burn()
on the source chain.RelayETH
— emitted on the destination chain when the relayer successfully submits the executing message andrelayETH
is called.LiquidityMinted
— emitted when ETH is minted and sent to the recipient usingETHLiquidity.mint()
andSafeSend
.
Together, these logs form a complete audit trail across both chains for every ETH bridge transaction.