Skip to content
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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

agusduha
Copy link
Contributor

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 the OptimismPortal. Additionally, the dependency manager role is being added to SuperchainConfig to manage the network’s dependency set.

Additional context

Design doc: ethereum-optimism/design-docs#84

* 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
## 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: across

Deposits and locks ETH into the lockbox's liquidity pool.

- The function MUST accept ETH.
- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact.
Copy link
Contributor

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.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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`.
Copy link
Contributor

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.

Copy link
Contributor

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


Withdraws a specified amount of ETH from the lockbox's liquidity pool.

- Only authorized `OptimismPortal` addresses SHOULD be allowed to interact.
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Contributor

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.

Comment on lines 24 to 34
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
Copy link
Contributor

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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`,
Copy link
Contributor

@smartcontracts smartcontracts Dec 11, 2024

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)

Copy link
Contributor

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
Copy link
Contributor

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 }();
Copy link
Contributor

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

Copy link
Contributor Author

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:

  1. What is the motivation behind this change?
  2. What about gas costs? Wouldn’t deploying a contract solely for making a transfer increase costs?
  3. 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`**
Copy link
Contributor

@tynes tynes Dec 11, 2024

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.
Copy link
Contributor

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

Copy link

@ControlCplusControlV ControlCplusControlV left a 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.

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.

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:

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

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.

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.

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

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");

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

Copy link
Contributor Author

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.

@smartcontracts
Copy link
Contributor

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:

  1. Only authorized Portal contracts should be able to use the lockbox
  2. Portal contracts can only be authorized by
  3. Only safe Portal contracts can be authorized (now we need to define "safe")

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
Copy link
Contributor

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`**
Copy link
Contributor

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants