diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index de20a9dfd..183822bee 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -79,6 +79,8 @@ - [SuperchainWETH](./interop/superchain-weth.md) - [Derivation](./interop/derivation.md) - [Transaction Pool](./interop/tx-pool.md) + - [OptimismPortal](./interop/optimism-portal.md) + - [ETH Lockbox](./interop/eth-lockbox.md) - [OP Contracts Manager](./experimental/op-contracts-manager.md) - [Governance Token](./experimental/gov-token.md) - [Multithreaded Cannon FPVM](./experimental/cannon-fault-proof-vm-mt.md) diff --git a/specs/interop/eth-lockbox.md b/specs/interop/eth-lockbox.md new file mode 100644 index 000000000..de92096a8 --- /dev/null +++ b/specs/interop/eth-lockbox.md @@ -0,0 +1,243 @@ +# ETH Lockbox + + + +**Table of Contents** + +- [Overview](#overview) +- [Design](#design) + - [Interface and properties](#interface-and-properties) + - [`lockETH`](#locketh) + - [`unlockETH`](#unlocketh) + - [`authorizePortal`](#authorizeportal) + - [`authorizeLockbox`](#authorizelockbox) + - [`migrateLiquidity`](#migrateliquidity) + - [`receiveLiquidity`](#receiveliquidity) + - [Events](#events) + - [`ETHLocked`](#ethlocked) + - [`ETHUnlocked`](#ethunlocked) + - [`PortalAuthorized`](#portalauthorized) + - [`LockboxAuthorized`](#lockboxauthorized) + - [`LiquidityMigrated`](#liquiditymigrated) + - [`LiquidityReceived`](#liquidityreceived) +- [Invariants](#invariants) + - [System level invariants](#system-level-invariants) + - [Contract level invariants](#contract-level-invariants) +- [Architecture](#architecture) + - [ETH Management](#eth-management) + - [Merge process](#merge-process) + + + +## Overview + +With interoperable ETH, withdrawals will fail if the referenced `OptimismPortal` lacks sufficient ETH. +This is due to having the possibility to move ETH liquidity across the different chains and it could happen +that a chain ends up with more liquidity than its `OptimismPortal`. +The `ETHLockbox` improves the Superchain's interoperable ETH withdrawal user experience and avoids this issue. +To do so, it unifies ETH L1 liquidity in a single contract (`ETHLockbox`), enabling seamless withdrawals of ETH +from any OP chain in the Superchain, regardless of where the ETH was initially deposited. + +## Design + +The `ETHLockbox` contract is designed to manage the unified ETH liquidity for the Superchain. +It implements two main functions: `lockETH` for depositing ETH into the lockbox, +and `unlockETH` for withdrawing ETH from the lockbox. + +These functions are called by the `OptimismPortal` contracts to manage the shared ETH liquidity +when making deposits or finalizing withdrawals. + +Authorization of `OptimismPortal`s is managed by the `ProxyAdmin` owner. +The `ETHLockbox` contract is proxied and managed by the L1 `ProxyAdmin`. + +### Interface and properties + +#### `lockETH` + +Deposits and locks ETH into the lockbox's liquidity pool. + +- The function MUST accept ETH. +- Only authorized `OptimismPortal` addresses MUST be allowed to interact. +- The function MUST NOT revert when called by an authorized `OptimismPortal` +- The function MUST emit the `ETHLocked` event with the `portal` that called it and the `amount`. + +```solidity +function lockETH() external payable; +``` + +#### `unlockETH` + +Withdraws a specified amount of ETH from the lockbox's liquidity pool to the `OptimismPortal` calling it. + +- Only authorized `OptimismPortal` addresses MUST be allowed to interact. +- The function MUST NOT revert when called by an authorized `OptimismPortal` unless paused. +- The function MUST emit the `ETHUnlocked` event with the `portal` that called it and the `amount`. +- The function MUST use `donateETH` when sending ETH to avoid triggering deposits. + +```solidity +function unlockETH(uint256 _value) external; +``` + +#### `authorizePortal` + +Authorizes a `OptimismPortal` to interact with the `ETHLockbox`. + +- Only the `ProxyAdmin` owner can call the function. +- The function MUST emit the `PortalAuthorized` event with the `portal`. + +```solidity +function authorizePortal(address _portal) external; +``` + +#### `authorizeLockbox` + +Authorizes another `ETHLockbox` to migrate its ETH liquidity to the current `ETHLockbox`. + +- Only the `ProxyAdmin` owner can call the function. +- The function MUST emit the `LockboxAuthorized` event with the `lockbox` that is being authorized. + +```solidity +function authorizeLockbox(address _lockbox) external; +``` + +#### `migrateLiquidity` + +Migrates the ETH liquidity from the current `ETHLockbox` to another `ETHLockbox`. + +- Only the `ProxyAdmin` owner can call the function. +- The function MUST emit the `LiquidityMigrated` event with the `lockbox` that is being migrated to. + +```solidity +function migrateLiquidity(address _lockbox) external; +``` + +#### `receiveLiquidity` + +Receives the ETH liquidity from another `ETHLockbox`. + +- Only an authorized `ETHLockbox` can call the function. +- The function MUST emit the `LiquidityReceived` event with the `lockbox` that is being received from. + +```solidity +function receiveLiquidity() external payable; +``` + +### Events + +#### `ETHLocked` + +MUST be triggered when `lockETH` is called + +```solidity +event ETHLocked(address indexed portal, uint256 amount); +``` + +#### `ETHUnlocked` + +MUST be triggered when `unlockETH` is called + +```solidity +event ETHUnlocked(address indexed portal, uint256 amount); +``` + +#### `PortalAuthorized` + +MUST be triggered when `authorizePortal` is called + +```solidity +event PortalAuthorized(address indexed portal); +``` + +#### `LockboxAuthorized` + +MUST be triggered when `authorizeLockbox` is called + +```solidity +event LockboxAuthorized(address indexed lockbox); +``` + +#### `LiquidityMigrated` + +MUST be triggered when `migrateLiquidity` is called + +```solidity +event LiquidityMigrated(address indexed lockbox); +``` + +#### `LiquidityReceived` + +MUST be triggered when `receiveLiquidity` is called + +```solidity +event LiquidityReceived(address indexed lockbox); +``` + +## Invariants + +### System level invariants + +- The ETH held in the `ETHLockbox` MUST never be less than the amount deposited but not yet withdrawn by the `OptimismPortal`s + +- The ETH unlocked by any `OptimismPortal` MUST NOT exceed the available shared liquidity in the `ETHLockbox`. + +- The total withdrawable ETH amount present on all the dependency set's chains MUST NEVER be more than the amount held + by the `ETHLockbox` of the cluster + > With "withdrawable amount", the ETH balance held on `ETHLiquidity` is excluded + +### Contract level invariants + +- It MUST allow only authorized portals to lock ETH + +- It MUST allow only authorized portals to unlock ETH + +- It MUST be in paused state if the `SuperchainConfig` is paused + +- No Ether MUST flow out of the contract when in a paused state + +- It MUST NOT trigger a new deposit when ETH amount is being unlocked from the `ETHLockbox` by the `OptimismPortal` + +- It MUST allow only the `ProxyAdmin` owner to call the `authorizePortal`, `authorizeLockbox` and `migrateLiquidity` functions + +- It MUST allow only authorized lockboxes to call the `receiveLiquidity` function + +- It MUST migrate the whole ETH liquidity from the source `ETHLockbox` to the destination `ETHLockbox` when calling `migrateLiquidity` + +- It MUST emit: + + - An `ETHLocked` event when locking ETH + + - An `ETHUnlocked` event when unlocking ETH + + - A `PortalAuthorized` event when authorizing a portal + + - A `LockboxAuthorized` event when authorizing a lockbox + + - A `LiquidityMigrated` event when migrating liquidity + + - A `LiquidityReceived` event when receiving liquidity + +## Architecture + +### ETH Management + +- ETH is locked in the `ETHLockbox` when: + + - A portal migrates its ETH liquidity when updating + + - A deposit is made with ETH value on an authorized portal + +- ETH is unlocked from the `ETHLockbox` when: + + - An authorized portal finalizes a withdrawal that requires ETH + +### Merge process + +The merge process is the process of migrating the ETH liquidity from one `ETHLockbox` to another. + +- The destination `ETHLockbox` MUST call `authorizeLockbox` with the source `ETHLockbox` as argument. + +- The source `ETHLockbox` MUST call `migrateLiquidity` with the destination `ETHLockbox` as argument. + +- `migrateLiquidity` MUST call `receiveLiquidity` from the destination `ETHLockbox`. + +This process ensures that the ETH liquidity is migrated from the source `ETHLockbox` to the correct destination `ETHLockbox`. diff --git a/specs/interop/optimism-portal.md b/specs/interop/optimism-portal.md new file mode 100644 index 000000000..16b6e770d --- /dev/null +++ b/specs/interop/optimism-portal.md @@ -0,0 +1,101 @@ +# OptimismPortal Interop + + + +**Table of Contents** + +- [Overview](#overview) + - [Integrating `ETHLockbox`](#integrating-ethlockbox) +- [Interface and properties](#interface-and-properties) + - [ETH Management](#eth-management) + - [`migrateLiquidity`](#migrateliquidity) + - [Internal ETH Functions](#internal-eth-functions) + - [`_lockETH`](#_locketh) + - [`_unlockETH`](#_unlocketh) +- [Events](#events) + - [`ETHMigrated`](#ethmigrated) +- [Invariants](#invariants) + + + +## Overview + +The `OptimismPortal` contract is integrated with the `ETHLockbox` for managing unified ETH liquidity. +This liquidity consists of every ETH balance migrated from each `OptimismPortal` when joining +the op-governed dependency set. + +It is possible to upgrade to this version without being part of the op-governed dependency set. In this case, +the corresponding chain would need to deploy and manage its own `ETHLockbox`. + +### Integrating `ETHLockbox` + +The integration with the `ETHLockbox` involves locking ETH when executing deposit transactions and unlocking ETH +when finalizing withdrawal transactions, without altering other aspects of the current `OptimismPortal` implementation. + +## Interface and properties + +### ETH Management + +#### `migrateLiquidity` + +Migrates the ETH liquidity to the `ETHLockbox`. This function will only be called once by the +`ProxyAdmin` owner when updating the `OptimismPortal` contract. + +```solidity +function migrateLiquidity() external; +``` + +- MUST only be callable by the `ProxyAdmin` owner +- MUST transfer all ETH balance to the `ETHLockbox` +- MUST emit an `ETHMigrated` event with the amount transferred + +### Internal ETH Functions + +#### `_lockETH` + +Called during deposit transactions to handle ETH locking. + +```solidity +function _lockETH() internal virtual override; +``` + +- MUST be called during `depositTransaction` when there is ETH value +- MUST lock any ETH value in the `ETHLockbox` +- MUST NOT revert on zero value + +#### `_unlockETH` + +Called during withdrawal finalization to handle ETH unlocking. + +```solidity +function _unlockETH(Types.WithdrawalTransaction memory _tx) internal virtual override; +``` + +- MUST be called during withdrawal finalization when there is ETH value +- MUST unlock the withdrawal value from the `ETHLockbox` +- MUST NOT revert on zero value +- MUST revert if withdrawal target is the `ETHLockbox` + +## Events + +### `ETHMigrated` + +MUST be triggered when the ETH liquidity is migrated to the `ETHLockbox`. + +```solidity +event ETHMigrated(uint256 amount); +``` + +## Invariants + +- Deposits MUST lock the ETH in the `ETHLockbox` + +- Withdrawals MUST unlock the ETH from the `ETHLockbox` and forward it to the withdrawal target + +- The contract MUST NOT hold any ETH balance from deposits or withdrawals + +- The contract MUST be able to handle zero ETH value operations + +- The contract MUST NOT allow withdrawals to target the `ETHLockbox` address + +- The contract MUST only migrate liquidity once diff --git a/specs/interop/predeploys.md b/specs/interop/predeploys.md index 3e3f60a35..817089830 100644 --- a/specs/interop/predeploys.md +++ b/specs/interop/predeploys.md @@ -95,10 +95,10 @@ Emits the `ExecutingMessage` event to signal the transaction has a cross chain m The following fields are required for validating a cross chain message: -| Name | Type | Description | -| -------- | ---------- | -------------------------------------------------------------------------- | -| `_id` | Identifier | A [`Identifier`] pointing to the initiating message. | -| `_msgHash` | `bytes32` | The keccak256 hash of the message payload matching the initiating message. | +| Name | Type | Description | +| ---------- | ---------- | -------------------------------------------------------------------------- | +| `_id` | Identifier | A [`Identifier`] pointing to the initiating message. | +| `_msgHash` | `bytes32` | The keccak256 hash of the message payload matching the initiating message. | ```solidity function validateMessage(Identifier calldata _id, bytes32 _msgHash) @@ -768,9 +768,9 @@ sequenceDiagram L2SBA->>SuperERC20_A: crosschainBurn(from, amount) SuperERC20_A-->SuperERC20_A: emit CrosschainBurn(from, amount) L2SBA->>Messenger_A: sendMessage(chainId, message) - Messenger_A->>L2SBA: return msgHash_ + Messenger_A->>L2SBA: return msgHash_ L2SBA-->L2SBA: emit SentERC20(tokenAddr, from, to, amount, destination) - L2SBA->>from: return msgHash_ + L2SBA->>from: return msgHash_ Inbox->>Messenger_B: relayMessage() Messenger_B->>L2SBB: relayERC20(tokenAddr, from, to, amount) L2SBB->>SuperERC20_B: crosschainMint(to, amount) @@ -801,7 +801,7 @@ The bridging of `SuperchainERC20` using the `SuperchainTokenBridge` will require to the same address on the target chain. Similarly, the `relayERC20()` function should only process messages originating from the same address. - Note: The [`Create2Deployer` preinstall](../protocol/preinstalls.md#create2deployer) - and the custom Factory will ensure same address deployment. + and the custom Factory will ensure same address deployment. - Locally initiated: The bridging action should be initialized from the chain where funds are located only. - This is because the same address might correspond to different users cross-chain. diff --git a/specs/protocol/superchain-config.md b/specs/protocol/superchain-config.md index 000b7cb34..155b052f6 100644 --- a/specs/protocol/superchain-config.md +++ b/specs/protocol/superchain-config.md @@ -20,8 +20,6 @@ a single Superchain network. ## Configurable values -Configurability of the Superchain is currently limited to two values: - The `SuperchainConfig` contract manages the following configuration values: - `PAUSED_SLOT`: A boolean value indicating whether the Superchain is paused. @@ -59,8 +57,8 @@ layer. When the Pause is activated, the following methods are disabled: 1. `OptimismPortal.proveWithdrawalTransaction()` -1. `OptimismPortal.finalizeWithdrawalTransaction()` -1. `L1CrossDomainMessenger.relayMessage()` -1. `StandardBridge.finalizeBridgeERC20()` -1. `StandardBridge.finalizeBridgeETH()` -1. `L1ERC721Bridge.finalizeBridgeERC721()` +2. `OptimismPortal.finalizeWithdrawalTransaction()` +3. `L1CrossDomainMessenger.relayMessage()` +4. `StandardBridge.finalizeBridgeERC20()` +5. `StandardBridge.finalizeBridgeETH()` +6. `L1ERC721Bridge.finalizeBridgeERC721()`