lido
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- tags: withdrawals, research post title: Lido on Ethereum. Withdrawals landscape [WIP] authors: Eugene Pshenichnyy, Eugene Mamin, Victor Suzdalev, Artyom Veremeenko created: 25-12-2022 --- # Lido on Ethereum. Withdrawals landscape 🚧 **Early draft, Work in Progress** 🚧 --- The Shanghai/Capella update for Ethereum, scheduled for March-April 2023, will introduce withdrawals feature for stakers. This is a crucial element for Lido's liquid staking pool to become self-sustaining and fulfill its promise of offering redeemable stETH. The ability for stETH holders to withdraw their funds in a seamless and fair manner is essential for the smooth operation and long-term success of Lido on the Ethereum platform. 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. To address these challenges, the Lido team has proposed a protocol design with the in-protocol withdrawal requests queue. The most difficult-to-handle scenario for Lido is validator slashing. To handle it properly while providing the best possible experience for stakers, the "turbo" and "bunker" modes are proposed. If a threat of high protocol-wide penalties is detected, the "bunker mode" is on. Withdrawal requests are paused unless either "turbo mode" is back, or a specific criteria for the withdrawal request is met. Those delays socialize the penalties and risks between stakers and withdrawers. The slashings have been rare & singular events in Ethereum so far, so it seems like the probability of mass slashing in the Lido protocol is low. That being said, the protocol design resilience is the most valuable then put into test, and it's extremely important to get it right. The protocol design aims for a balance between providing an optimal experience for stakers and ensuring fairness for both those who withdraw and those who remain in the protocol, while also being implementable and safe. We are seeking the community's feedback in order 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. Have all important considerations especially caveats been covered in the proposal? 2. Whether the proposed design is sound, and are all considerations taken into account? 3. What improvements can be made to the current proposal, in terms of both mechanisms and text? Your feedback is invaluable to create a proposal that is effective, efficient, and fair for all stakeholders. ## Problem statement Lido protocol should provide stakers the ability to withdraw ETH corresponding to their stETH position. That should be done under the constraints of the exisiting Ethereum network design and coming changes, all while providing the best possible service for stakers and withdrawers and not compromising on protocol security and fairness. ## How Ethereum rewards, withdrawals, penalties & slashing 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 withdrawing the funds 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, withdrawal credentials, and 32 ETH attached. Once a new validator has been accepted and appears on the Beacon Chain with a balance of 32 Ether, the validator begins performing duties earning rewards or potentially facing penalties for inactivity or misbehavior. Ethereum validators earn rewards for securing the network through number of 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. This rewads are credited to validators' `feeRecipient` address on execution layer. Validators can be [penalized](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/rewards-and-penalties/#penalties) for missing block attestations, and if their balance falls below 16 ETH, 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 a lack of finality for more than four epochs in a row. Breaking consensus rules, such as double signing proposing/attesting blocks, 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 a period of 36 days. The validator is then 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 issues once slashing detected and has a fixed size. The midterm attack multiplier penalty applied on the 18th day and is proportional to the number of other slashings in the past ~36 days (counting backwards from the midterm point), the penalty can be as high as 32 ETH. In addition, the validator is penalized for inactivity during all ~36 days before being finally ejected. ```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 ``` It's worth noting that the validator's maximum effective balance is 32 ETH (i.e., if the validator's balance is 33 ETH, then its effective balance is 32 ETH, and 1 ETH is unaccounted for CL 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 (being injected in blocks as an [execution payload “operation”](https://eips.ethereum.org/EIPS/eip-4895) ). To fully withdraw a validator, it’s required to send a voluntary exit message, still performing participation in network consensus till the assigned by Consensus Layer [exit epoch](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#initiate_validator_exit) come. The validator would be withdrawn a few blocks later the exit epoch passed. 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)). ## How Lido on Ethereum works: socialization & pooling Lido's `stETH` is designed to provide the best holder experience. 1) `stETH` should be easy-to-understand: have the stETH balance track 1-1 state of the "my share of ETH in overall pool". 2) `stETH` has to be fungible token so 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. There are two Ethereum design features Lido has to be built around. First is the fact that there's no direct communication channel from CL to EL. Lido protocol learns of relevant CL state through the Oracle reports. Currently those happen approximately daily, the timeframe can — potentially — be shortened, but there's no practical design with the continuous reporting for every block. That forces "natural batching" for CL state syncronization inside Lido protocol. Another relevant Ethereum feature is that both deposits and withdrawals in Ethereum are implemented with async queues. Funds don't accrue rewards while they hadn't passed through the deposit queue, and can be penalized while validator exit isn't finalized is in progress. Slashings are async as well, with total amount of penalties depending on the overall state of the network during the whole slashing time of 36 days. Single stETH balance represents the share of the staking pool. Due to the async nature of both Ethereum design and "batched" EL-CL state coordination inside Lido, that staking pool must be viewed as a path-dependent & time-spawning process, where a share of the pool isn't just momentary share in relevant CL and EL balances, but is that share plus all the potential rewards, risks & delays. ## Embedded trust assumptions (How staking pools withdrawals would work in Shanghai/Capella) Lido optimizes for being operating & practical as fast as possible, and in doing so it embeds some trust assumptions. Development of Ethereum ecosystem, both the network itself and technological solutions, allow to gradualy sunset some of those. For instance, withdrawals spec would allow, among other things, to rotate Lido validators currently with BLS withdrawal credentials to smart contract one. One of the biggest trust assumptions of the protocol is Node Operators acting in good faith. In the current spec withdrawals require active cooperation from Lido Node Operators, and there's no way around it right now. There's a discussion around smart contract triggerable withdrawals. That feature being shipped would allow Lido and other liquid staking protocols to remove this trust assumption. Current design doc doesn't address this consideration. ## Lido withdrawal trilemma There are three main qualities the design should provide. 1. Fairness Any design Lido implements should first and foremost be fair. Both stakers & withdrawers interests should be met, and in doing so no party should get neither risk-free rewards nor carry the extra risks. That being said, the protocol safety & stakers interest are the first priority of the design. Among other things, the design should disincentivize the bank run possibility, not providing "first withdrawer" advantage. 2. User experience Design should optimize for best possible experience of both stakers and withdrawers. 3. Practicality Design should be implementable before the withdrawals-enabling hardfork & practical, both in smart contract architecture, gas costs and operation load for offchain tooling. ## Considerations Going forward, the protocol design should consider: 1. Design must conform to Ethereum spec & previous promises. 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 on top of which a market pricing in withdrawer's risks & delays could be built. 5. stETH account should be able to enter and exit the pool at any time. 6. Withdrawing shouldn’t have any extra fees. 7. Bank run should be disincentivized (no “first withdrawer advantage”). 8. Design should be practical from smart contracts standpoint. ## What design do we propose ### Overall mechanics For withdrawals, the regular flow looks like this: 1. Staker requests withdrawal, sending stETH to the Lido protocol withdrawal queue. Note that single withdrawal request amount is bounded. It shouldn't be too low so the protocol withdrawal queue gets griefed with dust withdrawal requests, nor shouldn't it be so high that it'll block the other requests from being processed for significant time due to taking all the throughput of Ethereum withdrawal queue. 2. The withdrawing request awaits in the Lido protocol withdrawal queue. 3. The request is getting processed. Lido protocol defines how much ETH should the withdrawer get. 4. Lido protocol provides the ETH for withdrawal. 5. Withdrawer’s ETH can be claimed. Due to async / queued nature of both Ethereum & Lido protocol, steps 2-3-4 are where most of design decisions are to be taken. As withdrawals on Ethereum are async, Lido has to have withdrawal queue as well. While withdrawal request awaits & is processed, validators funded with Lido protocol can get penalties (so the withdrawer gets less than 1:1 to withdrawing stETH amount) or slashing (special async batch of penalties and forced ejection in the end, exact amount of which depends on the network state during the 36 days after the penalty is triggered). The Lido withdrawal request queue is FIFO. The withdrawal request can't be revoked from the queue: otherwise, the Oracle report can be frontrunned to grief Lido's CL->EL withdrawals (send request -> revoke right before Lido administers CL->EL withdrawal). The request should be transferrable though: that'll open the possibility to build a market for "place in withdrawal queue", allowing for "emergency exit" by "outside-of-the-protocol" means. Withdrawer's stETH in withdrawal queue **stops accruing positive rebases**. Otherwise there's an incentive to always keep the stETH in the queue, depositing ETH back it once it's redeemable, and carry lower staking risks in the process. In case of negative rebase, rewards are still being accounted for, so penalties are socialized evenly between stakers and withdrawers. ### Buffers and shortcuts The faster funds get into CL and the longer they stay there, the higher rewards Lido accrues. There are 4 main sources of EL ETH in Lido: 1) deposits yet to be sent to CL; 2) skimmed CL rewards; 3) EL rewards; 4) fulfilled CL->EL withdrawals. Only the latter requires an active withdrawal of funds from CL to EL, so it seems prudent to delay using those funds if possible. EL ETH in the protocol forms a buffer which could be used to fulfil withdrawal requests, speeding up withdrawals processing and protecting source of rewards in the protocol. One particular thing to note is, as Ethereum withdrawals are processed in ~27h the fastest, in some cases it may be worth waiting to the next Lido oracle report (those happen approximately every 24h) to see if the withdrawal requests can be fulfilled with the funds accrued in EL buffers. ### Withdrawal queue & what the turbo and delayed modes are One of the big risks for any liquid staking protocol is “mass slashing” scenario. Lido works hard on technological and geographical diversity of the validators the Lido Node Operators are running, but in general single slashing may signify the wider issue. The more validators are under slashing in the given moment, the higher the penalties are. Thus, the protocol should be able to react on the signs of potential wide issue with high care and caution. Such signs are: 1. N ongoing slashings of Lido validators. 2. M ongoing slashings of Ethereum validators. 3. X negative Lido rebases during defined time window. 4. Ethereum entering inactivity leak mode during defined time window. If either is detected, the protocol goes into "delayed mode". In this mode, withdrawal request isn't processed unless either all "associated slashings" are resolved or the protocol is back to the "turbo mode". It doesn't seem fair for the withdrawal request to be exposed to risks happening way later than the request has been made. Withdrawal request is "associated" with all the Lido validators' slashings starting before the first oracle report after withdrawal is requested. Note that "associated" slashings affect the time when the request can be fulfilled, but not the stETH-ETH withdrawal rate: the rate would depend on all the penalties in the protocol by the moment the request can be fulfilled. For instance, consider this scenario: ![](https://hackmd.io/_uploads/HygDA1DKo.png) 1. Slashing 1 2. Oracle report 1 3. Slashing 2 4. **Withdrawal request** 5. Slashing 3 6. Oracle report 2 7. Slashing 4 Slashings 1, 2, 3 are "associated" with the request, and 4 isn't. The processing of the witdrawal request is delayed until either Lido goes back to "turbo" mode or slashing 3 is resolved completely. Note that the slashing 4 won't affect the timing on the withdrawal request, but the penalties would affect the resulting stETH-ETH rate for the request. Delaying all withdrawals if the detected slashing is a singular event isn't great for the holder experience. The turning point is the negative rebase, where total penalties exceed total rewards for a given protocol rebase: once this threshold is reached, all Lido stakers take a loss. As neither rewards nor slashing penalty can be predicted, we can roughly use 32 ETH / validator as a penalty ceiling, and Ethereum staking daily rewards rate with "confidence multiplier" applied as a rewards floor. For instance, targeting "4% APR CL rewards -20% cover worst possible slashing scenario for a daily report" yields the 13 validators under slashing as a threshold (roughly 1 slashing per 11,500 validators). It's worth noting that since the CL launch, the biggest number of ongoing slashings in Ethereum has been about 100 (second biggest amount is ~20 simultaneous slashings across the network), and zero Lido validators has been slashed. Another nuance is that turbo / delayed mode affects all withdrawal requests alike, "retroactively" delaying the requests not fulfiled by the moment the mode is switched to delayed. ### Gatekeepers It's impossible to predict every scenario, and it's important to get things right and prevent catastrophic scenario from breaking the protocol altogether. In order to allow the protocol to react on unforseen events, Gatekeeper feature is proposed. Gatekeeper is two-piece: the gate seal smart contract stopping 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 one on explicitly. ## Appendix 1: High-level smart contract architecture The overall design idea is defined in the various important aspects, though still far from being refined enough before running on-chain code on EVM practically. Let's dive deeper unravelling the knots. An extensive part of the Lido withdrawals requests handling is bounded to live off-chain due to: - consensus layer isolation - unbounded loop of the withdrawals finalization - handling slashing scenarios and lack of finality - validator exits requires sidecar channels with policy-guided timings - both partial and full withdrawals require tracking validator balances combined with withdrawal credentials balance in a synchronous manner (preventing double accounting of validator balances otherwise) The proposed architecture is agnostic to the final decisions regarding "turbo mode" and "delayed mode" criteria described above. Though, off-chain oracle code incorporates all of the complexity of these day-to-day decisions. The outline of the withdrawals-enabled Lido protocol design and funds flows is presented below: ![](https://hackmd.io/_uploads/BkIV2gJKo.jpg =700x) The Lido protocol collects all ether under control, and allows an off-chain oracle to decide on partitioning between withdrawals fulfillment and forwarding staking deposits, spawning new validators. ### On-chain contracts The picture below outlines the smart contracts architecture. ![](https://hackmd.io/_uploads/By_jeodKi.jpg) #### WithdrawalsQueue `stETH` holder interacts with Lido by locking tokens on the `WithdrawalQueue` contract and receiving an enqueued position index in return. The position is monotonically increasing counter representing the place in the internal withdrawal requests queue. After the withdrawals requests have been finalized, anyone can trigger sending the claimed ether by providing finalized position index to the original recipient by interacting with `WithdrawalsQueue`. ![](https://hackmd.io/_uploads/HkIWF3bFo.jpg =700x) #### WithdrawalsVault Lido has only one type-`0x01` [withdrawal credentials](https://twitter.com/LidoFinance/status/1414644285425831944) currently corresponding to the upgradable [`WithdrawalsManagerStub`](https://docs.lido.fi/contracts/withdrawals-manager-stub) contract. The implementation upgrades to the new `WithdrawalsVault` contract resembling [`Execution Layer Rewards Vault`](https://etherscan.io/address/0x388C818CA8B9251b393131C08a736A67ccB19297) interface and mechanics by allowing funds withdrawing and appending to the Lido buffered ether as a part of each oracle report. #### LidoOracle Accepts oracle committee reports about protocol accounting state (validators and their balances), fulfillment of withdrawal requests, and exited validators since the previous quorum-reached report. Triggers `stETH` token rebase, calculates rewards and performs protocol fee distribution. The following actions are performed as a part of the report inside the contract: - withdraw & append the `LidoExecutionLayerRewardsVault` balance to the Lido [buffered ether](https://docs.lido.fi/contracts/lido#getbufferedether) - withdraw & append the `WithdrawalsVault` balance to the Lido buffered ether - transfers the requested by oracle committee amount of buffered ether to `WithdrawalsQueue` - advance the counter of the finalized withdrawals requests - burn underlying `stETH` shares for the just finalized withdrawal requests #### ValidatorExitBus Accepts oracle committee reports about required validator exits. Emits events with the validator identification data (a validator belonging attributes and pubkey) ```solidity event ValidatorExitRequest( address indexed stakingModule, uint256 indexed nodeOperatorId, uint256 indexed validatorId, bytes validatorPubkey ); ``` The events are used to monitor for exit requests, executing and propagating voluntary exits from the side of node operators once the belonging key detected. ### Off-chain oracle Off-chain oracle consists of two functional modules. - The **accounting module** is responsible for protocol accounting and follows the current lifecycle (24h amortized period), reporting more values (withdrawals requests to finalize, withdrawals vault balance corresponding to the report's epoch, and exited validators for each node operator). This module interacts with the `LidoOracle` contract. - The **ejection module** performs validator exit requests singalling, estimating the needed validator exits amount and interacting with `ValidatorExitBus`, using more agile scheduling (roughly each 6 hours). Both modules are running using the same oracle committee, but their lifecycle is uncoupled due to different asynchronous environment and report asumptions. #### Validator exit order Lido has embarked on the [validator exit order discussion](https://research.lido.fi/t/withdrawals-on-validator-exiting-order/3048). The gist of the problem is establishing a consensus on algorithm structure to implement deterministic and feasible exits accepted by node operators. There are various considerations regarding ejection ordering. - alignment with [Lido goals](https://research.lido.fi/t/lido-on-ethereum-community-validation-manifesto/3331) and [scorecard](https://lido.fi/scorecard) - equal opportunities for node operators earning with Lido - lack of deep controversities with the current validator distribution - consistency with the activation algorithm of new validators (to converge to the balanced state node operators keys distibution in a long run) - tech implementability The current sentiment is mostly towards [combined](https://research.lido.fi/t/withdrawals-on-validator-exiting-order/3048#combined-approach-17) approach, though, no strong objections to start with [more simple](https://research.lido.fi/t/withdrawals-on-validator-exiting-order/3048#approach-1-eject-from-the-node-operator-with-the-largest-number-of-active-validators-15) algorithm in the short term (basically reverting the activation algorithm logic ejecting the next key from the largest node operator). #### Validator exit sidecar Lido devteam has proposed an additional tooling for semi-automated exit messages signing. The full proposal text is presented on the [research forum](https://research.lido.fi/t/withdrawals-automating-lido-validator-exits/3272). The main idea is to have a batch of pre-singed messages ready to be fired once requested from `ValidatorExitBus` with the emitted events. Community meetings with the Lido-participating node operators representatives made it clear that while the most node operators are aligned with using the proposed tools, there are some corner cases where in-house solutions could serve better. The main takeaway that Lido should not enforce infrastructure requrements, better defining, in constract, a policy for performing validator exits, centered around "time-to-react" requirements. ### On-chain safety measures The proposed architecture relies on the data provided from off-chain oracles using newly developed software. There are multiple levels of safety measures to minimize the possible risks and attack surface. #### Process & operations - The protocol will undergo multiple external audits for both on-chain (smart contracts) and off-chain (oracle daemon) parts. The proposal is to perform three security audits, and at least two of them before the mainnet contracts deployment. - Lido governance team [facilitates](https://snapshot.org/#/lido-snapshot.eth/proposal/0xcbf534335fe07c046caa933e1623ac38bfb3d1890ab825264a0b47415cf7799b) the Oracle set expansion initiative to attain 3rd-parties participation in the committee beyond node operators only (improving the quorum from 3/5 to 5/9) - The prominent area for the Lido protocol is a formal verification. Lido contributors are looking for engagement of security experts in this field. #### Safety nets There are on-chain safety/sanity checks capping the values in the Oracle report. In a nutshell it prevents daily report drastically change the protocol state including rewards and penalties, lowering the possibility of `stETH/wstETH` price manipulation and harming users. The list of the items that will be enforced on-chain: - **Safety checks on oracle report**: - For Consensus Layer data part of the report: - hard caps preventing consensus layer balance changes exceed sane APR boundaries (both positive and negative) - hard cap on the number of activated validators since the previous report - hard cap on the number of exited validators since the previous report - For Execution Layer data part of the report: - limiters to fit the rebase in the frame making oracle arb sandwiching economically infeasible - security check on the placement block number of the recently finalized withdrawal requests - security check on the ether amount for the recently finalized withdrawal requests - **WithdrawalsQueue requests** - `WithdrawalsQueue` accepts the amount distinctly different from dust (>= 1e-9 stETH) but lower than 16000 stETH (to prevent queue clogging) per single request. - `WithdrawalsQueue` has the `pause`/`resume` requests placement & fulfillment levers to be applied in case of emergencies and suspicious activity. When queue is paused, no new withdrawal requests can neither be placed nor finalized. #### Gate seal smart contract The smart contracts that used by [Gatekeepers](#Gatekeepers) allowing withdrawals being stopped once unforseen event happenned. Once the stop is triggered by the assigned gatekeepers multisig, the contract calls `WithdrawalsQueue.pause` and starts a confirmation timer. Aragon Agent through the voting should confirm that the triggered stop is valid before confirmation timer expired (roughly one week: 72h voting + comms timings), otherwise anyone (stranger) can lift the stop calling the permissionless method, which internally invokes `WithdrawalsQueue.resume`. - The gate seal can be broken only once, so the DAO would have to switch the new one on explicitly. - The gate seal contract itself has an expiration date (like a difficulty bomb for Ethereum PoW before) that self-deprecates the contract if the Lido DAO haven't decided to prolong the experation date via Aragon voting. ## Appendix 2: User flow examples For clarity of the explanation let's assume: - Alice is a holder of stETH who wants to withdraw some of her funds - there are no other withdrawal requests except for the one mentioned in a case - initial protocol parameters: - there are at least 2 validators in the protocol - amount of total pooled ether is 200 (validators balances, various EL buffers) - amount of total stETH shares is 192 - by "W-buffers ether" we denote the ether in EL buffers (withdrawal credentials contract, EL rewards, deposit buffer) considered by Oracle accounting module alowed for satisfaction of withdrawal requests - all function calls mentioned are related to `WithdrawalQueue` contract ### Case 1: enough ETH in EL buffers, turbo mode 1. Alice: requests to withdraw `1 stETH` by call `requestWithdrawal(10**18, recipientAddress)`. The request is registered with id `requestId`. Maximum amount of ETH to receive is fixed to `1 ETH`. Alice's `1 stETH` is locked on `WithdrawalQueue`. 2. Anyone: can call `getWithdrawalRequestStatus(requestId)` to check the request status. It can be "not finalized", "finalized" (what means it the ETH is claimable) or "claimed". 3. Oracle accounting module: reports. `1 ETH` is moved from `W-buffers` to `WithdrawalQueue`. The request `requestId` is finalized and is claimable now. 5. Anyone: can call `claim(requestId, ...)`. `1 ETH` is moved to the `recipientAddress`, request `requestId` is marked claimed. The min time before ether is claimable after it was requested varies from 12 seconds (one block duration) to approximately 24 hours (time between Oracle accounting module reports). ### Case 2: not enough ETH in EL buffers, turbo mode Here we assume there is 60 ETH in W-buffers at the moment of the first Oracle accounting report after the withdrawal request is made. 1. Alice: makes withdrawal request of `100 stETH`. The request `requestId` is registered, max amount of ETH to receive is fixed to `100 ETH`. 2. Oracle accounting module reports. No ether is moved from W-buffers, because the amount (60 ETH) isn't enough to satisfy the request. Status of request `requestId` is unchanged. 3. Oracle ejection module reports to `ValidatorExitBus` to request 2 validators to exit. The contract emits two `ValidatorExitRequest` events. Node Operators receive `ValidatorExitRequest` events and do Consensus Layer commands to initiate exits of 2 validators. 3. *(time passes (at least 27 hours) till the validators are ejected and their funds are withdrawn from CL to EL withdrawal contract.)* 4. Oracle accounting module reports. *(There are at least (60+64) ETH W-buffers.)* `100 ETH` is moved from `W-buffers` to `WithdrawalQueue`. Request `requestId` is finalized with amount `100 ETH` and marked as claimable 5. Anyone: can call `claim(requestId, ...)`. `100 ETH` is moved to the `recipientAddress`, request `requestId` is marked claimed. \* Steps 3 and 4 might happen in reversed order. It won't change outcome of the case. The min time before ether is claimable after it was requested is approximately 27 hours. ### Case 3: negative stETH rebase, turbo mode Continues case 1 with the difference that stETH rebased negatively. Here we demonstrate more details of the accounting to explain the numbers. We assume that: - total amount of ETH pooled decreased between the previous Oracle accounting module report and the report mentioned in step 2 - the ETH decrease was caused by penalties and/or valdiator slashings. The situation is not considered by the Oracle accounting module severe enough to switch into the delayed mode. - At the moment of Oracle accounting module report there are at least `0.99 ETH` in W-buffers. 1. Alice requests to withdraw `1 stETH`. Request `requestId` is registered, Alice's `0.96*10^18` stETH shares locked on `WithrawalQueue`, current amount of pooled ether `200*10^18` is recorded with the request. 2. Oracle accounting module reports. Total `198 ETH` pooled is reported. The request is finalized with `0.99 ETH` (calculated as `(198/192)`) and is claimable. The min time before ether is claimable is the same as in case 1. ### Case 4: positive stETH rebase, delayed mode In this case we assume that: - Between the previous Oracle accounting module report and the report mentioned in step 2 there were validator slashings. The situation IS considered by the Oracle accounting module severe enough to switch into the delayed mode. - Oracle accounting module uses option when after slashings it postpones finalization of the requests for 36 days (the other design option mentioned in the main part of the doc is to wait 18 days till the midterm, when the most of the slashing effects can be identified) - At the moment of the first Oracle accounting module report there are at least `1 ETH` in W-buffers. 1. Alice requests to withdraw `1 stETH`. Request `requestId` is registered, Alice's `0.96*10^18` stETH shares locked on `WithrawalQueue`, current amount of pooled ether `200*10^18` is recorded with the request. 2. Oracle accounting module reports. The request `requestId` isn't finalized despite there is enough ether in W-buffers due to the delayed mode. Finalization of all unfinalized yet requests is postponed for 36 days. 3. *(time passes, at least 36 days till all mentioned in step 2 slashed validators are exited)* 4. Oracle accounting module reports. Total `202 ETH` pooled is reported. Here we assume the tootal pooled ether is decreased due to the 2 validators slashing penalties, but in total it increased due to the various kinds of rewards. The reqeust `requestId` is finalized with `1 ETH` and is claimable. The min time before ether is claimable after it was requested is approximately 36 days. ## Appendix 3: 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. Lido devteam calls out for a rotation messages signing ceremony to be conducted in February, 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 SigmaPrime completed. ### Initial rewards skimming gush When Shanghai/Capella activates, partial withdrawals will transfer excessive balance from validators (above 32 ETH) to the withdrawals credentials (if 0x01). For Lido it means that for the first post-hardfork week there will be roughly 150,000 ETH available for spawning new validators or executing the first bunch of withdrawals requests. 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.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully