The document outlines the high-level design of withdrawals feature for Lido on Ethereum protocol.
The Shanghai/Capella upgrade for Ethereum, scheduled for March-April 2023, will introduce the support of Ether unstaking. The ability to unstake a validator is required for the Lido protocol to build the withdrawals feature. Incorporating this change and making stETH redeemable for Ether is crucial for Lido’s liquid staking protocol. The ability for stETH holders to withdraw their Ether seamlessly and fairly is essential for the smooth operation, sustainability, and long-term success of Lido on Ethereum.
Designing the withdrawal feature for the Lido liquid staking pool is a complex task due to the difficulty of synchronizing access to data between the Consensus and Execution Layers, as well as the async nature of the Consensus Layer validator exit and slashing mechanics.
The design proposed by Lido on the Ethereum Protocol Engineering team addresses these challenges with the in-protocol withdrawal requests queue. The process has to be async, due to the async nature of Ethereum withdrawals.
An Ethereum validator breaking the consensus rules can get slashed. Penalties caused by slashing can't be predicted, as they depend on the network state during the most recent 36-day period. The proposed design for Lido on Ethereum withdrawals has two operating modes — "turbo" and "bunker" — to address this issue. Turbo mode processes stETH withdrawal requests as fast as possible and is expected to be working most of the time. The bunker mode activates under mass slashing conditions. It introduces extra delay for the slashing penalties to be realized and socialized in full before the withdrawal request can be fulfilled.
Under current conditions and the proposed design, in order to switch to the bunker mode Lido should experience simultaneous slashing of 600+ Lido validators. That's 6 times more simultaneous slashings than Ethereum has experienced, and no Lido validator has been slashed ever.
The bunker mode is proposed as the Lido protocol emergency plan. The goal is to specify what is considered a protocol-wide emergency and specify the protocol behavior under extreme conditions ahead of time. The design is aimed to prevent sophisticated actors from making a profit at the cost of everyone else.
The design should consider tail risks like client bugs. To address those the Gatekeeper committee is introduced. The committee multisig would have the right to one-time pause the Lido protocol for a sufficient timeframe for the DAO to react.
Once withdrawals are enabled, there could be a quite wide range of possible demand. Long-time stakers may wish to withdraw their positions not touching the secondary market. To fulfill these requests, Lido on Ethereum could utilize the approximately 200,000 Ether (150,000 from 0x01 withdrawal credentials and 50,000 from 0x00 after 0x00->0x01 WC rotation) obtained through rewards skimming in the week following the hardfork. For a detailed breakdown, please refer to the appendix section.
We are seeking the community’s feedback to make sure that our proposal takes all important considerations into account and to identify any potential improvements. We would greatly appreciate your input on the following three questions:
Your feedback is invaluable to create a proposal that is effective, efficient, and fair for all stakeholders.
The withdrawals feature is a must-have for Lido liquid staking protocol. Ethereum withdrawals present a specific set of challenges the protocol design must overcome to provide the best possible service to Lido's stakers.
Lido is a liquid staking protocol built on top of the Ethereum network, its staking and validator lifecycle mechanics, including rewards, penalties, and slashings.
To become a validator on Ethereum, one needs to generate a signing key, which is a BLS keypair used to sign messages on the Beacon chain, and withdrawal credentials. Those prove ownership of staked Ether and allow to withdraw Ether in the future. Then, a transaction should be sent to the official Ethereum deposit contract with the validator's public key, and withdrawal credentials. After 32 Ether in total is sent to the validator, it becomes eligible to enter the activation queue.
A new validator doesn't get rewards while it's in the activation queue. Once it has gone through the queue, it begins performing duties earning rewards or potentially facing penalties for inactivity or misbehavior.
Ethereum validators earn rewards for securing the network through several activities like block proposing, block attestations, and sync committee participation. These rewards are credited to the validator's balance on the Consensus Layer.
Starting from the Merge, validators also earn Execution Layer (EL) rewards when proposing a block, such as priority fees and MEV payouts. These rewards are credited to validators' feeRecipient
address on the Execution Layer.
It's worth noting that the validator's maximum effective balance is 32 Ether (i.e., if the validator's balance is 33 Ether, then its effective balance is still 32 Ether, and 1 Ether is not accounted for in the Consensus Layer rewards calculation). This is the basis for partial withdrawals (also known as “rewards skimming”).
Both partial and full withdrawals are implemented in a unified way, being "pushed out" from the Consensus Layer side without explicit withdrawal requests from the Execution Layer and injected in blocks as an execution payload “operations”.
To fully withdraw a validator, it’s required to send a voluntary exit message, still participating in the network consensus until the exit epoch assigned by the Consensus Layer comes. The validator would be withdrawn once peaked up with a withdrawals sweep round.
There’s an ongoing discussion around smart contract triggerable exits. This functionality wouldn't be available on Ethereum after Shanghai/Capella hardfork. The implementation of this functionality at Ethereum base layer would allow Lido and other liquid staking protocols to initiate a validator exit without NO involvement.
There are delays and a churn limit in place to prevent abrupt changes to network consensus conditions when activating a new validator or exiting an existing one. The minimum delay is 27 hours, with a possible extension if the corresponding activation/exit queue is congested (currently, the throughput is approximately one validator per minute). An additional delay up to ~4.34 days in the worst case is imposed depending on the validator index and the current withdrawals sweep state (the validator undergoes transition from withdrawable
to withdrawn
at this stage). To sum up briefly, the expected time to withdraw the funds from a validator is about 3-4 days even if the exit queue isn't congested.
Lido’s stETH is designed to provide the best user experience:
From the first principle follows the rebasing mechanics (aToken-like design), from the second — socialization of all rewards and penalties, no matter how they’ve been accrued and what validator operates the specific stake.
stETH represents a share in the staking pool, every single address' balance representing the current Ether value of that share. Ether is submitted to the Lido contract, gets into the Lido protocol buffer and eventually is "delegated" to the Lido validator.
The total Ether value of the pool consists of buffered Ether from user deposits, staked Ether and various kinds of staking rewards. Note that 32 Ether in the buffer and 32 Ether staked have very different qualities regarding potential rewards, associated risks and liquidity. Given that, a share of the total pool should be viewed as a sum of shares in those pool parts.
The rewards and penalties are socialized between all Lido stakers. In particular, stakers start receiving the rewards straight after staking through the Lido smart contracts, even though their Ether doesn't contribute towards increasing stETH rewards until they have gone through the Ethereum activation queue.
The withdrawals part of such a "socializing" design meets two complexities. First is that one can't predict how much Ether would be withdrawn from a particular validator. Exiting validators still have to do validation and can get penalized or slashed in the process. Second is that the Lido protocol learns of the Consensus Layer state only on Oracle reports.
As withdrawals on Ethereum are processed asynchronously, Lido has to have a request-claim process for stETH holders. To ensure the requests are processed in the order they are received, the queue is introduced. Here is an overview of the withdrawals handling process that the development team is proposing:
WithdrawalQueue
contract, locking the stETH amount to be withdrawn.It's important to note that the redemption rate at the finalization step may be lower than the rate at the time of the withdrawal request due to slashing or penalties that have been incurred by the protocol. This means that a user may receive less Ether for their stETH than they expected when they originally submitted the request.
A user can put any number of withdrawal requests in the queue. While there is an upper limit on the size of a particular request, there is no effective limit as a user can submit multiple withdrawal requests. There's a minimal request size threshold of 100 wei required as well due to rounding error issues.
A withdrawal request could be finalized only when the protocol has enough Ether to fulfill it completely. Partial fulfillments are not possible, however, a user can accomplish similar behavior by splitting a bigger request into a few smaller ones.
For UX reasons, the withdrawal request should be transferrable. We are intending to keep the basic implementation as simple as possible. That also would allow for the secondary market for "Lido withdrawal queue positions" to form.
It is important to note two additional restrictions related to withdrawal requests. Both restrictions serve to mitigate possible attack vectors allowing would-be attackers to effectively lower the protocol's APR and carry fewer penalties/slashing risk than stETH holders staying in the protocol.
This section defined the rules and principles of the withdrawal request lifecycle. The presented rules ensure that withdrawal requests are handled efficiently and fairly, avoiding the situation where sophisticated actors can gain an advantage over ordinary stakers.
However, this section didn't consider several important questions which will be discussed further in separate sections:
max(36 days, exit queue duration)
).To properly discuss how the Lido protocol should work with slashings, let's dive into Ethereum slashing mechanics first.
Validators can be penalized for missing block attestations. If their balance falls below 16 Ether, they get ejected from the network. While these penalties are usually minor when the network is functioning normally, they can become severe if the network enters “inactivity leak” mode due to the lack of finality for more than four epochs in a row.
Breaking consensus rules, such as double signing block proposals or attestations, is a serious offense in the Ethereum network. This could be done intentionally to destabilize the network, or it could occur due to a bug in the client or misconfiguration. When this happens, the network slashes the validator and imposes three types of penalties for 36 days at a minimum. The period is longer than 36 days when the Ethereum validators exit queue is clogged which itself is a subject of churn limit. Only then the validator is permanently removed from the network.
Slashing penalties consist of three types. The minimum penalty is issued once slashing is detected and has a fixed size. The midterm attack multiplier penalty is applied on the 18th day and is proportional to the number of other slashings in the past ~36 days (counting backward from the midterm point). This penalty can be as high as 32 Ether. In addition, the validator is penalized for inactivity during all days before being finally ejected (~36 days at minimum or more if too many exits are registered in the Ethereum queue).
gantt
title Ethereum slashing penalties timeline
axisFormat %d
section begin
Slashing started (epoch0) :done, :m3, 2022-10-01, 6h
section slashing
Slashing duration :crit, :a1, 2022-10-01, 36d
Minimum penalty (epoch0) : milestone, m1, 2022-10-01,
Midterm Attack Multiplier Penalty (epoch0 + 2^12): milestone, m2, 2022-10-19,
Missed attestation penalties (epoch0, ..., epoch0 + 2^13) :a2, 2022-10-01, 36d
section end
Slashing completed (epoch0 + 2^13) :done, m3, 2022-11-06, 6h
Accounting for the mechanics of slashing is the most difficult part of designing Lido’s Ethereum liquid staking protocol, as Vasilly’s post on Ethereum Research explains.
In the event of mass slashing, it is necessary to either delay withdrawal requests until penalties can be socialized among holders or to fulfill the requests with a discount. Calculating discount at the moment of slashing start arguably would have been the best way forward. Unfortunately, one can't predict either exit time or total penalties caused by slashing, so there's no way to calculate the discount properly before the slashing has ended.
The main idea here is to guarantee that the Lido protocol should operate in a fair way regarding unsophisticated stakers. For example, a sophisticated actor can, by observing the current state of the blockchain, predict, that due to a mass slashing today, the protocol will experience a negative rebase in 18 days. Then, in the absence of any delay or discount mechanism, they will be able to avoid losses by withdrawing their funds and re-staking them back after the penalties are socialized between other protocol users. This would lead to an arbitrage resulting in the burden of losses being handled by those who didn't have the time or experience to withdraw first.
The two options of mechanism that would protect against that in the event of mass slashing are delaying the withdrawal requests, or fulfilling them without additional delay but with a discount.
There's no way to predict the overall penalty amount associated with slashing. So, the only penalty estimate which won't be giving the withdrawing stakers a better rate than the regular ones is the full balance of the validator under slashing — one just can't lose more. In most cases the penalty amount would be significantly lower, so fulfilling requests with a discount is not optimal from the UX and fairness perspective. Moreover, any user already can sell their stETH (or even withdrawal request position) on a secondary market with a discount. Therefore, the “delay” option should be preferred as the way to socialize losses from mass slashing events.
Single and multiple-validator non-mass slashing, on the other hand, should not greatly affect the protocol, as the daily protocol rewards compensate fully for these types of slashings. Therefore, delaying all withdrawal requests due to minor slashing instances, while protocol rewards are still higher than the losses, is impractical and unnecessary.
To address this, the proposed protocol design consists of two modes: “turbo mode” and “bunker mode”. In the "turbo mode", the protocol fulfills withdrawal requests once it has enough Ether to do so. In the "bunker mode", a user must wait until all losses that might've been predicted based on the state of the blockchain observable at the moment of withdrawal request creation are socialized between all users.
Oracles could trigger a "bunker mode" based on the information about the number of slashings that happened within the last 36 days for Lido and non-Lido validators, as well as extrapolated daily Lido on Ethereum protocol rewards. Here is a rough idea of how it might be evaluated:
def is_bunker_mode(
lido_slashings_last_36d,
non_lido_slashings_last_36d,
projected_rewards_per_day
):
# calculate slashing losses by day based on current data
losses_for_next_days = get_penalties_by_day(
lido_slashings_last_36d,
non_lido_slashings_last_36d
)
# the minimum slashing duration is 36 days
assert len(losses_for_next_days) >= 36
# a negative rebase is anticipated
for loss_per_day in losses_for_next_days:
if loss_per_day > projected_rewards_per_day:
return True
return False
Note that it's a naïve definition. Most likely other pieces of context (number of offline validators and overall network's state, for instance), need to be taken into account.
In the "bunker mode", a withdrawal request's fulfillment is delayed until the Lido protocol gets back to the "turbo mode" or all slashings associated with the request have passed.
Associated slashings are all ongoing slashings that occurred before the withdrawal request or in the same oracle report period (or the next oracle report period in some cases, see the next section for the details).
The definition of the request’s associated slashings ensures that the consequences of any slashing that is observable at the moment of the withdrawal request creation are experienced in full. At the same time, users won’t be affected by slashings happening way after the withdrawal request is made (this issue is discussed in more detail in the overlapping slashings section).
Finally, if it’s observed that Ethereum has been in the leak mode after the previous oracle report, no withdrawal request should be finalized on the next oracle report to allow time for the blockchain to fully apply all the consequences of the leak mode and allow additional time to respond to the incident.
To fulfill a withdrawal request, the protocol should do several things:
To make these decisions, the protocol needs information from the Consensus Layer, which is not directly accessible from the Execution Layer. The current version of the protocol relies on a committee of oracles to provide this missing information for protocol operation on the Execution Layer.
There are two approaches to using oracles: data can be brought on-chain and calculations can be performed there, or calculations can be performed by the oracles and the outcome can be pushed to the contract.
In general, these two approaches are equivalent in terms of the trust of the oracles committee, but the first approach would require performing the onchain calculations over unbounded data which is not practical due to limited block size and high gas costs.
For example, one of the proposed algorithms for determining the next validator to eject would require a loop over all active validators to find the next candidate. Unbounded loops will also occur when processing withdrawal requests.
Thus, a more practical approach here would be to perform calculations and data aggregation off-chain & make Oracles report the outcomes to smart contracts.
N
epochs, oracle members analyze newly appeared withdrawals requests in the WithdrawalQueue
contract, calculate the list of the next validators to eject using the algorithm approved by the Lido DAO and implemented in the oracle code, and publish the list to oracle smart contracts. The algorithm must handle failures and delays in validators' ejection.M
blocks (~1 hour, for example) away from the reportable epoch could not be finalized in this report. Such a restriction is necessary so that any slashing that occurs can be recorded in the Consensus Layer chain. In the case of a bunker mode, a withdrawal request shall not be finalized until all associated slashings are completed.This section provides only a schematic description of the proposed process. The practical user flow examples are given in the appendix.
Although practical, this approach additionally puts the trust assumptions on the oracle committee. The development team considers this approach to be temporary and will work on a ZK-oracle that can perform the same tasks but in a trustless way.
The use of buffered Ether and accrued rewards for fulfilling withdrawal requests are grounded in the principle of APR maximization strategy which aims to ensure that Ether spends as little time as possible in the buffer or the activation queue.
To provide the best UX possible, withdrawal requests should be fulfilled as fast as possible by utilizing Ether from the deposit buffer and rewards vaults to fulfill withdrawal requests. This way the protocol can balance capital efficiency and UX considerations.
As outlined previously, withdrawal request fulfillment can only happen at the time of an oracle report due to the lack of up-to-date information on the state of the Consensus Layer at any other time. This holds true even when using solely the Ether held on the Execution Layer (i.e. deposit buffer and rewards) for request fulfillment in both turbo and bunker mode. Satisfying withdrawal requests immediately would give sophisticated actors a way to avoid losses from slashings.
Oracle report uses a combined Execution Layer buffer of deposited Ether, rewards accrued & withdrawals vault to fulfill the withdrawal requests, restake the amount which could be restaked & leaves the rest in the buffer.
In a scenario where one slashing happens before the previous one has fully finished, it is essential to consider how this can affect the finalization of withdrawal requests.
Consider the following situation:
When should the withdrawal request be finalized?
The withdrawal delay is introduced to prevent sophisticated actors from avoiding penalties which can be predicted by observing Consensus Layer. The withdrawal request is delayed until all validators under associated slashings have exited, but it shouldn't be delayed for longer than necessary.
Delaying until Option 2 opens up a potential griefing vector. A malicious node operator could be slashing extra validators before all validators under slashing 1 have exited, blocking all withdrawals in the Lido protocol. Note that the cost of such an attack gets significantly lower under extreme network conditions.
Thus it's practical to choose Option 1.
Under Option 1 the withdrawal request can be finalized once either all validators under "associated slashings" have exited or Lido protocol has gotten back to the turbo mode, lifting slashing-induced delays altogether.
Note also that under Option 1 withdrawal requests made way before Lido protocol has entered bunker mode could get unaffected by bunker mode delays.
It's impossible to predict every scenario, and it's important to get things right and prevent the catastrophic scenario from breaking the protocol altogether. To allow the protocol to react to unforeseen events, the Gatekeeper feature is proposed.
Gatekeeper is two-piece:
Once the stop is triggered, Lido DAO should vote on whether the stop is valid & should carry on, or should be lifted as already resolved or a false alarm. The gate seal can be broken only once, so the DAO would have to switch the new multisig explicitly.
Lido protocol has a buffer that accumulates Ether from deposits, rewards and withdrawals.
Some amount is reserved for withdrawals fulfillment. Rewards consist of partial withdrawals of excess validator balances and block proposer rewards (Execution Layer rewards).
For all cases below:
200 ETH
(validators balances, various EL buffers)Case precondition: there is 1 ETH
in the buffer.
1 stETH
. Maximum amount of ETH to receive is fixed to 1 ETH
. Alice's 1 stETH
is locked on WithdrawalQueue
.1 ETH
is moved from buffer to WithdrawalQueue
to finalize Alice's request.1 ETH
is moved to the Alice's recipient address, the request becomes claimed.Case postcondition: 0 ETH
left in the buffer.
The min time before Ether is claimable after it was requested varies from 1 hour (requests per-day batching margin) to approximately 24 hours (time between Oracle reports).
Case precondition: there is 60 ETH
in the buffer.
100 stETH
.60 ETH
isn't enough to satisfy the request. The status of the request is unchanged.60+64 ETH
buffer)100 ETH
is moved from buffer to WithdrawalQueue
. The request is finalized with the amount `100 ETH`` and is claimable.Case postcondition: 24 ETH
left in the buffer.
The expected time before Ether is claimable after it was requested is ~3-4 days
Case preconditions:
1 ETH
in the bufferThe following scenario would happen:
1 stETH
.200 ETH
198 ETH
pooled Ether is reported.0.99 ETH
(calculated as 1 * (198/200)
) and is claimable.Case postcondition: 0,01 ETH
left in the buffer.
The min time before Ether is claimable after it was requested varies from 1 hour (requests per-day batching margin) to approximately 24 hours (time between Oracle reports) which is the same as the case 1.
Case preconditions:
1 ETH
in the buffer.Then the following scenario would happened:
1 stETH
.WithdrawalQueue
.200 ETH
.202 ETH
pooled Ether is reported.1 ETH
and is claimable.The min time before Ether is claimable after it was requested is approximately 18 days.
Lido has 13% of validators using BLS type-0x00 withdrawal credentials. These credentials are controlled by the distributed 6/11 threshold signature. A validator that has type-0x00 withdrawal credentials can't be withdrawn.
Starting from the Shanghai/Capella hardfork, these credentials can be rotated pointing to the Execution Layer address (type-0x01) via one-shot operation. For Lido it requires signing 18632 messages using the distributed threshold signature.
The rotation messages signing ceremony is to be conducted before the hardfork, preparing the updated dc4bc tooling and instructions for participants. Further announcements will be fired once the ongoing dc4bc audit by Sigma Prime completed.
When Shanghai/Capella activates, partial withdrawals will transfer excessive balance from validators (above 32 Ether) to the withdrawals credentials (if 0x01).
For Lido it means that for the first post-hardfork week there will be roughly 200,000 Ether available (150,000 from 0x01 withdrawal credentials and 50,000 from 0x00 after 0x00->0x01 WC rotation) for spawning new validators or executing the first bunch of withdrawals requests. The projection on the timing of this buffer filling in under different scenarios is:
The scenarios considered are:
One of the possible forecasts that these funds will be used for arbitrage purposes of the Curve pool, pushing up the stETH/ETH
exchange rate closer to 1
.
The actionable item here is deploying the upgraded protocol before the hardfork, allowing first withdrawal requests happen as soon as possible.