or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
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.
Do you want to remove this version name and description?
Syncing
---
LIP-19. Staking Router
Abstract
Other than the curated set of node operators, the existing monolithic architecture of the registry makes it difficult for Lido to onboard different validator subsets such as community operators, DVT-enabled validators, off-chain and L2 validators. We propose to move to a modular architecture by introducing Staking Router. With this protocol update, various validator subsets will be able to join Lido as separate registries, or modules. Each module will be responsible for managing their operators, storing their keys, distributing stake and rewards between them. Modules are integrated into Lido through Staking Router, a controller contract that operates at the module-level. This document covers the important decisions made in the design of Staking Router and the emerging changes to the existing protocol.
Motivation
As introduced in The Next Chapter for Lido | Lido Finance, the protocol’s long-term goal is to ensure that the validator set is good and stays good. To achieve this, Lido is working on diversifying its validator set: committing to allowing solo stakers to participat1 in the protocol and actively experimenting with DVT.
To incorporate all these mechanics, it is necessary to support this possibility at the smart-contract level. Now Lido only supports the validator set curated by Lido DAO through LNOSG. All logic for managing the list of operator nodes, validator keys, and reward distribution is handled by the contract NodeOperatorRegistry, which is part of the Lido protocol.
The current monolithic architecture of the protocol needs to be more flexible to test different validator set approaches because different validator subsets require different implementations in the protocol. For instance, permissionless validation requires the implementation of bonds or a reputation mechanism, and to integrate DVT; it is required to support even more features like random committee formation, DKG, validator performance oracle, and so on. Since the validator registry is strongly coupled with the rest of the protocol logic, it is difficult to implement these features in parallel.
To simplify the protocol code and speed up further iterations, we propose a modular way for Lido on Ethereum validator set. Instead of the monolithic registry, we want to introduce the StakingRouter contract, which would be responsible for stake balancing and reward distribution between modules that implement business logic related to specific validator subsets.
This approach is similar to the current NodeOperatorRegistry, which distributes staked ether between node operators. Still, instead of addresses of node operators, StakingRouter has a list of smart contracts responsible for different validator subsets along with a quota for the maximum amount of stake set in percent.
We propose implementing a modular approach based on the staking router in the next protocol upgrade. Just after the upgrade, the current NodeOperatorRegistry contract will become the only module for the staking router with a stake share of 100%. As more validator subsets are connected to the StakingRouter, Lido DAO can start with small numbers to safely test new approaches before gradually increasing the limit. This will significantly speed up experiments with the diversification and optimization of the validator set, allowing several independent teams to work on this without a major protocol upgrade.
Despite the simplicity of the idea behind StakingRouter, it is necessary to agree on a number of architectural decisions and a router-module interface. The Lido protocol contributors prepared an Architecture Design Record about StakingRouter in which they looked at various approaches to the problem.
General overview
The diagram below illustrates a high-level architecture that the StakingRouter upgrade proposes.
Deposit workflow
The deposit workflow is a procedure whereby a batch of deposits of 32 ether together with validator keys are submitted to
DepositContract
in a single transaction. Because each module is responsible for their own deposits, each batch deposit is limited to keys from one module only.The deposit procedure is essentially a chain of contract calls initiated by an off-chain software called the depositor bot. The bot collects guardian messages verifying that there are no pre-submitted keys in the registry to exploit the frontrunning vulnerability. Once the guardian quorum is reached, the bot passes the messages and the module identifier to

DepositSecurityModule
which first verifies the authenticity of messages and calls the deposit function onLido
passing the maximum number of deposits that the current block size allows. The latter then computes the maximum number of deposits to include in the batch based on the current deposit buffer and callsStakingRouter
's deposit function.StakingRouter
calculates the allocation of buffered ether to the module whose keys are to be used in the deposit and finally performs the batch deposit.Allocation algorithm
One of the main responsibilites of
StakingRouter
is to distribute depositable ether to modules in a proportion that brings them closer to their DAO-imposed target shares. The target shares are the DAO's way to guide newly joined module through a controlled growth. The motivation behind this mechanism is given in ADR: Staking Router.As mentioned in the Deposit workflow section, the deposit procedure starts with the depositor bot calling the deposit function on
DepositSecurityModule
. Along with guardian messages, the bot passes along the identifier of the staking module whose keys will be used for the deposit.Lido
then relays the call toStakingRouter
which calculates the allocation to the specified module and performs the deposit returning any unused ether back toLido
.The main variables of the allocation algorithm are:
_maxDepositsPerBlock
fromDepositSecurityModule
, a manually set value that limits the number of deposits in a single transaction based on the current block size;depositableEth
fromLido
, a multiple of 32 ether passed toStakingRouter
for deposit;DepositContract
;The steps of the allocation algorithm:
_maxDepositsPerBlock
inDepositSecurityModule
. This is to ensure that the batch transaction does not hit the block size limit;Lido
asBUFFERED_ETHER_POSITION
is the sum of all user submissions and execution-layer rewards transferred toLido
from the execution-layer rewards vault;finalMaxDeposits
.finalMaxDeposits
and multiplying by the module's target share. The result is the flat cap of active keys of the module;Lido
.The animated chart below illustrates the process of allocation where the bars represent the modules, the black outline is the module capacity, the green fill is the active keys, and yellow is the allocation.
Rewards distribution
Every stETH represents equivalent ether in the total supply. However, internally the protocol keeps track of user balances by way of shares, a fraction of the total stETH pool that each holder is entitled to. Whenever a user submits ether,
Lido
mints new shares at the current rate of a share to stETH. On the rebase, the same number of shares appreciate or depreciate in terms of stETH compared to the previous day based on whether the validators gained profit or were slashed.Thus, one of the core mechanisms in Lido is synchronizing validator balances on the consensus layer with the total stETH supply on the execution layer. Lido employs a set of oracles to track validator balances and report them at regular intervals to
LidoOracle
, a contract that stores the beacon chain state. Once the quorum is reached, the total supply of stETH is updated. This is how all stETH holders receive their rewards. However, to ensure sustainability and to incentivize node operators, Lido takes a cut of these rewards and splits it between the protocol treasury and node operators on each finalized report.On each report, the protocol determines whether the rebase is positive by comparing the total balance of validators on the consensus layer against the reward base, which is the sum of the previously reported balance and all newly activated validators times 32 ether. Even though execution-layer rewards are distributed across the protocol, they do not contribute to the decision on whether the rebase is positive or negative. If the rebase is positive,
Lido
fetches the protocol fee breakdown fromStakingRouter
and mints new shares in the amount that reduces stETH inflation by the total protocol fee and then distributes the fee between the modules and the treasury.Fee structure
The fee structure is set independently in each module. There are two components to the fee structure: the module fee and the treasury fee, both specified as percentages (basis points). For example, a 5% (500 basis points) module fee split between node operators in the module and a 5% (500 basis points) treasury fee sent to the treasury. Additionally,
StakingRouter
utilizes a precision factor of 100 * 1018 for fees that prevents arithmetic operations from truncating the fees of small modules.Because Lido does not account for validator performance, the protocol fee is distributed between modules proportionally to active validators and the specified module fee. For example, a module with 75% of all validators in the protocol and a 5% module fee will receive 3.75% of the total rewards across the protocol, obtained with the equation below:
The treasury fee is calculated the same way. The same module from the example above with a 5% treasury fee will bring 3.75% to the treasury:
It follows that the total protocol fee is calculated by adding the module and treasury shares across all modules:
This means that if the modules' fee and treasury fee do not exceed 10%, the total protocol fee will not either, no matter how many modules there are.
There is also an edge case where the module is stopped for emergency while its validators are still active. In this case the module fee will be transferred to the treasury and once the module is back online, the rewards will be returned back to the module from the treasury.
Specification
Overview
This Staking Router update includes one new contract,
StakingRouter
, and significant changes to the three existing contracts:Lido
,NodeOperatorsRegistry
andDepositSecuirityModule
.StakingRouter
is the contract responsible for managing the list of modules, allocating stake to modules, calculating fees and sending deposits toDepositContract
, a former responsibility ofLido
.Lido
will be distributing the protocol fee based on the calculations provided byStakingRouter
. With a significant amount of protocol logic moving out ofLido
, a number of its functions will be deprecated.DepositSecurityModule
will updated to reflect the changes in the deposit workflow. And finally, to connect toStakingRouter
,NodeOperatorsRegistry
now must implement theIStakingModule
interface and expose functions given in this specification.StakingRouter
StakingRouter
is the top-level controller contract tha manages the list of modules, allocates stake to modules, calculates fees and submits deposits toDepositContract
.Used libraries:
UnstructuredStorage
associates a value with a storage key obtained with a keccak256 hash of the descriptive string, to avoid storage slot collissions;MinFirstAllocationStrategy
abstracts away allocation logic described in Allocation algorithm.Inherited contracts:
BeaconChainDepositor
provides a function for performing a batch deposit toDepositContract
;AccessControlEnumerable
provides a role-based access system where members of a role may be enumerated.Enum:
StakingModuleStatus
Indicates the module's operational mode.
Active
DepositsPaused
Stopped
Struct:
StakingModule
A structure containing all relevant information about the staking module.
id
uint24
stakingModuleAddress
address
stakingModuleFee
uint16
treasuryFee
uint16
targetShare
uint16
status
uint8
StakingModuleStatus
enumname
string
lastDepositAt
uint64
lastDepositBlock
uint256
exitedKeysCount
uint256
Struct:
KeysCountCorrection
A structure containing corrections details for unsafely updating exited keys; accepting a structure instead of individual arguments prevents the stack depth compiler error.
currentModuleExitedKeysCount
uint256
currentNodeOperatorExitedKeysCount
uint256
currentNodeOperatorStuckKeysCount
uint256
newModuleExitedKeysCount
uint256
newNodeOperatorExitedKeysCount
uint256
newNodeOperatorStuckKeysCount
uint256
Roles
DEFAULT_ADMIN_ROLE
MANAGE_WITHDRAWAL_CREDENTIALS_ROLE
STAKING_MODULE_PAUSE_ROLE
STAKING_MODULE_RESUME_ROLE
STAKING_MODULE_MANAGE_ROLE
REPORT_EXITED_KEYS_ROLE
UNSAFE_SET_EXITED_KEYS_ROLE
REPORT_REWARDS_MINTED_ROLE
Constants
FEE_PRECISION_POINTS
uint256
public
TOTAL_BASIS_POINTS
uint256
public
Constructor
Stores
_depositContract
as the address to which deposits will be submitted and sets the contract version in the implementation contract to the maximumuint256
value.Branches:
_depositContract
is zero address.Events:
event ContractVersionSet(uint256 version)
Parameters
_depositContract
address
DepositContract
addressFunction:
initialize
Sets the initial state variables:
1
._admin
as the default admin who can grant and revoke roles;_lido
as the address ofLido
, the core protocol contract;_withdrawalCredentials
as the protocol-wide withdrawal credentials used for deposits.Branches:
_admin
is zero address;_admin
already is the default admin;_lido
is zero address;0
.Events:
event ContractVersionSet(uint256 version)
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)
event WithdrawalCredentialsSet(bytes32 withdrawalCredentials, address setBy)
Parameters
_admin
address
_lido
address
Lido
contract address_withdrawalCredentials
bytes32
Function:
getLido
Returns the address of the
Lido
contract.Function:
addStakingModule
Registers a new staking module.
Branches:
STAKING_MODULE_MANAGE_ROLE
role;_targetShare
is greater than 10,000, i.e. 100%;_stakingModuleFee
and_treasuryFee
is greater than 10,000, i.e. 100%;0
.Events:
event StakingModuleAdded(uint24 indexed stakingModuleId, address stakingModule, string name, address createdBy)
event StakingModuleTargetShareSet(uint24 indexed stakingModuleId, uint16 targetShare, address setBy)
event StakingModuleFeesSet(uint24 indexed stakingModuleId, uint16 stakingModuleFee, uint16 treasuryFee, address setBy)
Parameters
_name
string
_stakingModuleAddress
address
_targetShare
uint16
_stakingModuleFee
uint16
_treasuryFee
uint16
Function:
updateStakingModule
Changes the module's target share, fee and treasury fee.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;STAKING_MODULE_MANAGE_ROLE
role;_targetShare
is greater than 10,000, i.e. 100%;_stakingModuleFee
and_treasuryFee
is greater than 10,000, i.e. 100%.Events:
event StakingModuleTargetShareSet(uint24 indexed stakingModuleId, uint16 targetShare, address setBy)
event StakingModuleFeesSet(uint24 indexed stakingModuleId, uint16 stakingModuleFee, uint16 treasuryFee, address setBy)
Parameters
_name
string
_stakingModuleId
address
_targetShare
uint16
_stakingModuleFee
uint16
_treasuryFee
uint16
Function:
reportRewardsMinted
Calls the reward handler function exposed on the modules passing the shares minted.
Branches:
_stakingModuleIds
contains an id greater thanUINT24_MAX
or0
;REPORT_REWARDS_MINTED_ROLE
role.Parameters
_stakingModuleIds
uint256[]
_totalShares
uint256[]
Function:
updateExitedKeysCountByStakingModule
Updates the exited keys count for multiple modules.
Branches:
_stakingModuleIds
contains an id greater thanUINT24_MAX
or0
;REPORT_EXITED_KEYS_ROLE
role;StakingRouter
;StakingModuleExitedKeysIncompleteReporting
if there is a discrepancy between what was reported toStakingRouter
and retrieved from the module directly using the module'sgetValidatorsKeysStats()
.Events:
event StakingModuleExitedKeysIncompleteReporting(uint24 indexed stakingModuleId, uint256 unreportedExitedKeysCount);
Parameters
_stakingModuleIds
uint256[]
_exitedKeysCounts
uint256[]
Function:
reportStakingModuleExitedKeysCountByNodeOperator
Updates the exited keys count for node operators in a module.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
or0
;REPORT_EXITED_KEYS_ROLE
role;Parameters
_stakingModuleId
uint256
_nodeOperatorIds
uint256[]
_exitedKeysCounts
uint256[]
Function:
unsafeSetExitedKeysCount
Updates the exited keys count for a node operator without security checks; should only be used by the DAO in extreme cases and with sufficient precautions to correct invalid data reported by the oracle committee due to a bug in the oracle daemon.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
or0
;REPORT_EXITED_KEYS_ROLE
role;Parameters
_stakingModuleId
uint256
_nodeOperatorId
uint256
_exitedKeysCount
uint256
Function:
getExitedKeysCountAcrossAllModules
Returns the sum of exited keys across all modules.
Function:
getStakingModules
Returns an array of registered module structs.
Function:
getStakingModuleIds
Returns an array of the ids of all registered staking modules.
Function:
getStakingModule
Returns the staking module struct with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
._stakingModuleId
uint256
Function:
getStakingModulesCount
Returns the number of registered staking modules.
Function:
getStakingModuleStatus
Returns the status of the staking module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
._stakingModuleId
uint256
Function:
setStakingModuleStatus
Update the status of the staking module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;STAKING_MODULE_MANAGE_ROLE
role;Events:
event StakingModuleStatusSet(uint24 indexed stakingModuleId, StakingModuleStatus status, address setBy)
Parameters
_stakingModuleId
uint256
_status
StakingModuleStatus
Function:
pauseStakingModule
Blocks deposits from the module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;STAKING_MODULE_PAUSE_ROLE
role;Events:
event StakingModuleStatusSet(uint24 indexed stakingModuleId, StakingModuleStatus status, address setBy)
Parameters
_stakingModuleId
uint256
Function:
resumeStakingModule
Lifts the block on deposits from the module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;STAKING_MODULE_RESUME_ROLE
role;Events:
event StakingModuleStatusSet(uint24 indexed stakingModuleId, StakingModuleStatus status, address setBy)
Parameters
_stakingModuleId
uint256
Function:
getStakingModuleIsStopped
Returns a boolean value indicating whether the module with the specified id is forbidden from performing deposits and does not receive rewards.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleIsDepositsPaused
Returns a boolean value indicating whether deposits from the module with the specified id are blocked.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleIsActive
Returns a boolean value indicating whether the module with the specified id can perform deposits and receive rewards.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleKeysOpIndex
Returns the nonce of key operations in the module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleLastDepositBlock
Returns the block number of the latest deposit from the module with the specified id.
Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleActiveKeysCount
Returns the number of keys deposited to
DepositContract
from the module with the specified id.Branches:
_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_stakingModuleId
uint256
Function:
getStakingModuleMaxDepositableKeys
Returns the number of keys currently depositable from the module with the specified id based on the current depositable buffer size.
Parameters
_stakingModuleIndex
uint256
Function:
getStakingFeeAggregateDistribution
Returns the aggregate fee distribution proportion.
Function:
getStakingRewardsDistribution
Returns:
recipients
: an array of rewarded staking module addresses;stakingModuleFees
: an array of module rewards, a percentage out of total rewards earned in a given round of distribution, in basis points;totalFee
: the protocol fee cut out of total rewards earned in a given round of distribution taken by the protocol, i.e. treasury fee and module rewards, in basis points;precisionPoints
: the precision factor used for calculation equivalent to 100% with a precision up to 18 decimals.Branches:
Function:
getKeysAllocation
Returns:
allocated
: the total number of keys across all modules that can be deposited at the given time;allocations
: an array of sums of active and depositable keys for each module.Branches:
_keysToAllocate
uint256
Function:
deposit
Performs a batch deposit to
DepositContract
using the keys from the module with the specified id and returns the number of keys deposited.Branches:
_stakingModuleId
is greater thanUINT24_MAX
;msg.sender
is not theLido
contract;msg.value
is 0 and transfers all ether on the balance to theLido
contract;_maxDepositsCount
or the module's depositable keys is less than or equals 0 and transfers all controlled ether to theLido
contract;Lido
contract;Events:
event StakingRouterETHDeposited(uint24 indexed stakingModuleId, uint256 amount);
Parameters
_maxDepositsCount
uint256
_stakingModuleId
uint256
_depositCalldata
bytes
Function:
setWithdrawalCredentials
Set credentials to withdraw ETH on Consensus Layer side after the phase 2 is launched to
_withdrawalCredentials
.Branches:
MANAGE_WITHDRAWAL_CREDENTIALS_ROLE
;Parameters
_withdrawalCredentials
uint256
Events:
event WithdrawalCredentialsSet(bytes32 withdrawalCredentials, address setBy);
Function:
getWithdrawalCredentials
Returns current credentials to withdraw ETH on Consensus Layer side after the phase 2 is launched.
IStakingModule.sol
The staking module interface required for
StakingRouter
.Function:
getType
Returns the type of the staking module; required for off-chain support.
Function:
getValidatorsKeysStats
Returns:
exitedValidatorsCount
: the number of exited validators for the entire module;activeValidatorsKeysCount
: the number of active validators for the entire module;readyToDepositValidatorsKeysCount
: the number of validators keys ready for deposit for the entire module.Function:
getValidatorsKeysStats
Returns:
exitedValidatorsCount
: the number of exited validators for the specified module;activeValidatorsKeysCount
: the number of active validators for the specified module;readyToDepositValidatorsKeysCount
: the number of validators keys ready for deposit for the specified module.Parameters
_nodeOperatorId
uint256
Function:
getValidatorsKeysNonce
Returns a monotonical counter incremented when:
Function:
getNodeOperatorsCount
Returns the total number of node operators in the module.
Function:
getActiveNodeOperatorsCount
Returns the number of active node operators in the module.
Function:
getNodeOperatorIsActive
Returns the boolean indicating whether the specified node operator is active.
Parameters
_nodeOperatorId
uint256
Function:
handleRewardsMinted
Called by
StakingRouter
to signal that stETH rewards were minted for this module.Parameters
_totalShares
uint256
Function:
updateStuckValidatorsKeysCount
Updates the number of the validators of the specified node operator that were requested to exit but failed to do so in the max allowed time.
Parameters
_nodeOperatorId
uint256
_stuckValidatorKeysCount
uint256
Function:
updateExitedValidatorsKeysCount
Updates the number of the validators in the EXITED state for the specified node operator.
Parameters
_nodeOperatorId
uint256
_exitedValidatorKeysCount
uint256
Function:
getNodeOperatorsCount
Called by StakingRouter after oracle finishes updating exited keys counts for all operators.
Function:
invalidateReadyToDepositKeys
Invalidates all unused validators keys for all node operators.
Function:
requestValidatorsKeysForDeposits
Requests the given number of the validator keys from the staking module.
Parameters
_keysCount
uint256
_calldata
bytes
Lido.sol
Much of the logic related to deposits and stake distribution was moved to
StakingRouter
. In this specification we will highlight these changes and omit anything that will remain unchanged.Function removed:
depositBufferedEther
Note: each module has own
deposit
functionFunction removed:
depositBufferedEther
Note: each module has own
deposit
functionFunction removed:
getDepositContract
**Note: Moved to
StakingRouter.getDepositContract()
Function removed:
getWithdrawalCredentials
**Note: Moved to
StakingRouter.getWithdrawalCredentials()
Function removed:
setWithdrawalCredentials
**Note: Moved to
StakingRouter.setWithdrawalCredentials()
Function:
deposit
Invokes a deposit call to the
StakingRouter
contract and updates buffered counters.Branches:
DepositSecurityModule
;_stakingModuleId
is greater thanUINT24_MAX
;Parameters
_maxDepositsCount
uint256
_stakingModuleId
uint256
_depositCalldata
bytes32
NodeOperatorRegistry.sol
(WIP)All the change in
NodeOperatorsRegistry
relate to the introduction ofStakingRouter
and theIStakingModule
specification requirements. In this specification we will highlight these changes and omit anything that will remain unchanged.Constants
Constant:
TOTAL_BASIS_POINTS
Total Basis points
Constant:
FEE_POSITION
Module fee
Constant:
TOTAL_KEYS_POSITION
Constant:
TOTAL_USED_KEYS_POSITION
Constant:
TOTAL_STOPPED_KEYS_POSITION
Constant:
TOTAL_EXITED_KEYS_POSITION
Constant:
CONTRACT_VERSION_POSITION
Contract version
Functions
Function:
getVersion()
Get contract version
Function:
finalizeUpgrade_v2()
Function:
deposit()
Call
deposit()
function on StakingRouter_numKeys
is not equal to actual on StakingRouterFunction:
distributeRewards()
Distribute rewards to node operators
Function:
getKeysStat
Returns structure including information about keys
Function:
getTotalKeys
Function:
getTotalUsedKeys
Function:
getTotalStoppedKeys
Function:
getTotalExitedKeys
Security Considerations
Upgradability
The contract is deployed as an implementation for the upgradable ossifiable proxy.
Proxy admin MUST be set to the
Lido DAO Agent
address.Ownership
Only
Lido DAO Agent
is allowed to perform administrative actions (pause/resume withdrawals requests placement).Reference implementation
Lido Shapella Upgrade
Links