-
Notifications
You must be signed in to change notification settings - Fork 152
feat: add shared lockbox spec #465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
smartcontracts
merged 21 commits into
ethereum-optimism:main
from
defi-wonderland:sc-feat/add-shared-lockbox
Feb 26, 2025
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
f9ef0fc
feat: add shared lockbox spec (#7)
agusduha 46aaed0
fix: lint
agusduha b25ed80
feat: add shared lockbox invariants (#15)
agusduha 1fa11bc
feat: dependency set refactor (#19)
agusduha 7ed9e1c
Merge branch 'main' into sc-feat/add-shared-lockbox
agusduha 688c2ad
Merge branch 'sc-feat/add-shared-lockbox' of github.com:defi-wonderla…
agusduha 10cfcf2
feat: add new shared lockbox specs
agusduha 87fadfa
fix: remove interop naming
agusduha d7c8d15
fix: extension
agusduha 594ff64
fix: summary
agusduha d82bedd
fix: dependency manager descope (#22)
agusduha eb01368
Merge branch 'main' into sc-feat/add-shared-lockbox
agusduha 9775ab2
feat: ETH lockbox redesign
agusduha f759ef1
fix: add proxy admin owner check
agusduha d3c6146
fix: pr fixes
agusduha f8227df
fix: authorize portal fixes
agusduha de3d771
fix: authorize lockbox fix
agusduha 871cfb5
fix: add unlockETH invariant and adminOwner getter
agusduha 0ba8b88
fix: add migrate liquidity invariant
agusduha a63d3fd
fix: pr fixes
agusduha d053265
fix: invariant rewording
agusduha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
# ETH Lockbox | ||
|
||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**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) | ||
- [`adminOwner`](#adminowner) | ||
- [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) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## 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. | ||
- The function MUST NOT allow to be called as part of a withdrawal transaction (`OptimismPortal.l2Sender()` MUST be the `DEFAULT_L2_SENDER`). | ||
|
||
```solidity | ||
function unlockETH(uint256 _value) external; | ||
``` | ||
|
||
#### `authorizePortal` | ||
|
||
Authorizes an `OptimismPortal` to interact with the `ETHLockbox`. | ||
|
||
- Only the `ProxyAdmin` owner can call the function. | ||
- The `ProxyAdmin` owner of the `OptimismPortal` must be the same as the `ProxyAdmin` owner of the `ETHLockbox`. | ||
- The function MUST emit the `PortalAuthorized` event with the `portal`. | ||
- The function MUST NOT allow the same `OptimismPortal` to be authorized more than once. | ||
|
||
```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 `ProxyAdmin` owner of the source lockbox must be the same as the `ProxyAdmin` owner of the destination lockbox. | ||
- The function MUST emit the `LockboxAuthorized` event with the `lockbox` that is being authorized. | ||
- The function MUST NOT allow the same `ETHLockbox` to be authorized more than once. | ||
|
||
```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 `ProxyAdmin` owner of the source lockbox must be the same as the `ProxyAdmin` owner of the destination lockbox. | ||
- The function MUST call `receiveLiquidity` from the destination `ETHLockbox`. | ||
- 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; | ||
``` | ||
smartcontracts marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### `adminOwner` | ||
|
||
Returns the `ProxyAdmin` owner that manages the `ETHLockbox`. | ||
|
||
```solidity | ||
function adminOwner() external view returns (address); | ||
``` | ||
|
||
### 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 | ||
agusduha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- The ETH held in the `ETHLockbox` MUST never be less than the amount deposited but not yet withdrawn by the `OptimismPortal`s | ||
|
||
- All chains joining the same `ETHLockbox` MUST have the same `ProxyAdmin` owner | ||
|
||
- 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 | ||
agusduha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### 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 merging two `ETHLockbox`es into a single one, | ||
transferring the ETH liquidity from the both source `ETHLockbox` to the destination `ETHLockbox`. | ||
|
||
For each source `ETHLockbox`, the following steps MUST be followed: | ||
|
||
- 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`. | ||
|
||
These transactions SHOULD be executed atomically. A possible way is through the `OPCM` contract. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# OptimismPortal Interop | ||
|
||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
**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) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## 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 | ||
agusduha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### Internal ETH Functions | ||
|
||
#### `_lockETH` | ||
|
||
Called during deposit transactions to handle ETH locking. | ||
|
||
```solidity | ||
function _lockETH() internal; | ||
``` | ||
|
||
- MUST be invoked during `depositTransaction` when there is ETH value | ||
- MUST lock any ETH value in the `ETHLockbox` | ||
|
||
#### `_unlockETH` | ||
|
||
Called during withdrawal finalization to handle ETH unlocking. | ||
|
||
```solidity | ||
function _unlockETH(uint256 _amount) internal; | ||
``` | ||
|
||
- MUST be invoked during withdrawal finalization when there is ETH value | ||
- MUST unlock the withdrawal value from the `ETHLockbox` | ||
- 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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.