From f9ef0fc6cd64b12c90b8d154725e92e9d7f14722 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:24:43 -0300 Subject: [PATCH 1/3] feat: add shared lockbox spec (#7) * feat: add shared lockbox spec * feat: update shared lockbox spec * fix: specs * feat: add mermaid diagram * fix: prs comments * fix: nits * fix: remove dependency manager * feat: refactor lockbox specs * fix: nit * fix: misspellings * fix: verb tense * fix: prs comments * fix: small changes --- specs/interop/optimism-portal-interop.md | 49 ++++++++ specs/interop/shared-lockbox-upgrade.md | 128 +++++++++++++++++++++ specs/interop/shared-lockbox.md | 128 +++++++++++++++++++++ specs/protocol/superchain-configuration.md | 58 +++++++++- 4 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 specs/interop/optimism-portal-interop.md create mode 100644 specs/interop/shared-lockbox-upgrade.md create mode 100644 specs/interop/shared-lockbox.md diff --git a/specs/interop/optimism-portal-interop.md b/specs/interop/optimism-portal-interop.md new file mode 100644 index 000000000..2f72cdfe5 --- /dev/null +++ b/specs/interop/optimism-portal-interop.md @@ -0,0 +1,49 @@ +# OptimismPortal + + + +**Table of Contents** + +- [Overview](#overview) + - [Interface and properties](#interface-and-properties) + - [Integrating `SharedLockbox`](#integrating-sharedlockbox) + + + +## Overview + +The `OptimismPortal` contract is upgraded to integrate the `SharedLockbox` and start using the shared ETH liquidity. + +### Interface and properties + +The `OptimismPortal` contract will add the following storage layout and modified functions: + +**`SHARED_LOCKBOX`** + +- An immutable address pointing to the `SharedLockbox` contract. +- This address MUST be immutable because all `OptimismPortals` will point to the same `SharedLockbox`, + and this address SHOULD not change. + +### Integrating `SharedLockbox` + +The integration with the `SharedLockbox` involves adding extra steps when executing deposit transactions +or finalizing withdrawal transactions. +These steps include locking and unlocking ETH without altering other aspects of the current `OptimismPortal` implementation. +To implement this solution, the following changes are needed: + +**`depositTransaction`** + +Calls `lockETH` on the `SharedLockbox` with the `msg.value`. + +- The function MUST call `lockETH` on the `SharedLockbox` if: + - The token is `ETHER`. + - `msg.value` is greater than zero. + +**`finalizeWithdrawalTransactionExternalProof`** + +Calls `unlockETH` on the `SharedLockbox` with the `tx.value`. + +- The function MUST call `unlockETH` on the `SharedLockbox` if: + - The token is `ETHER`. + - `tx.value` is greater than zero. +- The ETH is received by the `OptimismPortal` and then sent with the withdrawal transaction diff --git a/specs/interop/shared-lockbox-upgrade.md b/specs/interop/shared-lockbox-upgrade.md new file mode 100644 index 000000000..168385b09 --- /dev/null +++ b/specs/interop/shared-lockbox-upgrade.md @@ -0,0 +1,128 @@ +# Shared Lockbox - Upgrade and migration process + + + +**Table of Contents** + +- [Overview](#overview) + - [Add the chain to the op-governed dependency set](#add-the-chain-to-the-op-governed-dependency-set) + - [Migrate ETH liquidity from `OptimismPortal` to `SharedLockbox`](#migrate-eth-liquidity-from-optimismportal-to-sharedlockbox) + - [`LiquidityMigrator`](#liquiditymigrator) + - [`OptimismPortal` code upgrade](#optimismportal-code-upgrade) +- [Batch transaction process](#batch-transaction-process) + - [Diagram](#diagram) +- [Future Considerations / Additional Notes](#future-considerations--additional-notes) + + + +## Overview + +Based on the assumption that a chain joining the dependency set is an irreversible process, +the on-chain chains list is simplified by assuming that joining the Shared Lockbox is +equivalent to joining the op-governed dependency set. + +The upgrade process consists of three main points: + +- Add the chain to the op-governed dependency set +- Move ETH liquidity from `OptimismPortal` to `SharedLockbox` +- Upgrade the code of `OptimismPortal` to include the `SharedLockbox` integration + +This process also requires that: + +- `SharedLockbox` is deployed +- `SuperchainConfig` is upgraded to manage the dependency set +- `SystemConfig` is upgraded to the interop contract version + +### Add the chain to the op-governed dependency set + +The `SuperchainConfig` contract will be responsible for storing and managing the dependency set. +Its `addChain` function will be used to add the chain to the dependency set and call the `SystemConfig` of each chain +to keep them in sync. +It will also allowlist the corresponding `OptimismPortal`, enabling it to lock and unlock ETH from the `SharedLockbox`. +Once this process is complete, the system will be ready to process deposits and withdrawals. + +### Migrate ETH liquidity from `OptimismPortal` to `SharedLockbox` + +The ETH will be transferred from the `OptimismPortal` to the `SharedLockbox` using an intermediate contract. +This contract functions similarly to upgrades using the `StorageSetter`, being updated immediately before to the real implementation. +Its sole purpose is to transfer the ETH balance. +This approach eliminates the need for adding code to move the liquidity to the lockbox that won't be used again. + +#### `LiquidityMigrator` + +This contract is meant to be used as an intermediate step for the liquidity migration. +Its unique purpose is to transfer the whole ETH balance from `OptimismPortal` to `SharedLockbox`. +This approach avoids adding extra code to the `initialize` function, which could be prone to errors in future updates. + +**Interface and properties** + +**`migrateETH`** + +Transfers the entire ETH balance from the `OptimismPortal` to the `SharedLockbox`. + +- It MUST transfer the whole ETH balance to the `SharedLockbox` when called. + +```solidity +function migrateETH() external; +``` + +### `OptimismPortal` code upgrade + +The `OptimismPortal` will start locking and unlocking ETH through the `SharedLockbox`. +It will continue to handle deposits and withdrawals but won't directly hold the ETH liquidity. +To set this up, the upgrade function will be called via `ProxyAdmin` to implement the new code, +which includes the necessary `SharedLockbox` integration. +The `SharedLockbox` address will be set during the `initialize` function. After this step, +the `OptimismPortal` will not be able to process deposits and withdrawals until the chain is registered +in `SuperchainConfig`. + +## Batch transaction process + +The approach consists of handling the entire migration process in a single batched transaction. +This transaction will include: + +1. Call `addChain` in the `SuperchainConfig` + - Sending chain ID + system config address +2. Call `upgradeAndCall` in the `ProxyAdmin` for the `OptimismPortal` + - Update provisionally to the `LiquidityMigrator` to transfer the whole ETH balance to the `SharedLockbox` in this call. +3. Call `upgrade` in the `ProxyAdmin` for the `OptimismPortal` + - The `SharedLockbox` address is set as immutable in the new implementation + +The L1 ProxyAdmin owner (L1PAO) will execute this transaction. As the entity responsible for updating contracts, +it has the authority to perform the second and third steps. +For the first step, the L1PAO has to be set as authorized for adding a chain to the op-governed dependency set +on the `SuperchainConfig` when initializing. +This process can be set as a [superchain-ops](https://github.com/ethereum-optimism/superchain-ops) task. + +### Diagram + +```mermaid +sequenceDiagram + participant L1PAO as L1 ProxyAdmin Owner + participant ProxyAdmin as ProxyAdmin + participant SuperchainConfig + participant OptimismPortalProxy as OptimismPortal + participant LiquidityMigrator + participant SharedLockbox + + Note over L1PAO: Start batch + + %% Step 1: Add chain to SuperchainConfig + L1PAO->>SuperchainConfig: addChain(chainId, SystemConfig address) + + %% Step 2: Upgrade OptimismPortal to intermediate implementation that transfers ETH + L1PAO->>ProxyAdmin: upgradeAndCall() + ProxyAdmin->>OptimismPortalProxy: Upgrade to LiquidityMigrator + OptimismPortalProxy->>LiquidityMigrator: Call migrateETH() + OptimismPortalProxy->>SharedLockbox: Transfer entire ETH balance + + %% Step 3: Upgrade OptimismPortal to final implementation + L1PAO->>ProxyAdmin: upgrade() + ProxyAdmin->>OptimismPortalProxy: Upgrade to new OptimismPortal implementation + + Note over L1PAO: End batch +``` + +## Future Considerations / Additional Notes + +- Before calling `addChain`, it MUST be ensured that the `chainId` and `systemConfig` match diff --git a/specs/interop/shared-lockbox.md b/specs/interop/shared-lockbox.md new file mode 100644 index 000000000..f05725c94 --- /dev/null +++ b/specs/interop/shared-lockbox.md @@ -0,0 +1,128 @@ +# Shared Lockbox + + + + +**Table of Contents** + +- [Overview](#overview) +- [Design](#design) + - [Interface and properties](#interface-and-properties) + - [Events](#events) +- [Reference implementation](#reference-implementation) + + + +## 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 accross the different chains and it could happen +that a chain ends up with more liquidity than its `OptimismPortal`. +The `SharedLockbox` 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 (`SharedLockbox`), enabling seamless withdrawals of ETH +from any OP chain in the Superchain, regardless of where the ETH was initially deposited. + +## Design + +The `SharedLockbox` 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. +These `OptimismPortal`s will be allowlisted by the `SuperchanConfig` using the `authorizePortal` function +when a chain is added. + +### Interface and properties + +**`lockETH`** + +Deposits and locks ETH into the lockbox's liquidity pool. + +- The function MUST accept ETH. +- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. +- 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. + +- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. +- The function MUST emit the `ETHUnlocked` event with the `portal` that called it and the `amount`. + +```solidity +function unlockETH(uint256 _value) external; +``` + +**`authorizePortal`** + +Grants authorization to a specific `OptimismPortal` contract. + +- Only `SuperchainConfig` address SHOULD be allowed to interact. +- The function MUST add the specified address to the mapping of authorized portals. +- The function MUST emit the [`PortalAuthorized`](#events) event when a portal is successfully added. + +```solidity +function authorizePortal(address _portal) external; +``` + +### 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); +``` + +## Reference implementation + +An example implementation could look like this: + +```solidity +// OptimismPortals that are part of the dependency cluster. +mapping(address _portal => bool) internal _authorizedPortals; + +function lockETH() external payable { + require(_authorizedPortals[msg.sender], "Unauthorized"); + + emit ETHLocked(msg.sender, msg.value); +} + +function unlockETH(uint256 _value) external { + require(_authorizedPortals[msg.sender], "Unauthorized"); + + // Using `donateETH` to not trigger a deposit + IOptimismPortal(msg.sender).donateETH{ value: _value }(); + + emit ETHUnlocked(msg.sender, _value); +} + +function authorizePortal(address _portal) external { + require(msg.sender == superchainConfig, "Unauthorized"); + + _authorizedPortals[_portal] = true; + + emit PortalAuthorized(_portal); +} +``` diff --git a/specs/protocol/superchain-configuration.md b/specs/protocol/superchain-configuration.md index 000b7cb34..3c9dcf5b3 100644 --- a/specs/protocol/superchain-configuration.md +++ b/specs/protocol/superchain-configuration.md @@ -2,6 +2,7 @@ + **Table of Contents** - [Overview](#overview) @@ -10,13 +11,16 @@ - [Pausability](#pausability) - [Paused identifiers](#paused-identifiers) - [Scope of pausability](#scope-of-pausability) +- [Dependency manager](#dependency-manager) + - [Interface and properties](#interface-and-properties) + - [Events](#events) ## Overview The SuperchainConfig contract is used to manage global configuration values for multiple OP Chains within -a single Superchain network. +a single Superchain network. Also is in charge of managing and keeping track of the network's dependency set. ## Configurable values @@ -64,3 +68,55 @@ When the Pause is activated, the following methods are disabled: 1. `StandardBridge.finalizeBridgeERC20()` 1. `StandardBridge.finalizeBridgeETH()` 1. `L1ERC721Bridge.finalizeBridgeERC721()` + +## Dependency manager + +The `SuperchainConfig` contract will manage and keep track of the dependency graph. +It will be queried as the source of truth to get which chains are part of the Superchain. +It will also allow to add a chain to the op-governed cluster and update each chain’s dependency set. + +### Interface and properties + +The contract will add the following storage layout and function: + +**`SHARED_LOCKBOX`** + +- An immutable address pointing to the `SharedLockbox` contract. +- This address MUST be immutable because there's only one `SharedLockbox` for each cluster. + +**`systemConfigs`** + +- A mapping that associates chain IDs with their respective SystemConfig addresses. +- It will be used when updating dependencies along each chain. + +**`dependencySet`** + +- An `EnumerableSet` that stores the current list of chain IDs in the dependency set. +- It MUST contain all the chain IDs of the chains that integrate the corresponding Superchain network. + +**`addChain`** + +The `addChain` function adds a new chain to the op-governed cluster. + +- It SHOULD only be callable by the authorized `updater` role of the `SuperchainConfig`. +- It MUST NOT add a chain ID to the dependency set if it is already included. +- It MUST check that the new chain dependency set size is zero. +- It MUST update all chain dependencies through deposit txs to form a complete mesh graph. +- It MUST store the provided `SystemConfig` address in the `systemConfigs` mapping. +- It MUST allowlist the new chain's `OptimismPortal` in the `SharedLockbox`. +- It MUST emit the `ChainAdded` event with the `chainId` and + its corresponding `SystemConfig` and `OptimismPortal`. + +```solidity +function addChain(uint256 _chainId, address _systemConfig) external; +``` + +### Events + +**`ChainAdded`** + +MUST be triggered when `addChain` is called + +```solidity +event ChainAdded(uint256 indexed chainId, address indexed systemConfig, address indexed portal); +``` From 46aaed0e84fa14ca91122c282b3815f1c09b5605 Mon Sep 17 00:00:00 2001 From: agusduha Date: Fri, 22 Nov 2024 18:38:50 -0300 Subject: [PATCH 2/3] fix: lint --- specs/interop/shared-lockbox.md | 1 - specs/protocol/superchain-configuration.md | 1 - 2 files changed, 2 deletions(-) diff --git a/specs/interop/shared-lockbox.md b/specs/interop/shared-lockbox.md index f05725c94..9f2ea3a10 100644 --- a/specs/interop/shared-lockbox.md +++ b/specs/interop/shared-lockbox.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Overview](#overview) diff --git a/specs/protocol/superchain-configuration.md b/specs/protocol/superchain-configuration.md index 3c9dcf5b3..99624b4a8 100644 --- a/specs/protocol/superchain-configuration.md +++ b/specs/protocol/superchain-configuration.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Overview](#overview) From b25ed80cdd47ebe226f21b6effcee1bef5664972 Mon Sep 17 00:00:00 2001 From: AgusDuha <81362284+agusduha@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:55:39 -0300 Subject: [PATCH 3/3] feat: add shared lockbox invariants (#15) * feat: add shared lockbox invariants * fix: remove immutable variable * feat: add upgrader role * feat: add authorized invariants to the portal * fix: pr * fix: add upgrader role and remove new portal invariants --- specs/interop/dependency-set.md | 16 +++--- specs/interop/optimism-portal-interop.md | 60 ++++++++++++++------ specs/interop/shared-lockbox-upgrade.md | 27 ++++----- specs/interop/shared-lockbox.md | 52 +++++++++++++++-- specs/protocol/superchain-configuration.md | 66 +++++++++++++++++----- 5 files changed, 166 insertions(+), 55 deletions(-) diff --git a/specs/interop/dependency-set.md b/specs/interop/dependency-set.md index 8c6351237..2f0176035 100644 --- a/specs/interop/dependency-set.md +++ b/specs/interop/dependency-set.md @@ -6,6 +6,7 @@ - [Chain ID](#chain-id) - [Updating the Dependency Set](#updating-the-dependency-set) + - [Invariants](#invariants) - [Security Considerations](#security-considerations) - [Layer 1 as Part of the Dependency Set](#layer-1-as-part-of-the-dependency-set) @@ -47,17 +48,18 @@ It is a known issue that not all software in the Ethereum can handle 32 byte cha ## Updating the Dependency Set -The `SystemConfig` is updated to manage a new role, `dependencyManager`. -It can only updated by the `ProxyAdmin` during an contract upgrade. -The sole holder of this role is the only address -permissioned to update (remove/add to) the dependency set of that chain. +The `SystemConfig` is updated to have the `SuperchainConfig` address and to manage the dependency set. -The `SystemConfig` is also updated to manage the dependency set. -The address with the `dependency manager` role can add or remove -chains from the dependency set through the `SystemConfig`. +The `SuperchainConfig` can add or remove chains from the dependency set through the `SystemConfig`. The `SystemConfig` MUST enforce that the maximum size of the dependency set is `type(uint8).max` or 255. +### Invariants + +- Only the `SuperchainConfig` contract MUST be able to add a new dependency + +- Only the `SuperchainConfig` contract MUST be able to remove a dependency + ## Security Considerations ### Layer 1 as Part of the Dependency Set diff --git a/specs/interop/optimism-portal-interop.md b/specs/interop/optimism-portal-interop.md index 2f72cdfe5..b0a33eb87 100644 --- a/specs/interop/optimism-portal-interop.md +++ b/specs/interop/optimism-portal-interop.md @@ -5,41 +5,49 @@ **Table of Contents** - [Overview](#overview) - - [Interface and properties](#interface-and-properties) - [Integrating `SharedLockbox`](#integrating-sharedlockbox) + - [`depositTransaction`](#deposittransaction) + - [`finalizeWithdrawalTransactionExternalProof`](#finalizewithdrawaltransactionexternalproof) + - [Invariants](#invariants) ## Overview The `OptimismPortal` contract is upgraded to integrate the `SharedLockbox` and start using the shared ETH liquidity. +This liquidity consists of every ETH balance migrated from each `OptimismPortal` +when joining the op-governed dependency set. -### Interface and properties - -The `OptimismPortal` contract will add the following storage layout and modified functions: - -**`SHARED_LOCKBOX`** - -- An immutable address pointing to the `SharedLockbox` contract. -- This address MUST be immutable because all `OptimismPortals` will point to the same `SharedLockbox`, - and this address SHOULD not change. +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 `SharedLockbox` and `SuperchainConfig`. ### Integrating `SharedLockbox` -The integration with the `SharedLockbox` involves adding extra steps when executing deposit transactions -or finalizing withdrawal transactions. -These steps include locking and unlocking ETH without altering other aspects of the current `OptimismPortal` implementation. +The integration with the `SharedLockbox` involves locking ETH when executing deposit transactions and unlocking ETH +when finalizing withdrawal transactions, without altering other aspects of the current `OptimismPortal` implementation. + To implement this solution, the following changes are needed: -**`depositTransaction`** +#### `depositTransaction` Calls `lockETH` on the `SharedLockbox` with the `msg.value`. -- The function MUST call `lockETH` on the `SharedLockbox` if: +- The function MUST call `lockETH` with `msg.value` on the `SharedLockbox` if: - The token is `ETHER`. - `msg.value` is greater than zero. -**`finalizeWithdrawalTransactionExternalProof`** +```mermaid +sequenceDiagram + participant User + participant OptimismPortal + participant SharedLockbox + + User->>OptimismPortal: depositTransaction(...) + OptimismPortal->>SharedLockbox: lockETH() + OptimismPortal->>OptimismPortal: emit TransactionDeposited() +``` + +#### `finalizeWithdrawalTransactionExternalProof` Calls `unlockETH` on the `SharedLockbox` with the `tx.value`. @@ -47,3 +55,23 @@ Calls `unlockETH` on the `SharedLockbox` with the `tx.value`. - The token is `ETHER`. - `tx.value` is greater than zero. - The ETH is received by the `OptimismPortal` and then sent with the withdrawal transaction + +```mermaid +sequenceDiagram + participant User + participant OptimismPortal + participant SharedLockbox + + User->>OptimismPortal: finalizeWithdrawalTransactionExternalProof(...) + OptimismPortal->>SharedLockbox: unlockETH(uint256 value) + SharedLockbox->>OptimismPortal: donateETH() + OptimismPortal->>OptimismPortal: emit WithdrawalFinalized() +``` + +### Invariants + +- It MUST lock the ETH amount on the `SharedLockbox` when on a deposit transaction with value greater than zero + +- It MUST unlock the ETH amount being withdrawn from the `SharedLockbox` if it is greater than zero + +- It MUST NOT hold any ETH balance from any deposit transaction. diff --git a/specs/interop/shared-lockbox-upgrade.md b/specs/interop/shared-lockbox-upgrade.md index 168385b09..1f7700f66 100644 --- a/specs/interop/shared-lockbox-upgrade.md +++ b/specs/interop/shared-lockbox-upgrade.md @@ -5,7 +5,7 @@ **Table of Contents** - [Overview](#overview) - - [Add the chain to the op-governed dependency set](#add-the-chain-to-the-op-governed-dependency-set) + - [Add the chain to the op-governed dependency set in `SuperchainConfig`](#add-the-chain-to-the-op-governed-dependency-set-in-superchainconfig) - [Migrate ETH liquidity from `OptimismPortal` to `SharedLockbox`](#migrate-eth-liquidity-from-optimismportal-to-sharedlockbox) - [`LiquidityMigrator`](#liquiditymigrator) - [`OptimismPortal` code upgrade](#optimismportal-code-upgrade) @@ -17,13 +17,12 @@ ## Overview -Based on the assumption that a chain joining the dependency set is an irreversible process, -the on-chain chains list is simplified by assuming that joining the Shared Lockbox is -equivalent to joining the op-governed dependency set. +Based on the assumption that a chain joining the op-governed dependency set is an irreversible process, +it is assumed that joining the Shared Lockbox is equivalent to it. The upgrade process consists of three main points: -- Add the chain to the op-governed dependency set +- Add the chain to the op-governed dependency set in `SuperchainConfig` - Move ETH liquidity from `OptimismPortal` to `SharedLockbox` - Upgrade the code of `OptimismPortal` to include the `SharedLockbox` integration @@ -33,17 +32,17 @@ This process also requires that: - `SuperchainConfig` is upgraded to manage the dependency set - `SystemConfig` is upgraded to the interop contract version -### Add the chain to the op-governed dependency set +### Add the chain to the op-governed dependency set in `SuperchainConfig` The `SuperchainConfig` contract will be responsible for storing and managing the dependency set. Its `addChain` function will be used to add the chain to the dependency set and call the `SystemConfig` of each chain to keep them in sync. It will also allowlist the corresponding `OptimismPortal`, enabling it to lock and unlock ETH from the `SharedLockbox`. -Once this process is complete, the system will be ready to process deposits and withdrawals. +Once `addChain` is called, the system will be ready to process deposits and withdrawals. ### Migrate ETH liquidity from `OptimismPortal` to `SharedLockbox` -The ETH will be transferred from the `OptimismPortal` to the `SharedLockbox` using an intermediate contract. +The ETH will be transferred from the `OptimismPortal` to the `SharedLockbox` using the `LiquidityMigrator` contract. This contract functions similarly to upgrades using the `StorageSetter`, being updated immediately before to the real implementation. Its sole purpose is to transfer the ETH balance. This approach eliminates the need for adding code to move the liquidity to the lockbox that won't be used again. @@ -60,21 +59,22 @@ This approach avoids adding extra code to the `initialize` function, which could Transfers the entire ETH balance from the `OptimismPortal` to the `SharedLockbox`. -- It MUST transfer the whole ETH balance to the `SharedLockbox` when called. - ```solidity function migrateETH() external; ``` +**Invariants** + +- It MUST migrate the whole `OptimismPortal` ETH balance to the `SharedLockbox` + +- It MUST emit `ETHMigrated` when migrating the balance + ### `OptimismPortal` code upgrade The `OptimismPortal` will start locking and unlocking ETH through the `SharedLockbox`. It will continue to handle deposits and withdrawals but won't directly hold the ETH liquidity. To set this up, the upgrade function will be called via `ProxyAdmin` to implement the new code, which includes the necessary `SharedLockbox` integration. -The `SharedLockbox` address will be set during the `initialize` function. After this step, -the `OptimismPortal` will not be able to process deposits and withdrawals until the chain is registered -in `SuperchainConfig`. ## Batch transaction process @@ -109,6 +109,7 @@ sequenceDiagram %% Step 1: Add chain to SuperchainConfig L1PAO->>SuperchainConfig: addChain(chainId, SystemConfig address) + SuperchainConfig->>SharedLockbox: authorizePortal(OptimismPortal address) %% Step 2: Upgrade OptimismPortal to intermediate implementation that transfers ETH L1PAO->>ProxyAdmin: upgradeAndCall() diff --git a/specs/interop/shared-lockbox.md b/specs/interop/shared-lockbox.md index 9f2ea3a10..054c33ad3 100644 --- a/specs/interop/shared-lockbox.md +++ b/specs/interop/shared-lockbox.md @@ -8,6 +8,9 @@ - [Design](#design) - [Interface and properties](#interface-and-properties) - [Events](#events) +- [Invariants](#invariants) + - [System level invariants](#system-level-invariants) + - [Contract level invariants](#contract-level-invariants) - [Reference implementation](#reference-implementation) @@ -15,7 +18,7 @@ ## 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 accross the different chains and it could happen +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 `SharedLockbox` 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 (`SharedLockbox`), enabling seamless withdrawals of ETH @@ -28,8 +31,9 @@ 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. -These `OptimismPortal`s will be allowlisted by the `SuperchanConfig` using the `authorizePortal` function +These `OptimismPortal`s will be allowlisted by the `SuperchainConfig` using the `authorizePortal` function when a chain is added. +The `SharedLockbox` contract is proxied and managed by the L1 `ProxyAdmin`. ### Interface and properties @@ -38,7 +42,8 @@ when a chain is added. Deposits and locks ETH into the lockbox's liquidity pool. - The function MUST accept ETH. -- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. +- 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 @@ -49,7 +54,8 @@ function lockETH() external payable; Withdraws a specified amount of ETH from the lockbox's liquidity pool. -- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. +- 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 `ETHUnlocked` event with the `portal` that called it and the `amount`. ```solidity @@ -60,7 +66,7 @@ function unlockETH(uint256 _value) external; Grants authorization to a specific `OptimismPortal` contract. -- Only `SuperchainConfig` address SHOULD be allowed to interact. +- Only `SuperchainConfig` address MUST be allowed to interact. - The function MUST add the specified address to the mapping of authorized portals. - The function MUST emit the [`PortalAuthorized`](#events) event when a portal is successfully added. @@ -94,6 +100,42 @@ MUST be triggered when `authorizePortal` is called event PortalAuthorized(address indexed portal); ``` +## Invariants + +### System level invariants + +- The ETH held in the SharedLockbox 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 `SharedLockbox`. + +- The total withdrawable ETH amount present on all the dependency set’s chains MUST NEVER be more than the amount held + by the `SharedLockbox` 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 + +- Only the `SuperchainConfig` contract MUST be able to authorize an `OptimismPortal` + +- It MUST be in paused state if the `SuperchainConfig` is paused + +- No Ether MUST flow out of the contract when in a paused state + +- No `OptimismPortal` can be authorized when in a paused state + +- It MUST NOT trigger a new deposit when ETH amount is being unlocked from the `SharedLockbox` by the `OptimismPortal` + +- It MUST emit: + + - An `ETHLocked` event when locking ETH + + - An `ETHUnlocked` event when unlocking ETH + + - A `PortalAuthorized` event when authorizing a new portal + ## Reference implementation An example implementation could look like this: diff --git a/specs/protocol/superchain-configuration.md b/specs/protocol/superchain-configuration.md index 99624b4a8..8c42ffa31 100644 --- a/specs/protocol/superchain-configuration.md +++ b/specs/protocol/superchain-configuration.md @@ -12,7 +12,14 @@ - [Scope of pausability](#scope-of-pausability) - [Dependency manager](#dependency-manager) - [Interface and properties](#interface-and-properties) + - [`SHARED_LOCKBOX`](#shared_lockbox) + - [`upgrader`](#upgrader) + - [`systemConfigs`](#systemconfigs) + - [`dependencySet`](#dependencyset) + - [`addChain`](#addchain) - [Events](#events) + - [`ChainAdded`](#chainadded) + - [Invariants](#invariants) @@ -29,6 +36,7 @@ The `SuperchainConfig` contract manages the following configuration values: - `PAUSED_SLOT`: A boolean value indicating whether the Superchain is paused. - `GUARDIAN_SLOT`: The address of the guardian, which can pause and unpause the system. +- `UPGRADER_SLOT`: The address of the upgrader, which can add a chain to the depenceny set. ## Configuration data flow @@ -41,6 +49,7 @@ StandardBridge --> SuperchainConfig L1ERC721Bridge --> SuperchainConfig L1CrossDomainMessenger --> SuperchainConfig OptimismPortal --> SuperchainConfig +SharedLockbox --> SuperchainConfig ``` ### Pausability @@ -67,6 +76,8 @@ When the Pause is activated, the following methods are disabled: 1. `StandardBridge.finalizeBridgeERC20()` 1. `StandardBridge.finalizeBridgeETH()` 1. `L1ERC721Bridge.finalizeBridgeERC721()` +1. `SharedLockbox.unlockETH()` +1. `SharedLockbox.authorizePortal()` ## Dependency manager @@ -74,37 +85,46 @@ The `SuperchainConfig` contract will manage and keep track of the dependency gra It will be queried as the source of truth to get which chains are part of the Superchain. It will also allow to add a chain to the op-governed cluster and update each chain’s dependency set. +The `SuperchainConfig` contract is updated with a new `UPGRADER` role that has the ability +to add a chain to the dependency set. + ### Interface and properties The contract will add the following storage layout and function: -**`SHARED_LOCKBOX`** +#### `SHARED_LOCKBOX` - An immutable address pointing to the `SharedLockbox` contract. - This address MUST be immutable because there's only one `SharedLockbox` for each cluster. -**`systemConfigs`** +#### `upgrader` + +- An address with the ability to add a chain to the dependency set. +- The `upgrader` can only be set during initialization. + +#### `systemConfigs` - A mapping that associates chain IDs with their respective SystemConfig addresses. - It will be used when updating dependencies along each chain. -**`dependencySet`** +#### `dependencySet` -- An `EnumerableSet` that stores the current list of chain IDs in the dependency set. +- Stores the current list of chain IDs in the op-governed dependency set. - It MUST contain all the chain IDs of the chains that integrate the corresponding Superchain network. -**`addChain`** +#### `addChain` The `addChain` function adds a new chain to the op-governed cluster. -- It SHOULD only be callable by the authorized `updater` role of the `SuperchainConfig`. -- It MUST NOT add a chain ID to the dependency set if it is already included. -- It MUST check that the new chain dependency set size is zero. -- It MUST update all chain dependencies through deposit txs to form a complete mesh graph. -- It MUST store the provided `SystemConfig` address in the `systemConfigs` mapping. -- It MUST allowlist the new chain's `OptimismPortal` in the `SharedLockbox`. -- It MUST emit the `ChainAdded` event with the `chainId` and - its corresponding `SystemConfig` and `OptimismPortal`. +It can only be called by the `UPGRADER` role in the `SuperchainConfig` and ensures that the chain ID +is not already included in the dependency set. + +Before proceeding, it verifies that the new chain's dependency set size is zero. +The function updates all chain dependencies by executing deposit transactions to form a complete mesh graph, +stores the provided `SystemConfig` address in the `systemConfigs` mapping, and allowlists the +new chain's `OptimismPortal` in the `SharedLockbox`. + +Finally, it emits the `ChainAdded` event with the `chainId`, its corresponding `SystemConfig`, and `OptimismPortal`. ```solidity function addChain(uint256 _chainId, address _systemConfig) external; @@ -112,10 +132,28 @@ function addChain(uint256 _chainId, address _systemConfig) external; ### Events -**`ChainAdded`** +#### `ChainAdded` MUST be triggered when `addChain` is called ```solidity event ChainAdded(uint256 indexed chainId, address indexed systemConfig, address indexed portal); ``` + +### Invariants + +- Only the `UPGRADER` role MUST be able to add a new chain to the dependency set + +- The chain being added MUST NOT have any other dependencies before joining a cluster + +- The same chain MUST NOT be added more than once + +- It MUST add the new chain as a dependency on each chain of the cluster + +- It MUST add the whole cluster as dependency set for the new chain + +- It MUST NOT add the new chain to its own dependency set + +- It MUST authorize the new chain’s `OptimismPortal` to interact with the `SharedLockbox` of the cluster + +- It MUST emit a `ChainAdded` event when the chain is added