Skip to main content

StakingRouter

StakingRouter is a registry of staking modules, each encapsulating a certain validator subset, e.g. curated staking module, community staking module. The contract allocates stake to modules, executes validator deposits and top-ups with ether pulled from Lido, distributes protocol fees, and tracks per-module validator balances.

What is StakingRouter?​

StakingRouter is a top-level controller contract for staking modules. Each staking module is a contract that, in turn, manages its own subset of validators, e.g. the Curated staking module is a set of Lido DAO-vetted node operators. Such modular design opens the opportunity for anyone to build a staking module and join the Lido staking platform, including permissionless community stakers, DVT-enabled validators or any other validator subset, technology or mechanics.

StakingRouter performs a number of functions, including:

  • maintaining a registry of staking modules,
  • allocating stake to modules,
  • executing deposits and validator top-ups with ether pulled from Lido,
  • tracking per-module validator balances and exited validators, and
  • distributing protocol fees.

Module Management​

Registering a module​

Modules are registered with StakingRouter through the Lido DAO voting process. To be considered by the governance, the applying module contract should implement the appropriate module interface, meet security requirements, and have a fee structure aligned with the Lido protocol sustainability. Once voted in, the module starts receiving stake and protocol fees.

Staking modules are registered using the addStakingModule function, providing a human-readable module name, the address of the deployed staking module contract, and a StakingModuleConfig struct with the module parameters:

  • stake share limit, a relative hard cap on deposits within Lido;
  • priority exit share threshold, the module's share upon crossing which validator exits from the module are prioritized;
  • module fee, a percentage of staking rewards to be awarded to the module;
  • treasury fee, a percentage of staking rewards to be directed to the protocol treasury;
  • maximum deposits per block and minimum deposit block distance, deposit rate limits;
  • withdrawal credentials type, either 0x01 or 0x02.

Withdrawal credential types​

Each staking module is permanently configured with a withdrawal credentials type:

  • 0x01 β€” modules whose validators have a maximum effective balance of 32 ETH (MAX_EFFECTIVE_BALANCE_WC_TYPE_01);
  • 0x02 β€” modules whose validators use EIP-7251 compounding withdrawal credentials with a maximum effective balance of 2048 ETH (MAX_EFFECTIVE_BALANCE_WC_TYPE_02) and support validator top-ups.

The router stores a single protocol-wide withdrawal credentials value that contains the withdrawal address. The effective withdrawal credentials of a particular module are derived by applying the module's type prefix (0x01 or 0x02) to that value and can be read via getStakingModuleWithdrawalCredentials.

Pausing modules​

Each staking module has a status: a state that determines whether the module can perform deposits and receive rewards:

  • Active, can make deposits and receives rewards,
  • DepositsPaused, deposits are not allowed but receives rewards,
  • Stopped, cannot make deposits and does not receive rewards.
enum StakingModuleStatus {
Active, // deposits and rewards allowed
DepositsPaused, // deposits NOT allowed, rewards allowed
Stopped // deposits and rewards NOT allowed
}

Exited validators​

When the withdrawal requests demand exceed buffered ether sitting in Lido together with projected rewards, the protocol signals to node operators to start exiting validators to cover the withdrawals. In this connection, StakingRouter distinguishes two types of validator states:

  • exited validators,
  • and late validators, meaning those validators which were requested to exit that failed to exit in a specified timeframe.

The StakingRouter tracks exited validators for correct stake allocation and notifies Staking Modules about late validators, enabling penalization actions if needed.

Stake allocation​

StakingRouter carries out a vital task of distributing depositable ether to staking modules in a manner that aligns with their growth targets set by the DAO. This design ensures a regulated and controlled growth for the modules that have been newly integrated into the system. The principles governing this methodology are comprehensively discussed in ADR: Staking Router.

Deposit​

The deposit workflow involves submitting batches of 32 ether deposits, along with associated validator keys, to DepositContract in one transaction. Given that each staking module handles its own deposits, every batch deposit is restricted to keys originating from a single module.

The deposit operation is, at its core, a sequence of contract calls sparked by an off-chain software, the depositor bot. This bot gathers guardian messages to confirm that there are no pre-existing keys in the registry that could take advantage of the frontrunning vulnerability. Once the necessary quorum of guardians is reached, the bot forwards these messages along with the module identifier to the DepositSecurityModule (not to be confused with a staking module). This contract verifies the messages and calls the deposit function on StakingRouter.

Deposits follow the pull model: buffered ether stays on the Lido contract until the moment of the deposit, and StakingRouter pulls exactly the amount it is about to deposit. The deposit function:

  1. verifies that the caller is the DepositSecurityModule and that the staking module is active;
  2. determines how much of Lido's depositable ether (Lido.getDepositableEther()) can be allocated to the module according to the allocation algorithm, and derives the maximum number of deposits as the allocation divided by 32 ether, additionally capped by the module's maxDepositsPerBlock;
  3. obtains deposit data (public keys and signatures) from the staking module contract via obtainDepositData; the module may return fewer keys than requested;
  4. records the current timestamp and block number as the module's last deposit state (this happens even if the module returned zero keys);
  5. pulls number of obtained keys Γ— 32 ether from Lido via Lido.withdrawDepositableEther(), which sends the ether back to the router's receiveDepositableEther within the same call;
  6. performs a 32 ether deposit to DepositContract for each key, using the module's withdrawal credentials (the module's 0x01 or 0x02 type prefix applied to the protocol-wide withdrawal credentials);
  7. confirms that all pulled ether has been deposited by comparing the contract's ether balance before and after the deposits.

Direct ether transfers to StakingRouter are rejected: the receive() function reverts with the DirectETHTransfer error. Ether can enter the contract only through receiveDepositableEther as part of a deposit or top-up transaction.

Top-ups​

Validators of 0x02 modules can hold an effective balance of up to 2048 ETH. Beyond the initial 32 ether deposit that activates a validator, additional stake reaches these validators via top-ups: the depositor bot calls TopUpGateway, which verifies each validator's Consensus Layer state with Merkle proofs, computes per-key top-up limits, and calls the router's topUp function.

The topUp function:

  1. verifies that the caller is the TopUpGateway, that the staking module is active, and that its withdrawal credentials type is 0x02;
  2. determines the module's top-up allocation according to the allocation algorithm, capped by the global per-block top-up limit (getMaxTopUpPerBlockGwei) and rounded down to a whole gwei;
  3. calls allocateDeposits on the staking module, which validates that the supplied keys belong to the module and returns the exact deposit amount for each key; each amount must be gwei-aligned and within its per-key limit, and the total must not exceed the module allocation;
  4. pulls the total amount from Lido via Lido.withdrawDepositableEther();
  5. executes a top-up deposit to DepositContract for each key with a non-zero amount;
  6. confirms that all pulled ether has been deposited by comparing the contract's ether balance before and after the top-ups.

If the module allocation rounds down to zero, the module is still called with a zero amount so that it can advance its internal deposit queue β€” but only if Lido deposits are enabled; otherwise the call reverts with LidoDepositsPaused.

Allocation algorithm​

StakingRouter distributes incoming ether across staking modules using the MinFirstAllocationStrategy algorithm: modules with proportionally less stake receive deposits first, gradually equalizing their sizes over time. The strategy operates in 32 ETH units (MAX_EFFECTIVE_BALANCE_WC_TYPE_01), so the amount allocated to each module is always a multiple of 32 ETH. The allocation is exposed via the getDepositAllocations view and is computed as follows:

  1. The ether amount to allocate is converted into 32 ETH units (depositsToAllocate).

  2. The current allocation of each module is calculated in the same units:

    • for 0x01 modules, it is derived from on-chain accounting as the number of active validators (each holding 32 ETH): currentAllocation = depositedValidators - max(moduleReportedExited, stakingRouterTrackedExited);
    • for 0x02 modules, the router queries the module directly via getTotalModuleStake() to obtain the actual total ether staked in the module and converts it into 32 ETH units, rounded up: currentAllocation = ceil(getTotalModuleStake() / 32 ETH).
  3. A capacities array is built, where each entry represents the maximum allocation a module can reach. For an active module, the capacity is the minimum of two constraints β€” the module's stake share limit and its available room:

    • for initial (seed) deposits: capacity = min(stakeShareLimit * totalUnits / TOTAL_BASIS_POINTS, currentAllocation + depositableValidatorsCount), where totalUnits is the sum of all current allocations plus depositsToAllocate;
    • for top-up allocations, a 0x02 module's capacity is measured by the effective balance headroom of its active validators rather than by available keys: capacity = min(stakeShareLimit * totalUnits / TOTAL_BASIS_POINTS, activeValidatorsCount * maxEBType2 / maxEBType1); 0x01 modules keep their seed-deposit capacity to preserve the priority ordering between modules, but the top-up amounts computed for them are never used since 0x01 modules cannot receive top-ups.

    A module that is not in the Active status has its capacity set to the current allocation and receives nothing.

  4. MinFirstAllocationStrategy.allocate is called with the current allocations, the capacities, and depositsToAllocate. It fills the modules with the least allocation first, until either the whole amount is distributed or all modules reach their capacity. The results are converted back to ether amounts.

Fee distribution​

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. The sum of the module fee and the treasury fee must be equal across all registered modules; this invariant is enforced on every fee update. Additionally,Β StakingRouterΒ utilizes a precision factor of 100 * 10^18Β for fees that prevents arithmetic operations from truncating the fees of small modules.

The protocol fee is distributed between modules proportionally to their validator balances and the specified module fee:

moduleShare = moduleValidatorsBalance / totalModulesValidatorsBalance

where moduleValidatorsBalance is the module's CL validators balance (excluding pending deposits) reported by the AccountingOracle and stored on the router (see Validator balance accounting). For example, a module holding 75% of the total validator balance in the protocol and having a 5% module fee will receive 3.75% of the total rewards across the protocol. A module with a single 2048 ETH validator receives the share of rewards corresponding to its contribution to the total stake, regardless of its validator count. 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.

The distribution function itself works as follows:

  1. The function reads the total validator balance across all registered modules. If there are no staking modules or the total balance is zero, it returns an empty response.

  2. Otherwise, it initializes arrays to store the module IDs (stakingModuleIds), the addresses of reward recipients (recipients), and the fees of each recipient (stakingModuleFees). It also sets the precisionPoints to a constant FEE_PRECISION_POINTS, which represents the base precision number that constitutes 100% fee.

  3. Then it loops through each staking module, skipping modules with a zero validator balance. For each remaining module, it:

    • Stores the module ID and recipient address in the respective arrays.
    • Calculates the module's share as its validator balance divided by the total validator balance across all modules (scaled by FEE_PRECISION_POINTS).
    • Calculates the stakingModuleFee as the product of the module's share and the fee of the staking module divided by TOTAL_BASIS_POINTS. If the module is not stopped, this fee is stored in the stakingModuleFees array.
    • Adds to totalFee the sum of the staking module's fee and a fee going to the treasury (calculated similarly to stakingModuleFee), where the treasury is a central pool of funds.
  4. After looping through all modules, it makes an assertion that totalFee doesn't exceed 100% (represented by precisionPoints).

  5. If there are staking modules with a zero validator balance, it shrinks the stakingModuleIds, recipients, and stakingModuleFees arrays to exclude those modules.

Finally, the function returns five arrays/values: recipients, stakingModuleIds, stakingModuleFees, totalFee, and precisionPoints. These give the caller an overview of how rewards are distributed amongst the staking modules.

Validator balance accounting​

StakingRouter tracks the total balance of active validators for each module (validatorsBalanceGwei) and the aggregate balance across all modules. These balances are the basis for fee distribution.

The balances are delivered by the AccountingOracle as part of the main report phase via reportValidatorBalancesByStakingModule. The report must include all registered staking modules in their registration order, with each value being the sum of the module's validator balances (excluding pending deposits), nominated in gwei. The view counterpart validateReportValidatorBalancesByStakingModule allows pre-validating a report against the current module set without mutating state.

The stored balances are readable via getModuleValidatorsBalance, getTotalModulesValidatorsBalance, and getStakingModuleStateAccounting.

View methods​

getStakingModules​

Returns the list of structs of all registered staking modules. Each staking module has an associated data structure,

struct StakingModule {
uint24 id;
address stakingModuleAddress;
uint16 stakingModuleFee;
uint16 treasuryFee;
uint16 stakeShareLimit;
uint8 status;
string name;
uint64 lastDepositAt;
uint256 lastDepositBlock;
uint256 exitedValidatorsCount;
uint16 priorityExitShareThreshold;
uint64 maxDepositsPerBlock;
uint64 minDepositBlockDistance;
uint8 withdrawalCredentialsType;
uint64 validatorsBalanceGwei;
}
function getStakingModules() external view returns (StakingModule[] memory res);

Returns:

NameTypeDescription
resStakingModule[]list of structs of all registered staking modules

getStakingModuleIds​

Returns the list of ids of all registered staking modules.

function getStakingModuleIds() external view returns (uint256[] memory stakingModuleIds);

Returns:

NameTypeDescription
stakingModuleIdsuint256[]list of id of all staking modules

getStakingModule​

Returns the struct of the specified staking module by its id.

function getStakingModule(uint256 _stakingModuleId) external view returns (StakingModule memory);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
StakingModulestaking module information

getStakingModulesCount​

Returns the number of registered staking modules.

function getStakingModulesCount() external view returns (uint256);

hasStakingModule​

Return a boolean value indicating whether a staking module with the specified id is registered.

function hasStakingModule(uint256 _stakingModuleId) public view returns (bool);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

getStakingModuleStatus​

Return the status of the staking module.

function getStakingModuleStatus(uint256 _stakingModuleId) public view returns (StakingModuleStatus);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
StakingModuleStatusstatus of the staking module

getStakingModuleStateConfig​

Returns the configuration part of the staking module state: the module address, fees, share limits, status, and withdrawal credentials type.

struct ModuleStateConfig {
address moduleAddress;
uint16 moduleFee;
uint16 treasuryFee;
uint16 stakeShareLimit;
uint16 priorityExitShareThreshold;
StakingModuleStatus status;
uint8 withdrawalCredentialsType;
}
function getStakingModuleStateConfig(uint256 _stakingModuleId) external view returns (ModuleStateConfig memory stateConfig);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
stateConfigModuleStateConfigconfiguration part of the module state

getStakingModuleStateDeposits​

Returns the deposit-related part of the staking module state: the last deposit timestamp and block, the maximum number of deposits per block, and the minimum deposit block distance.

struct ModuleStateDeposits {
uint64 lastDepositAt;
uint64 lastDepositBlock;
uint64 maxDepositsPerBlock;
uint64 minDepositBlockDistance;
}
function getStakingModuleStateDeposits(uint256 _stakingModuleId) external view returns (ModuleStateDeposits memory stateDeposits);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
stateDepositsModuleStateDepositsdeposit-related part of the module state

getStakingModuleStateAccounting​

Returns the accounting part of the staking module state: the total balance of the module's active validators (in gwei) and the total exited validators count tracked by StakingRouter for the module.

function getStakingModuleStateAccounting(uint256 _stakingModuleId) external view returns (uint64 validatorsBalanceGwei, uint64 exitedValidatorsCount);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
validatorsBalanceGweiuint64total balance of the module's active validators, in gwei
exitedValidatorsCountuint64total exited validators count tracked by the router for the module

getStakingModuleSummary​

Returns the struct containing a short summary of validators in the specified staking module, as shown below,

struct StakingModuleSummary {
uint256 totalExitedValidators;
uint256 totalDepositedValidators;
uint256 depositableValidatorsCount;
}
function getStakingModuleSummary(uint256 _stakingModuleId) external view returns (StakingModuleSummary memory summary);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
StakingModuleSummarysummary of the staking module's validators

getNodeOperatorSummary​

Returns the summary of a node operator from the staking module, as shown below,

struct NodeOperatorSummary {
uint256 targetLimitMode;
uint256 targetValidatorsCount;
uint256 stuckValidatorsCount; // DEPRECATED: always zero, kept for backward compatibility
uint256 refundedValidatorsCount; // DEPRECATED: always zero, kept for backward compatibility
uint256 stuckPenaltyEndTimestamp; // DEPRECATED: always zero, kept for backward compatibility
uint256 totalExitedValidators;
uint256 totalDepositedValidators;
uint256 depositableValidatorsCount;
}
function getNodeOperatorSummary(
uint256 _stakingModuleId,
uint256 _nodeOperatorId
) external view returns (NodeOperatorSummary memory summary);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_nodeOperatorIduint256node operator id

Returns:

NameTypeDescription
NodeOperatorSummarysummary of the node operator

getAllStakingModuleDigests​

Returns the digests of all staking modules, as show below,

struct StakingModuleDigest {
uint256 nodeOperatorsCount;
uint256 activeNodeOperatorsCount;
StakingModule state;
StakingModuleSummary summary;
}
function getAllStakingModuleDigests() external view returns (StakingModuleDigest[]);

Returns:

NameTypeDescription
StakingModuleDigest[]array of staking module digests

getStakingModuleDigests​

Returns the digest of the specified staking modules.

function getStakingModuleDigests(uint256[] memory _stakingModuleIds) public view returns (StakingModuleDigest[]);

Parameters:

NameTypeDescription
_stakingModuleIdsuint256[]array of staking module ids

Returns:

NameTypeDescription
StakingModuleDigest[]array of staking module digests

getAllNodeOperatorDigests​

Returns the digests of all node operators in the specified staking module,

struct NodeOperatorDigest {
uint256 id;
bool isActive;
NodeOperatorSummary summary;
}
function getAllNodeOperatorDigests(uint256 _stakingModuleId) external view returns (NodeOperatorDigest[]);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
NodeOperatorDigest[]array of node operator digests

getNodeOperatorDigests​

Returns the digests for the specified node operators in the staking module.

function getNodeOperatorDigests(uint256 _stakingModuleId, uint256[] memory _nodeOperatorIds) public view returns (NodeOperatorDigest[]);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_nodeOperatorIdsuint256[]array of node operator ids

Returns:

NameTypeDescription
NodeOperatorDigest[]array of node operator digests

getStakingModuleIsStopped​

Return a boolean value whether the staking module is stopped.

function getStakingModuleIsStopped(uint256 _stakingModuleId) external view returns (bool);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
booltrue if the staking module is stopped, false otherwise

getStakingModuleIsDepositsPaused​

Return a boolean value whether deposits are paused for the staking module.

function getStakingModuleIsDepositsPaused(uint256 _stakingModuleId) external view returns (bool);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
booltrue if deposits are paused for the staking module, false otherwise

getStakingModuleIsActive​

Return a boolean value whether the staking module is active.

function getStakingModuleIsActive(uint256 _stakingModuleId) external view returns (bool);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
booltrue if the staking module is active, false otherwise

getStakingModuleNonce​

Get the nonce of a staking module.

function getStakingModuleNonce(uint256 _stakingModuleId) external view returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
uint256nonce of the staking module

getStakingModuleLastDepositBlock​

Get the block number of the last deposit to the staking module.

function getStakingModuleLastDepositBlock(uint256 _stakingModuleId) external view returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
uint256block number of the last deposit

getStakingModuleMinDepositBlockDistance​

Get the min deposit block distance for the staking module

function getStakingModuleMinDepositBlockDistance(uint256 _stakingModuleId) external view returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
uint256min deposit block distance for the staking module

getStakingModuleMaxDepositsPerBlock​

Get the max deposits count per block for the staking module

function getStakingModuleMaxDepositsPerBlock(uint256 _stakingModuleId) external view returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
uint256Max deposits count per block for the staking module

getStakingModuleActiveValidatorsCount​

Returns the number of active validators in the staking module.

function getStakingModuleActiveValidatorsCount(uint256 _stakingModuleId) external view returns (uint256 activeValidatorsCount);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
uint256number of active validators

getModuleValidatorsBalance​

Returns the total balance of the staking module's active validators used for fee distribution, in wei. See Validator balance accounting.

function getModuleValidatorsBalance(uint256 moduleId) external view returns (uint256);

Parameters:

NameTypeDescription
moduleIduint256staking module id

Returns:

NameTypeDescription
uint256total balance of the module's active validators, in wei

getTotalModulesValidatorsBalance​

Returns the sum of active validators balances across all registered staking modules, in wei.

function getTotalModulesValidatorsBalance() external view returns (uint256);

Returns:

NameTypeDescription
uint256total balance of validators across all modules, in wei

getStakingModuleMaxDepositsCount​

Calculates the maximum number of deposits a staking module can handle based on the available deposit value: the module's allocation for that amount divided by 32 ether.

function getStakingModuleMaxDepositsCount(
uint256 _stakingModuleId,
uint256 _maxDepositsValue
) public view returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_maxDepositsValueuint256maximum amount of deposits based on the available ether

Returns:

NameTypeDescription
uint256maximum number of deposits that can be made using the given staking module

getStakingFeeAggregateDistribution​

Returns the total fee distribution proportion.

function getStakingFeeAggregateDistribution() public view returns (
uint96 modulesFee,
uint96 treasuryFee,
uint256 basePrecision
);

Returns:

NameTypeDescription
modulesFeeuint96total fees for all staking modules
treasuryFeeuint96total fee for the treasury
basePrecisionuint256base precision number, a value corresponding to the full fee

getStakingRewardsDistribution​

Get the shares table.

function getStakingRewardsDistribution() public view returns (
address[] memory recipients,
uint256[] memory stakingModuleIds,
uint96[] memory stakingModuleFees,
uint96 totalFee,
uint256 precisionPoints
);

Returns:

NameTypeDescription
recipientsaddress[]total staking module addresses
stakingModuleIdsuint256[]staking module ids
stakingModuleFeesuint96[]staking module fees
totalFeeuint96total fee
precisionPointsuint256base precision number, a value corresponding to the full fee

getDepositAllocations​

Calculates the ether allocation between staking modules after the distribution of the _depositAmount amount using the allocation algorithm. With _isTopUp set to true, calculates the top-up allocation; with false, the initial-deposit allocation.

function getDepositAllocations(uint256 _depositAmount, bool _isTopUp) public view returns (
uint256 totalAllocated, uint256[] memory allocated, uint256[] memory newAllocations
);

Parameters:

NameTypeDescription
_depositAmountuint256maximum ether amount of deposits to be allocated between staking modules
_isTopUpboolwhether the allocation is requested for top-ups (true) or initial deposits (false)

Returns:

NameTypeDescription
totalAllocateduint256ether amount actually allocated
allocateduint256[]array of newly allocated ether amounts per module
newAllocationsuint256[]array of resulting allocation amounts per module

getWithdrawalCredentials​

Get the protocol-wide withdrawal credentials containing the withdrawal address. The per-module credentials are derived from this value by applying the module's type prefix; see getStakingModuleWithdrawalCredentials.

function getWithdrawalCredentials() public view returns (bytes32);

Returns:

NameTypeDescription
bytes32withdrawal credentials

getStakingModuleWithdrawalCredentials​

Get the withdrawal credentials of the staking module: the protocol-wide withdrawal credentials with the module's type prefix applied β€” 0x01... for 0x01 modules, 0x02... for 0x02 modules.

function getStakingModuleWithdrawalCredentials(uint256 _stakingModuleId) external view returns (bytes32);

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id

Returns:

NameTypeDescription
bytes32withdrawal credentials of the staking module

getMaxTopUpPerBlockGwei​

Returns the global per-block limit for validator top-ups, in gwei.

function getMaxTopUpPerBlockGwei() external view returns (uint64);

Returns:

NameTypeDescription
uint64per-block top-up limit, in gwei

validateReportValidatorBalancesByStakingModule​

Validates a validator balances report against the current staking module set: the arrays must cover all registered staking modules in their registration order, and each balance value must not exceed the sanity limit. The view counterpart of reportValidatorBalancesByStakingModule used to pre-validate report data without mutating state.

function validateReportValidatorBalancesByStakingModule(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _validatorBalancesGwei
) external view;

Parameters:

NameTypeDescription
_stakingModuleIdsuint256[]ids of all registered staking modules in their registration order
_validatorBalancesGweiuint256[]validator balances for the specified staking modules, in gwei

getContractVersion​

Returns the current initialized version of the contract.

function getContractVersion() external view returns (uint256);

Returns:

NameTypeDescription
uint256current initialized version of the contract

Write methods​

deposit​

Invokes a batch of 32 ether deposit calls to the official DepositContract using the keys of the specified staking module. The router calculates the module allocation, obtains deposit data from the module, pulls the required ether from Lido via Lido.withdrawDepositableEther(), and performs a 32 ether deposit for each key using the module's withdrawal credentials. See Deposit for the detailed flow.

Can be called only by the DepositSecurityModule contract.

function deposit(uint256 _stakingModuleId, bytes calldata _depositCalldata) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256id of the staking module to deposit to
_depositCalldatabytesstaking module calldata

topUp​

Performs top-up deposits to the official DepositContract for existing validators of a 0x02 staking module. The router determines how much ether can be deposited to the module, obtains the exact per-key amounts from the module via allocateDeposits, pulls the total ether from Lido, and executes the top-ups. See Top-ups for the detailed flow.

Can be called only by the TopUpGateway contract.

function topUp(
uint256 _stakingModuleId,
uint256[] calldata _keyIndices,
uint256[] calldata _operatorIds,
bytes[] calldata _pubkeys,
uint256[] calldata _topUpLimits
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256id of the staking module to deposit to
_keyIndicesuint256[]list of keys' indices
_operatorIdsuint256[]list of node operator ids
_pubkeysbytes[]list of validator public keys to top up
_topUpLimitsuint256[]maximum amount (in wei) that can be deposited per key based on CL data and TopUpGateway logic

receiveDepositableEther​

A payable function for depositable ether acquisition: the single entry point for ether into the contract, invoked within Lido.withdrawDepositableEther() during deposits and top-ups. Any other direct ether transfer to StakingRouter reverts.

Can be called only by the Lido contract.

function receiveDepositableEther() external payable;

addStakingModule​

Register a staking module. Restricted to the STAKING_MODULE_MANAGE_ROLE role.

struct StakingModuleConfig {
uint256 stakeShareLimit;
uint256 priorityExitShareThreshold;
uint256 stakingModuleFee;
uint256 treasuryFee;
uint256 maxDepositsPerBlock;
uint256 minDepositBlockDistance;
uint256 withdrawalCredentialsType;
}
function addStakingModule(
string calldata _name,
address _stakingModuleAddress,
StakingModuleConfig calldata _stakingModuleConfig
) external;

Parameters:

NameTypeDescription
_namestringhuman-readable name of the module
_stakingModuleAddressaddressaddress of the module contract
_stakingModuleConfigStakingModuleConfigstaking module configuration

StakingModuleConfig fields:

NameTypeDescription
stakeShareLimituint256maximum share that can be allocated to a module, in basis points
priorityExitShareThresholduint256module's priority exit share threshold, in basis points
stakingModuleFeeuint256fee of the staking module taken from the staking rewards, in basis points
treasuryFeeuint256treasury fee, in basis points
maxDepositsPerBlockuint256maximum number of validators that can be deposited in a single block
minDepositBlockDistanceuint256minimum distance between deposits in blocks
withdrawalCredentialsTypeuint256withdrawal credentials type of the module (0x01 or 0x02)

updateStakingModule​

Update the parameters of a staking module. The withdrawal credentials type cannot be changed. Restricted to the STAKING_MODULE_MANAGE_ROLE role.

function updateStakingModule(
uint256 _stakingModuleId,
uint256 _stakeShareLimit,
uint256 _priorityExitShareThreshold,
uint256 _stakingModuleFee,
uint256 _treasuryFee,
uint256 _maxDepositsPerBlock,
uint256 _minDepositBlockDistance
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256id of the module
_stakeShareLimituint256maximum share that can be allocated to a module
_priorityExitShareThresholduint256Module's priority exit share threshold
_stakingModuleFeeuint256updated module fee
_treasuryFeeuint256updated module treasury fee
_maxDepositsPerBlockuint256maximum number of validators that can be deposited in a single block
_minDepositBlockDistanceuint256minimum distance between deposits in blocks

updateAllStakingModulesFees​

Updates fees for all staking modules in a single atomic operation. The values must be provided in the module registration order (as returned by getStakingModuleIds()), and the sum staking module fee + treasury fee must be equal across all modules. Restricted to the STAKING_MODULE_MANAGE_ROLE role.

function updateAllStakingModulesFees(
uint256[] calldata _stakingModuleFees,
uint256[] calldata _treasuryFees
) external;

Parameters:

NameTypeDescription
_stakingModuleFeesuint256[]new staking module fee values in the module registration order
_treasuryFeesuint256[]new treasury fee values in the module registration order

updateModuleShares​

Updates the share-related parameters of a staking module. Restricted to the STAKING_MODULE_SHARE_MANAGE_ROLE role, which allows for granular permissions on share management separate from the full module management.

function updateModuleShares(
uint256 _stakingModuleId,
uint16 _stakeShareLimit,
uint16 _priorityExitShareThreshold
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256id of the module
_stakeShareLimituint16new stake share limit value, in basis points
_priorityExitShareThresholduint16new priority exit share threshold, in basis points

setMaxTopUpPerBlockGwei​

Sets the global per-block limit for validator top-ups, in gwei. The value must be greater than zero and fit into uint64. Restricted to the STAKING_MODULE_MANAGE_ROLE role.

function setMaxTopUpPerBlockGwei(uint256 _newValue) external;

Parameters:

NameTypeDescription
_newValueuint256new per-block top-up limit, in gwei

updateTargetValidatorsLimits​

Updates the limit of the validators that can be used for deposit. Restricted to the STAKING_MODULE_MANAGE_ROLE role.

function updateTargetValidatorsLimits(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
uint256 _targetLimitMode,
uint256 _targetLimit
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256id of the module
_nodeOperatorIduint256id of the node operator
_targetLimitModeuint256target limit mode (0 = disabled, 1 = soft, 2 = boosted)
_targetLimituint256target limit validators count of the node operator

reportRewardsMinted​

Reports the minted rewards to the staking modules with the specified ids. Restricted to the REPORT_REWARDS_MINTED_ROLE role.

function reportRewardsMinted(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _totalShares
) external;

Parameters:

NameTypeDescription
_stakingModuleIdsuint256[]list of the reported staking module ids
_totalSharesuint256[]total shares minted to the given staking modules

updateExitedValidatorsCountByStakingModule​

Update total numbers of exited validators for staking modules with the specified module ids. Called by the AccountingOracle; restricted to the REPORT_EXITED_VALIDATORS_ROLE role.

function updateExitedValidatorsCountByStakingModule(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _exitedValidatorsCounts
) external returns (uint256);

Parameters:

NameTypeDescription
_stakingModuleIdsuint256[]list of the reported staking module ids
_exitedValidatorsCountsuint256[]new counts of exited validators for the specified staking modules

Returns:

NameTypeDescription
uint256total increase in the aggregate number of exited validators across the updated modules

reportValidatorBalancesByStakingModule​

Stores per-module validator balances used for fee distribution; see Validator balance accounting. The report must include all registered staking modules in their registration order; each value is the sum of the module's validator balances (excluding pending deposits), nominated in gwei. Called by the AccountingOracle as part of the main report phase; restricted to the REPORT_EXITED_VALIDATORS_ROLE role.

function reportValidatorBalancesByStakingModule(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _validatorBalancesGwei
) external;

Parameters:

NameTypeDescription
_stakingModuleIdsuint256[]ids of all registered staking modules in their registration order
_validatorBalancesGweiuint256[]validator balances for the specified staking modules, in gwei

reportStakingModuleExitedValidatorsCountByNodeOperator​

Updates exited validators counts per node operator for the staking module with the specified id. Restricted to the REPORT_EXITED_VALIDATORS_ROLE role.

function reportStakingModuleExitedValidatorsCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _exitedValidatorsCounts
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_nodeOperatorIdsbytesids of the node operators
_exitedValidatorsCountsbytesnew counts of exited validators for the specified node operators

unsafeSetExitedValidatorsCount​

DEPRECATED. Sets exited validators count for the given module and given node operator in that module without performing critical safety checks, e.g. that exited validators count cannot decrease. 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. Restricted to the UNSAFE_SET_EXITED_VALIDATORS_ROLE role.

function unsafeSetExitedValidatorsCount(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
bool _triggerUpdateFinish,
ValidatorsCountsCorrection calldata _correction
) external;

where ValidatorsCountsCorrection is a struct as seen below,

struct ValidatorsCountsCorrection {
/// @notice The expected current number of exited validators of the module that is
/// being corrected.
uint256 currentModuleExitedValidatorsCount;
/// @notice The expected current number of exited validators of the node operator
/// that is being corrected.
uint256 currentNodeOperatorExitedValidatorsCount;
/// @notice The corrected number of exited validators of the module.
uint256 newModuleExitedValidatorsCount;
/// @notice The corrected number of exited validators of the node operator.
uint256 newNodeOperatorExitedValidatorsCount;
}

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_nodeOperatorIduint256id of the node operator
_triggerUpdateFinishboolflag to call onExitedAndStuckValidatorsCountsUpdated on the module after applying the corrections
_correctionValidatorsCountsCorrectioncorrection details

onValidatorsCountsByNodeOperatorReportingFinished​

Post-report hook called by the oracle when the second phase of data reporting finishes, i.e. when the oracle submitted the complete data on the exited validator counts per node operator for the current reporting frame. Restricted to the REPORT_EXITED_VALIDATORS_ROLE role.

function onValidatorsCountsByNodeOperatorReportingFinished() external;

decreaseStakingModuleVettedKeysCountByNodeOperator​

Decreases vetted signing keys counts per node operator for the staking module with the specified id. Method is called by DSM during the unvetting process. Restricted to the STAKING_MODULE_UNVETTING_ROLE role.

function decreaseStakingModuleVettedKeysCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _vettedSigningKeysCounts
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256staking module id
_nodeOperatorIdsbytesids of the node operators
_vettedSigningKeysCountsbytesnew counts of vetted signing keys for the specified node operators.

reportValidatorExitDelay​

Reports a validator that was requested to exit, but has not exited yet. The report is used to track potential exit‑delay penalties for the responsible node operator within the specified staking module.

Called by the designated verifier contract (ValidatorExitDelayVerifier) that verifies validator status on Consensus Layer. Restricted to the REPORT_VALIDATOR_EXITING_STATUS_ROLE role.

function reportValidatorExitDelay(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
uint256 _proofSlotTimestamp,
bytes calldata _publicKey,
uint256 _eligibleToExitInSec
) external;

Parameters:

NameTypeDescription
_stakingModuleIduint256Staking module id
_nodeOperatorIduint256Node operator id within the specified staking module
_proofSlotTimestampuint256Beacon slot timestamp used as a proof reference for the validator status
_publicKeybytesValidator BLS public key
_eligibleToExitInSecuint256How many seconds the validator has been eligible to exit up to the proof

onValidatorExitTriggered​

Handles triggerable exit events for a set of validators, notifying the corresponding staking modules.

Called by the Triggerable Withdrawals Gateway. Restricted to the REPORT_VALIDATOR_EXIT_TRIGGERED_ROLE role.

struct ValidatorExitData {
uint256 stakingModuleId;
uint256 nodeOperatorId;
bytes pubkey;
}
function onValidatorExitTriggered(
ValidatorExitData[] calldata validatorExitData,
uint256 _withdrawalRequestPaidFee,
uint256 _exitType
) external;

Parameters:

NameTypeDescription
validatorExitDataValidatorExitData[]array of validators for which a triggerable exit was requested (staking module id, node operator id, public key)
_withdrawalRequestPaidFeeuint256Fee paid to submit the withdrawal request on the Execution Layer
_exitTypeuint256Exit trigger type code; may be interpreted differently across staking modules