--- tags: withdrawals, research post title: Lido on Ethereum. Withdrawals landscape. authors: Eugene Pshenichnyy, Eugene Mamin, Victor Suzdalev, Artyom Veremeenko, Sam Kozin created: 25-12-2022 --- # Lido on Ethereum. Withdrawals Landscape. The document outlines the high-level design of withdrawals feature for Lido on Ethereum protocol. 1. [Intro](#intro). 2. [Problem statement](#Problem-statement). 3. [How Ethereum rewards & withdrawals work](#How-Ethereum-rewards-amp-withdrawals-work). 4. [How Lido on Ethereum works: socialization & pooling](#How-Lido-on-Ethereum-works-socialization-amp-pooling). 5. [Lido withdrawals design: goals and constraints](#Lido-withdrawals-design-goals-and-constraints). 6. [Proposed design](#Proposed-design). 1. [Main flow](#Main-flow). 2. [How Ethereum penalties & slashing work](#How-Ethereum-penalties-amp-slashing-work). 3. [How slashings would affect withdrawal request fulfillment](#How-slashings-would-affect-withdrawal-request-fulfillment). 4. [Withdrawal request fulfillment mechanics](#Withdrawal-request-fulfillment-mechanics). 5. [Using buffered Ether from deposits and rewards for withdrawal request fulfillment](#Using-buffered-Ether-from-deposits-and-rewards-for-withdrawal-request-fulfillment). 6. [Handling overlapping slashing](#Handling-overlapping-slashing). 7. [Gatekeepers committee](#Gatekeepers-committee). 7. [Appendix 1: User flow examples](#Appendix-1-User-flow-examples). 8. [Appendix 2: Skimmed rewards and 0x00 -> 0x01 WC rotation](#Appendix-2-Skimmed-rewards-and-0x00--gt-0x01-WC-rotation). ## Intro 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](#Appendix-2-Skimmed-rewards-and-0x00--gt-0x01-WC-rotation). 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: 1. Are the chosen decision drivers right? 2. Given those drivers, is that the best design possible? 3. What can be improved in the design or its presentation? Your feedback is invaluable to create a proposal that is effective, efficient, and fair for all stakeholders. ## Problem statement 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. ## How Ethereum rewards & withdrawals work 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](https://etherscan.io/address/0x00000000219ab540356cBB839Cbe05303d7705Fa) 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](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/rewards-and-penalties/#rewards) are credited to the validator's balance on the Consensus Layer. Starting from the Merge, validators also earn [Execution Layer (EL) rewards](https://www.figment.io/resources/ethereum-understanding-post-merge-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”](https://eips.ethereum.org/EIPS/eip-4895). To fully withdraw a validator, it’s required to send a voluntary exit message, still participating in the network consensus until the [exit epoch](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#initiate_validator_exit) assigned by the Consensus Layer comes. The validator would be withdrawn once peaked up with a [withdrawals sweep](https://github.com/ethereum/consensus-specs/pull/3068) round. There’s [an ongoing discussion around smart contract triggerable exits](https://ethresear.ch/t/withdrawal-credentials-exits-based-on-a-generalized-message-bus/12516). 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](https://kb.beaconcha.in/glossary#2.-pending)). 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](https://notes.ethereum.org/@launchpad/withdrawals-faq#Q-How-fast-will-I-be-able-to-make-a-partial-withdrawal-Or-when-will-I-get-access-to-the-excess-rewards-that-are-on-my-validator) (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.* ## How Lido on Ethereum works: socialization & pooling Lido’s stETH is designed to provide the best user experience: - stETH should be easy-to-understand: have the stETH balance track 1-1 state of the “my share of Ether in the overall pool”. - stETH has to be a fungible token so as not to divide liquidity. 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](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/#validators). 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. ## Lido withdrawals design: goals and constraints ### Design goals 1. Lido withdrawals can happen when the Ethereum withdrawals are switched on. 2. Withdrawals should have good UX. 3. Protocol design should be fair to unsophisticated stakers. ### Constraints 1. Design must conform to Ethereum spec. 2. Design should be as straightforward and easy to understand as possible. 3. Design shouldn’t make assumptions about future events & market conditions. 4. Design should provide the necessary capabilities to construct a withdrawer’s risks & delays "pricing market". 5. Design should be practical from smart contracts composition & operation gas costs standpoint. 6. Design should minimize the time funds are in the activation queue and buffer, where they don't contribute to rewards for the stakers. ## Proposed design ### Main flow 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: 1. **Request:** To withdraw stETH to Ether, the user sends the withdrawal request to the `WithdrawalQueue` contract, locking the stETH amount to be withdrawn. 2. **Fulfillment:** The protocol handles the requests one-by-one, in the order of creation. Once the protocol has enough information to calculate the stETH share redemption rate of the next request and obtains enough Ether to handle it, the request can be finalized: the required amount of Ether is reserved and the locked stETH is burned. 3. **Claim:** The user can then claim their Ether at any time in the future. The stETH share redemption rate for each request is determined at the time of its finalization and is the inverse of the Ether/stETH staking rate. 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. 1. **Withdrawal requests cannot be canceled.** To fulfill a withdrawal request, the Lido protocol potentially has to eject validators. A malicious user could send a withdrawal request to the queue, wait until the protocol sends ejection requests to the corresponding Node Operators, and cancel the request after that. By repeating this process, the attacker could effectively lower the protocol APR by forcing Lido validators to spend time in the activation queue without accruing rewards. If the withdrawal request can't be canceled, there's no vulnerability. As noted above, making the position in the withdrawal queue transferrable would provide a "fast exit path" for regular stakers. 2. **The redemption rate at which a request is fulfilled cannot be better than the redemption rate on the request creation.** Otherwise, there’s an incentive to always keep the stETH in the queue, depositing Ether back once it’s redeemable, as this allows to carry lower staking risks without losing rewards. This would also allow a malicious actor to effectively lower the protocol APR. To avoid this, we propose that penalties leading to a negative rebase are accounted for and socialized evenly between stETH holders and withdrawers. Positive rebases could still affect requests in the queue, but only to the point where rebases compensate for previously accrued penalties and don't push the redemption rate higher than it was at the moment of the withdrawal request's creation. 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: 1. **How would slashings affect withdrawal request fulfillment?** Since slashings have a pretty async nature the final penalties for each slashing can only be calculated precisely after the validator is exited. The time it takes for a slashed validator to exit is at least 36 days ([the precise definition is `max(36 days, exit queue duration)`](#How-Ethereum-penalties-amp-slashing-work)). 2. **What exactly should the withdrawal request fulfillment mechanics look like?** Since Execution and Consensus Layers have no direct communication channel, Lido uses oracle to sync the data between them. To fulfill a withdrawal request, the protocol has to know the up-to-date stETH share redemption rate which is available only at the moment of the oracle report. 3. **Should the protocol use buffered** Ether (e.g. not-yet-staked user's Ether) and rewards for withdrawal requests fulfillment? To properly discuss how the Lido protocol should work with slashings, let's dive into Ethereum slashing mechanics first. ### How Ethereum penalties & slashing work Validators can be [penalized](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/rewards-and-penalties/#penalties) for missing block attestations. If their balance falls below 16 Ether, they get ejected from the network. While these penalties are usually [minor](https://twitter.com/superphiz/status/1606331749754998786) when the network is functioning normally, they can become severe if the network enters [“inactivity leak”](https://eth2book.info/altair/part2/incentives/inactivity) 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](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/rewards-and-penalties/#slashing) 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](https://eth2book.info/altair/annotated-spec#get_validator_churn_limit). Only then the validator is permanently removed from the network. Slashing penalties [consist of three types](https://docs.prylabs.network/docs/how-prysm-works/validator-lifecycle#slashing-state). 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). ```mermaid 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 ``` ### How slashings would affect withdrawal request fulfillment 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](https://ethresear.ch/t/handling-withdrawals-in-lidos-eth-liquid-staking-protocol/8873) 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: ```python 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](#Withdrawal-request-fullfilment-mechanics) 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](#Handling-overlapping-slashing)). 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. ### Withdrawal request fulfillment mechanics To fulfill a withdrawal request, the protocol should do several things: 1. Determine the next validator to exit based on a predetermined order and notify the respective Node Operator of this decision. 2. Decide on the time of finalization (including delays imposed by slashing conditions) and the stETH share redemption rate of each withdrawal request. 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](https://research.lido.fi/t/withdrawals-on-validator-exiting-order/3048/1) 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. 1. Assign oracle committee members the additional duty of signaling the next validators to eject. Once in `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. 2. Oracles must keep track of the queue, and when more requests can be finalized based on the consensus and Execution Layer states, the oracle must transmit the index of the last order to be finalized, the amount of Ether needed to satisfy the range of orders and the redemption rate. Withdrawal requests that were sent less than `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. ### Using buffered Ether from deposits and rewards for withdrawal request fulfillment 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. ![](https://hackmd.io/_uploads/Bk4z-EPoj.png) ### Handling overlapping slashing 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: - The protocol switches to bunker mode due to **mass slashing 1** - A user submits a withdrawal request to the WithdrawalQueue - **Slashing 2** occurs, not necessarily a big one - All validators from mass **slashing 1** have exited - *Option 1: Withdrawal request can be finalized here* - After some time, all validators from **slashing 2** have exited - *Option 2: Withdrawal request can be finalized here* 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. ### Gatekeepers committee 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: - the gate seal smart contract stops the withdrawals in the protocol - multisig with the rights to trigger the stop. 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. ## Appendix 1: User flow examples 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: - Alice is a holder of stETH who wants to withdraw some of her funds. There are no other withdrawal requests - There are at least 5 validators in the protocol - The amount of total pooled Ether is `200 ETH` (validators balances, various EL buffers) ### Case 1: enough ETH in buffer, turbo mode **Case precondition**: there is `1 ETH` in the buffer. 1. Alice: requests to withdraw `1 stETH`. Maximum amount of ETH to receive is fixed to `1 ETH`. Alice's `1 stETH` is locked on `WithdrawalQueue`. 2. Lido oracle: reports. `1 ETH` is moved from buffer to `WithdrawalQueue` to finalize Alice's request. 3. Anyone: can claim the 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 2: not enough ETH in buffer, turbo mode **Case precondition**: there is `60 ETH` in the buffer. 1. Alice: makes withdrawal request of `100 stETH`. The request is registered, and the max amount of ETH to receive is fixed at `100 ETH``. 2. Lido oracle: reports. No Ether is moved from the buffer because the amount `60 ETH` isn't enough to satisfy the request. The status of the request is unchanged. 3. Lido protocol: decides to request 2 validators to exit. Node Operators receive events and do Consensus Layer commands to initiate exits of 2 validators. 3. *(time passes (at least 27 hours for exit queue and about 52 hours avg for the withdrawals sweep) till the validators are ejected and their funds are withdrawn from Consensus Layer to Execution Layer withdrawal contract)* 4. Lido oracle: reports. *(There are at least `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 3: negative stETH rebase, turbo mode **Case preconditions**: - there is `1 ETH` in the buffer - the ETH decrease was caused by penalties and/or validator slashings and the situation isn't severe enough for switching to the bunker mode The following scenario would happen: 1. Alice: requests to withdraw `1 stETH`. The request is registered. NB: the current amount of total pooled Ether is `200 ETH` 2. Lido oracle: reports. Total `198 ETH` pooled Ether is reported. The request is finalized with `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 4: positive stETH rebase, bunker mode **Case preconditions**: - there is `1 ETH` in the buffer. - the ETH decrease was caused by penalties and validator slashings and the situation IS severe enough for switching to the bunker mode Then the following scenario would happened: 1. Alice: requests to withdraw `1 stETH`. The request is registered, Alice's stETH locked on `WithdrawalQueue`. NB: the current amount of pooled Ether `200 ETH`. 2. Lido oracle: reports. Bunker mode activated due to projected impact of the registered slashings. The request isn't finalized despite there is enough Ether in the buffer because of the bunker mode. Finalization of all unfinalized yet requests is postponed till bunker mode ends or the request-associated slashings resolved. 3. *(time passes till the condition within point 2 is met which should happen after 18 days when midterm passed)* 4. Lido oracle: reports. The protocol enters turbo mode. Total `202 ETH` pooled Ether is reported. Despite the total pooled Ether is decreased due to the validators slashing penalties, the total redemption rate has been increased due to the various kinds of rewards (like MEV spikes). The request is finalized with `1 ETH` and is claimable. The min time before Ether is claimable after it was requested is approximately 18 days. ## Appendix 2: Skimmed rewards and 0x00 -> 0x01 WC rotation ### Withdrawals credentials rotation Lido has [13%](https://dune.com/queries/96764/193960) of validators using BLS type-0x00 withdrawal credentials. These credentials are controlled by the [distributed 6/11 threshold signature](https://blog.lido.fi/withdrawal-credentials-in-lido/). 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](https://github.com/lidofinance/dc4bc/commit/7a391fa6f109b85830da5d50bbce9ab68cf4a171) tooling and instructions for participants. Further announcements will be fired once the ongoing dc4bc audit by Sigma Prime completed. ### Initial rewards skimming gush 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: ![](https://hackmd.io/_uploads/HySlvSdso.png) The scenarios considered are: - **Forward index rotation**: validators are rotated in order based on validator Index. This scenario represents one of the “worst case” possibilities, delaying skimming rewards for Lido - **Backward index rotation**: validators are rotated in reversed order based on Validation Index, representing opposite case for previous scenario with minimum delay for Lido due to later rotation of “older” non-Lido validators - **Best case scenario**: rotation messages are sent only by Lido validators - this scenario provides upper limit on possible skimming inflow with unrealistic assumption on other actors behaviour One of the possible forecasts that these funds will be used for arbitrage purposes of the [Curve pool](https://curve.fi/steth), 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. --- ## Edits log - **2023-01-28**: More precise timings on validator withdrawals (according to [[1]](https://notes.ethereum.org/@launchpad/withdrawals-faq#Q-How-fast-will-I-be-able-to-make-a-partial-withdrawal-Or-when-will-I-get-access-to-the-excess-rewards-that-are-on-my-validator), [[2]](https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#new-get_expected_withdrawals)), minor edits.