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

On Solving the Interop Withdrawal Liquidity Problem #362

Open
tynes opened this issue Sep 5, 2024 · 2 comments
Open

On Solving the Interop Withdrawal Liquidity Problem #362

tynes opened this issue Sep 5, 2024 · 2 comments

Comments

@tynes
Copy link
Contributor

tynes commented Sep 5, 2024

Overview

As interop is currently specified, there is a problem with withdrawal liquidity. What does this mean in practice?

Users can deposit ether between an arbitrary number of OptimismPortal contracts into the set of interoperable chains. The current scope of interop does not unify this liquidity on L1. Users are able to send the ether between various L2 chains and must check that there is liquidity in the OptimismPortal that corresponds to their L2 before withdrawing to L1. A race condition exists in which the user initiates their withdrawal and then is frontran by another withdrawal that removes the liquidity from the OptimismPortal. The cannot get their funds back to L2 and must wait for more deposits until their withdrawal can be processed.

If we want to guarantee solvency of withdrawals, there are 2 known approaches:

  • L2 reverts on no liquidity
  • L1 unified liquidity

L2 Reverts

This solution involves tracking the balance of the OptimismPortal in L2 and incrementing and decrementing the value as there are deposits and withdrawals. A one time upgrade transaction is included in a hardfork that uses the balance of the OptimismPortal as an input to generate an upgrade transaction that sets the value in an L2 storage slot. The L1 attributes transaction then includes a sum of the mint fields in all deposits and that increments the balance. When a user calls L2ToL1MessagePasser.initiateWithdrawal, it decrements the balance and reverts on underflow. This prevents withdrawals on L2 by a revert if there is insufficient liquidity.

This balance accumulator on L2 is not an accurate counter for the amount of ether on the L2. It is just meant to observe the liquidity for withdrawals. The invariant address(OptimismPortal).balance >= L2 view of balance must hold.

  • It is possible to transfer ether to the OptimismPortal without minting it on L2 by calling the donateETH function or by using SELFDESTRUCT to move ether without EVM execution.
  • L2 to L2 cross chain messaging of ether will not modify the balance

The semantics here also need to account for custom gas token chains, which could be solved by coupling an eth_call to OptimismPortal.balance rather than reading its balance from the state directly, but that would require L1 EVM execution to be in the fault proof.

L1 Unified Liquidity

The most minimal version of a L1 unified bridge is one where there is a shared lockbox. This involves each OptimismPortal migrating its ether balance to a single contract. Any new deposit would forward the ether here and any withdrawal would remove ether from here. This creates a shared liquidity pool for the set of interoperable chains.

There are open questions around the specific design of the shared lockbox. It cannot be permissionless to join unless the contracts were deployed by OP Stack Manager or older systems have had history integrity checks, then we know for a fact that the L2 system doesn't have a backdoor in its contracts.

Known Issues

These solutions do not solve for ERC20 tokens.

@mds1
Copy link
Contributor

mds1 commented Sep 5, 2024

One more option for the design space is letting portals take ETH from each other on-demand. The way this would work is:

  • A portal must know the address of all other portals that have it in their dependency sets. So if I deposit ETH into chain A via portal A, once on L2 I can move that ETH to chains B or C, since B and C both have A in their dependency set. Chain A's portal needs to know the addresses of chain B and C's portals. (More generally it just needs some way to discover them, e.g. storing system config addresses and looking up the portal's address, or a dependency set registry, etc).
  • Now when a portal processes a withdrawal, if it has insufficient ETH, it loops through addresses of portals B and C to see if they have sufficient ETH, and if so calls an auth'd function that lets it withdraw the required amount of ETH.

Considerations:

  • Maybe naive looping is too insufficient to scale and we need to be smarter about how we find the portal(s) to pull ETH from.
  • The invariant we want to hold here is that all withdrawals can be processed, assuming aggregate solvency across all bridges. But is this guaranteed given that dependency sets are not symmetrical (i.e. B has A in it's dependency set, but A might not have B)? Does it matter how I choose which portal to pull from?
  • How to handle custom gas token withdrawals with this design.

@tynes
Copy link
Contributor Author

tynes commented Sep 6, 2024

The solution that @mds1 proposes is interesting because it optimizes for each chain's OptimismPortal to not need to transfer its ether to another contract. While this makes the upgrade more simple, the implementation is more complicated. I would lean towards a less complex on chain solution and have the one time ether migration during the upgrade

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

No branches or pull requests

2 participants