-
Notifications
You must be signed in to change notification settings - Fork 100
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
feat: add shared lockbox spec #465
base: main
Are you sure you want to change the base?
feat: add shared lockbox spec #465
Conversation
* 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/shared-lockbox.md
Outdated
## 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: across
specs/interop/shared-lockbox.md
Outdated
Deposits and locks ETH into the lockbox's liquidity pool. | ||
|
||
- The function MUST accept ETH. | ||
- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth adding "MUST not revert" when called by an allowed address.
specs/interop/shared-lockbox.md
Outdated
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
|
||
- 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`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a product decision, but it might be helfpul to also emit an l2 chain id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have the L2 chainid legible on L1 right now to be able to do this but I do agree this is useful
specs/interop/shared-lockbox.md
Outdated
|
||
Withdraws a specified amount of ETH from the lockbox's liquidity pool. | ||
|
||
- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: why isn't this a MUST?
## 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I lack some context here, what/where is the on-chain chains list?
It seems like it's becoming equivalent to the dependency set, was it previously different?
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this contract proxied/upgradeable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see in the impl that it is. Should be added to the spec, as otherwise the options for moving assets out of the lockbox again are much trickier.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment: This process should be managed by an OPCM (there is a different OCPM for each l1 contracts release).
Thoughts: Chains in the initial interop set will upgrade together to this shared lockbox. But other chains will upgrade seperately to their own shared lockbox.
Future versions of the OPCM will therefore need to enable some distinct upgrade path which enables moving ETH into the interop set's lockbox.
MUST be triggered when `authorizePortal` is called | ||
|
||
```solidity | ||
event PortalAuthorized(address indexed portal); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be clear, there is no intention to enable deletion from the list right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct, it is a confirmed decision across the organization https://docs.google.com/document/d/1UixbH3G088fD7ecHSOA8ZinzE4UdcvxFl_1GLyM_VcI
**`SHARED_LOCKBOX`** | ||
|
||
- An immutable address pointing to the `SharedLockbox` contract. | ||
- This address MUST be immutable because all `OptimismPortals` will point to the same `SharedLockbox`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Immutable but not an immutable
variable (just a note for implementation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value should never change but if we make it an immutable
in practice, it means that chains that upgrade but don't join the cluster instantly will need to deploy their own portal contract, which just adds cost. Another way to do it would be to be the address of the lockbox on the superchain config and fetch it from there, it adds an extra call which isn't ideal but it does reduce the amount of config footguns
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## Overview |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General feedback, would like to see more of an invariant-driven approach to this specification. We need to understand the top-level properties that the contract must satisfy, then we can define the implementation spec that satisfies these properties.
require(_authorizedPortals[msg.sender], "Unauthorized"); | ||
|
||
// Using `donateETH` to not trigger a deposit | ||
IOptimismPortal(msg.sender).donateETH{ value: _value }(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about using the selfdestruct trick to transfer ether rather than using donateETH
? we have a SafeSend
library that triggers a balance transfer without any execution in the contract
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a few questions about this:
- What is the motivation behind this change?
- What about gas costs? Wouldn’t deploying a contract solely for making a transfer increase costs?
- Is
donateETH
being used anywhere? If not, could we rename it to make its functionality clearer?
|
||
The contract will add the following storage layout and function: | ||
|
||
**`SHARED_LOCKBOX`** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nit: ####
rather than **
so there are hyperliinks directly to the section
|
||
**`dependencySet`** | ||
|
||
- An `EnumerableSet` that stores the current list of chain IDs in the dependency set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There isn't context on what an EnumerableSet
is. Its an implementation detail, we should describe the properties of this data structure that we depend on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor comments, but overall looks great!
|
||
## Overview | ||
|
||
The `OptimismPortal` contract is upgraded to integrate the `SharedLockbox` and start using the shared ETH liquidity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"using the shared ETH liquidity" is a bit ambiguous imo, as each OptimismPortal
is practically storing its Ether in the shared lock box, so we should note that funds are being moved with this change
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also encourage more direct language here instead of "adding extra steps" and "include," are these definitively all the extra steps being added?
|
||
Calls `lockETH` on the `SharedLockbox` with the `msg.value`. | ||
|
||
- The function MUST call `lockETH` on the `SharedLockbox` if: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would specify the value it must call with here as well
|
||
The upgrade process consists of three main points: | ||
|
||
- Add the chain to the op-governed dependency set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How exactly is this defined? Which contract is involved here?
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would replace "this process" for "addChain is called"
|
||
### Migrate ETH liquidity from `OptimismPortal` to `SharedLockbox` | ||
|
||
The ETH will be transferred from the `OptimismPortal` to the `SharedLockbox` using an intermediate contract. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should name this intermediate contract specifically, and just mention defined below
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably specify a downtime consideration here
} | ||
|
||
function authorizePortal(address _portal) external { | ||
require(msg.sender == superchainConfig, "Unauthorized"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fwiw I would like to use custom errors going forward in new code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the new implementation code, we’re using custom errors throughout. However, in the specs, we’ve been using require statements to keep the reading simpler.
Dropping this comment here as per request from @tynes. I think the most valuable think to add to this PR would be to clearly define the invariants that need to be maintained when we introduce this component. Some examples here would be:
I think point (3) is the most important and it would be valuable to explore all of the ways in which this condition could be violated. What is "safe"? We need to be exhaustive. |
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
||
## Overview |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be clear that its possible to upgrade to this hardfork and not be part of a cluster. The requirement would be that a chain has its own lockbox
|
||
The `OptimismPortal` contract will add the following storage layout and modified functions: | ||
|
||
**`SHARED_LOCKBOX`** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer ####
over ** ... **
so that hyperlinks can be generated to specific sections
* 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
Description
Introducing the
SharedLockbox
spec and its corresponding upgrade process. This enhancement improves the Superchain’s interoperable ETH withdrawal user experience and prevents withdrawal failures caused by insufficient ETH in theOptimismPortal
. Additionally, the dependency manager role is being added toSuperchainConfig
to manage the network’s dependency set.Additional context
Design doc: ethereum-optimism/design-docs#84