Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
OracleReportSanityChecker
Compiler Version
v0.8.9+commit.e5eed63a
Optimization Enabled:
Yes with 200 runs
Other Settings:
istanbul EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import {SafeCast} from "@openzeppelin/contracts-v4.4/utils/math/SafeCast.sol";
import {Math256} from "../../common/lib/Math256.sol";
import {AccessControlEnumerable} from "../utils/access/AccessControlEnumerable.sol";
import {PositiveTokenRebaseLimiter, TokenRebaseLimiterData} from "../lib/PositiveTokenRebaseLimiter.sol";
import {ILidoLocator} from "../../common/interfaces/ILidoLocator.sol";
import {IBurner} from "../../common/interfaces/IBurner.sol";
import {StakingRouter} from "../../0.8.9/StakingRouter.sol";
import {ISecondOpinionOracle} from "../interfaces/ISecondOpinionOracle.sol";
interface IWithdrawalQueue {
struct WithdrawalRequestStatus {
/// @notice stETH token amount that was locked on withdrawal queue for this request
uint256 amountOfStETH;
/// @notice amount of stETH shares locked on withdrawal queue for this request
uint256 amountOfShares;
/// @notice address that can claim or transfer this request
address owner;
/// @notice timestamp of when the request was created, in seconds
uint256 timestamp;
/// @notice true, if request is finalized
bool isFinalized;
/// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed)
bool isClaimed;
}
function getWithdrawalStatus(uint256[] calldata _requestIds)
external
view
returns (WithdrawalRequestStatus[] memory statuses);
}
interface IBaseOracle {
function SECONDS_PER_SLOT() external view returns (uint256);
function GENESIS_TIME() external view returns (uint256);
function getLastProcessingRefSlot() external view returns (uint256);
}
/// @notice The set of restrictions used in the sanity checks of the oracle report
/// @dev struct is loaded from the storage and stored in memory during the tx running
struct LimitsList {
/// @notice The max possible number of validators that might be reported as `exited`
/// per single day, depends on the Consensus Layer churn limit
/// @dev Must fit into uint16 (<= 65_535)
uint256 exitedValidatorsPerDayLimit;
/// @notice The max possible number of validators that might be reported as `appeared`
/// per single day, limited by the max daily deposits via DepositSecurityModule in practice
/// isn't limited by a consensus layer (because `appeared` includes `pending`, i.e., not `activated` yet)
/// @dev Must fit into uint16 (<= 65_535)
uint256 appearedValidatorsPerDayLimit;
/// @notice The max annual increase of the total validators' balances on the Consensus Layer
/// since the previous oracle report
/// @dev Represented in the Basis Points (100% == 10_000)
uint256 annualBalanceIncreaseBPLimit;
/// @notice The max deviation of the provided `simulatedShareRate`
/// and the actual one within the currently processing oracle report
/// @dev Represented in the Basis Points (100% == 10_000)
uint256 simulatedShareRateDeviationBPLimit;
/// @notice The max number of exit requests allowed in report to ValidatorsExitBusOracle
uint256 maxValidatorExitRequestsPerReport;
/// @notice The max number of data list items reported to accounting oracle in extra data per single transaction
/// @dev Must fit into uint16 (<= 65_535)
uint256 maxItemsPerExtraDataTransaction;
/// @notice The max number of node operators reported per extra data list item
/// @dev Must fit into uint16 (<= 65_535)
uint256 maxNodeOperatorsPerExtraDataItem;
/// @notice The min time required to be passed from the creation of the request to be
/// finalized till the time of the oracle report
uint256 requestTimestampMargin;
/// @notice The positive token rebase allowed per single LidoOracle report
/// @dev uses 1e9 precision, e.g.: 1e6 - 0.1%; 1e9 - 100%, see `setMaxPositiveTokenRebase()`
uint256 maxPositiveTokenRebase;
/// @notice Initial slashing amount per one validator to calculate initial slashing of the validators' balances on the Consensus Layer
/// @dev Represented in the PWei (1^15 Wei). Must fit into uint16 (<= 65_535)
uint256 initialSlashingAmountPWei;
/// @notice Inactivity penalties amount per one validator to calculate penalties of the validators' balances on the Consensus Layer
/// @dev Represented in the PWei (1^15 Wei). Must fit into uint16 (<= 65_535)
uint256 inactivityPenaltiesAmountPWei;
/// @notice The maximum percent on how Second Opinion Oracle reported value could be greater
/// than reported by the AccountingOracle. There is an assumption that second opinion oracle CL balance
/// can be greater as calculated for the withdrawal credentials.
/// @dev Represented in the Basis Points (100% == 10_000)
uint256 clBalanceOraclesErrorUpperBPLimit;
}
/// @dev The packed version of the LimitsList struct to be effectively persisted in storage
struct LimitsListPacked {
uint16 exitedValidatorsPerDayLimit;
uint16 appearedValidatorsPerDayLimit;
uint16 annualBalanceIncreaseBPLimit;
uint16 simulatedShareRateDeviationBPLimit;
uint16 maxValidatorExitRequestsPerReport;
uint16 maxItemsPerExtraDataTransaction;
uint16 maxNodeOperatorsPerExtraDataItem;
uint32 requestTimestampMargin;
uint64 maxPositiveTokenRebase;
uint16 initialSlashingAmountPWei;
uint16 inactivityPenaltiesAmountPWei;
uint16 clBalanceOraclesErrorUpperBPLimit;
}
struct ReportData {
uint64 timestamp;
uint64 totalExitedValidators;
uint128 negativeCLRebaseWei;
}
uint256 constant MAX_BASIS_POINTS = 10_000;
uint256 constant SHARE_RATE_PRECISION_E27 = 1e27;
uint256 constant ONE_PWEI = 1e15;
/// @title Sanity checks for the Lido's oracle report
/// @notice The contracts contain methods to perform sanity checks of the Lido's oracle report
/// and lever methods for granular tuning of the params of the checks
contract OracleReportSanityChecker is AccessControlEnumerable {
using LimitsListPacker for LimitsList;
using LimitsListUnpacker for LimitsListPacked;
using PositiveTokenRebaseLimiter for TokenRebaseLimiterData;
bytes32 public constant ALL_LIMITS_MANAGER_ROLE = keccak256("ALL_LIMITS_MANAGER_ROLE");
bytes32 public constant EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE =
keccak256("EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE");
bytes32 public constant APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE =
keccak256("APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE");
bytes32 public constant ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE =
keccak256("ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE");
bytes32 public constant SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE =
keccak256("SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE");
bytes32 public constant MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE =
keccak256("MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE");
bytes32 public constant MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION_ROLE =
keccak256("MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION_ROLE");
bytes32 public constant MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_ROLE =
keccak256("MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_ROLE");
bytes32 public constant REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE = keccak256("REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE");
bytes32 public constant MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE =
keccak256("MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE");
bytes32 public constant SECOND_OPINION_MANAGER_ROLE =
keccak256("SECOND_OPINION_MANAGER_ROLE");
bytes32 public constant INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE =
keccak256("INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE");
uint256 private constant DEFAULT_TIME_ELAPSED = 1 hours;
uint256 private constant DEFAULT_CL_BALANCE = 1 gwei;
uint256 private constant SECONDS_PER_DAY = 24 * 60 * 60;
ILidoLocator private immutable LIDO_LOCATOR;
uint256 private immutable GENESIS_TIME;
uint256 private immutable SECONDS_PER_SLOT;
address private immutable LIDO_ADDRESS;
LimitsListPacked private _limits;
/// @dev Historical reports data
ReportData[] public reportData;
/// @dev The address of the second opinion oracle
ISecondOpinionOracle public secondOpinionOracle;
/// @param _lidoLocator address of the LidoLocator instance
/// @param _admin address to grant DEFAULT_ADMIN_ROLE of the AccessControl contract
/// @param _limitsList initial values to be set for the limits list
constructor(
address _lidoLocator,
address _admin,
LimitsList memory _limitsList
) {
if (_admin == address(0)) revert AdminCannotBeZero();
LIDO_LOCATOR = ILidoLocator(_lidoLocator);
address accountingOracle = LIDO_LOCATOR.accountingOracle();
GENESIS_TIME = IBaseOracle(accountingOracle).GENESIS_TIME();
SECONDS_PER_SLOT = IBaseOracle(accountingOracle).SECONDS_PER_SLOT();
LIDO_ADDRESS = LIDO_LOCATOR.lido();
_updateLimits(_limitsList);
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
}
/// @notice Return number of report data elements available on the public reportData array.
function getReportDataCount() external view returns (uint256) {
return reportData.length;
}
/// @notice returns the address of the LidoLocator
function getLidoLocator() public view returns (address) {
return address(LIDO_LOCATOR);
}
/// @notice Returns the limits list for the Lido's oracle report sanity checks
function getOracleReportLimits() public view returns (LimitsList memory) {
return _limits.unpack();
}
/// @notice Returns max positive token rebase value with 1e9 precision:
/// e.g.: 1e6 - 0.1%; 1e9 - 100%
/// - zero value means uninitialized
/// - type(uint64).max means unlimited
///
/// @dev Get max positive rebase allowed per single oracle report token rebase happens on total
/// supply adjustment, huge positive rebase can incur oracle report sandwiching.
///
/// stETH balance for the `account` defined as:
/// balanceOf(account) =
/// shares[account] * totalPooledEther / totalShares = shares[account] * shareRate
///
/// Suppose shareRate changes when oracle reports (see `handleOracleReport`)
/// which means that token rebase happens:
///
/// preShareRate = preTotalPooledEther() / preTotalShares()
/// postShareRate = postTotalPooledEther() / postTotalShares()
/// R = (postShareRate - preShareRate) / preShareRate
///
/// R > 0 corresponds to the relative positive rebase value (i.e., instant APR)
///
/// NB: The value is not set by default (explicit initialization required),
/// the recommended sane values are from 0.05% to 0.1%.
function getMaxPositiveTokenRebase() public view returns (uint256) {
return _limits.maxPositiveTokenRebase;
}
/// @notice Sets the new values for the limits list and second opinion oracle
/// @param _limitsList new limits list
/// @param _secondOpinionOracle negative rebase oracle.
function setOracleReportLimits(LimitsList calldata _limitsList, ISecondOpinionOracle _secondOpinionOracle) external onlyRole(ALL_LIMITS_MANAGER_ROLE) {
_updateLimits(_limitsList);
if (_secondOpinionOracle != secondOpinionOracle) {
secondOpinionOracle = _secondOpinionOracle;
emit SecondOpinionOracleChanged(_secondOpinionOracle);
}
}
/// @notice Sets the new value for the exitedValidatorsPerDayLimit
///
/// NB: AccountingOracle reports validators as exited once they passed the `EXIT_EPOCH` on Consensus Layer
/// therefore, the value should be set in accordance to the consensus layer churn limit
///
/// @param _exitedValidatorsPerDayLimit new exitedValidatorsPerDayLimit value
function setExitedValidatorsPerDayLimit(uint256 _exitedValidatorsPerDayLimit)
external
onlyRole(EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.exitedValidatorsPerDayLimit = _exitedValidatorsPerDayLimit;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the appearedValidatorsPerDayLimit
///
/// NB: AccountingOracle reports validators as appeared once they become `pending`
/// (might be not `activated` yet). Thus, this limit should be high enough because consensus layer
/// has no intrinsic churn limit for the amount of `pending` validators (only for `activated` instead).
/// For Lido it depends on the amount of deposits that can be made via DepositSecurityModule daily.
///
/// @param _appearedValidatorsPerDayLimit new appearedValidatorsPerDayLimit value
function setAppearedValidatorsPerDayLimit(uint256 _appearedValidatorsPerDayLimit)
external
onlyRole(APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.appearedValidatorsPerDayLimit = _appearedValidatorsPerDayLimit;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the annualBalanceIncreaseBPLimit
/// @param _annualBalanceIncreaseBPLimit new annualBalanceIncreaseBPLimit value
function setAnnualBalanceIncreaseBPLimit(uint256 _annualBalanceIncreaseBPLimit)
external
onlyRole(ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.annualBalanceIncreaseBPLimit = _annualBalanceIncreaseBPLimit;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the simulatedShareRateDeviationBPLimit
/// @param _simulatedShareRateDeviationBPLimit new simulatedShareRateDeviationBPLimit value
function setSimulatedShareRateDeviationBPLimit(uint256 _simulatedShareRateDeviationBPLimit)
external
onlyRole(SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.simulatedShareRateDeviationBPLimit = _simulatedShareRateDeviationBPLimit;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the maxValidatorExitRequestsPerReport
/// @param _maxValidatorExitRequestsPerReport new maxValidatorExitRequestsPerReport value
function setMaxExitRequestsPerOracleReport(uint256 _maxValidatorExitRequestsPerReport)
external
onlyRole(MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.maxValidatorExitRequestsPerReport = _maxValidatorExitRequestsPerReport;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the requestTimestampMargin
/// @param _requestTimestampMargin new requestTimestampMargin value
function setRequestTimestampMargin(uint256 _requestTimestampMargin)
external
onlyRole(REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.requestTimestampMargin = _requestTimestampMargin;
_updateLimits(limitsList);
}
/// @notice Set max positive token rebase allowed per single oracle report token rebase happens
/// on total supply adjustment, huge positive rebase can incur oracle report sandwiching.
///
/// @param _maxPositiveTokenRebase max positive token rebase value with 1e9 precision:
/// e.g.: 1e6 - 0.1%; 1e9 - 100%
/// - passing zero value is prohibited
/// - to allow unlimited rebases, pass max uint64, i.e.: type(uint64).max
function setMaxPositiveTokenRebase(uint256 _maxPositiveTokenRebase)
external
onlyRole(MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.maxPositiveTokenRebase = _maxPositiveTokenRebase;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the maxItemsPerExtraDataTransaction
/// @param _maxItemsPerExtraDataTransaction new maxItemsPerExtraDataTransaction value
function setMaxItemsPerExtraDataTransaction(uint256 _maxItemsPerExtraDataTransaction)
external
onlyRole(MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.maxItemsPerExtraDataTransaction = _maxItemsPerExtraDataTransaction;
_updateLimits(limitsList);
}
/// @notice Sets the new value for the max maxNodeOperatorsPerExtraDataItem
/// @param _maxNodeOperatorsPerExtraDataItem new maxNodeOperatorsPerExtraDataItem value
function setMaxNodeOperatorsPerExtraDataItem(uint256 _maxNodeOperatorsPerExtraDataItem)
external
onlyRole(MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.maxNodeOperatorsPerExtraDataItem = _maxNodeOperatorsPerExtraDataItem;
_updateLimits(limitsList);
}
/// @notice Sets the address of the second opinion oracle and clBalanceOraclesErrorUpperBPLimit value
/// @param _secondOpinionOracle second opinion oracle.
/// If it's zero address — oracle is disabled.
/// Default value is zero address.
/// @param _clBalanceOraclesErrorUpperBPLimit new clBalanceOraclesErrorUpperBPLimit value
function setSecondOpinionOracleAndCLBalanceUpperMargin(ISecondOpinionOracle _secondOpinionOracle, uint256 _clBalanceOraclesErrorUpperBPLimit)
external
onlyRole(SECOND_OPINION_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.clBalanceOraclesErrorUpperBPLimit = _clBalanceOraclesErrorUpperBPLimit;
_updateLimits(limitsList);
if (_secondOpinionOracle != secondOpinionOracle) {
secondOpinionOracle = ISecondOpinionOracle(_secondOpinionOracle);
emit SecondOpinionOracleChanged(_secondOpinionOracle);
}
}
/// @notice Sets the initial slashing and penalties Amountficients
/// @param _initialSlashingAmountPWei - initial slashing Amountficient (in PWei)
/// @param _inactivityPenaltiesAmountPWei - penalties Amountficient (in PWei)
function setInitialSlashingAndPenaltiesAmount(uint256 _initialSlashingAmountPWei, uint256 _inactivityPenaltiesAmountPWei)
external
onlyRole(INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE)
{
LimitsList memory limitsList = _limits.unpack();
limitsList.initialSlashingAmountPWei = _initialSlashingAmountPWei;
limitsList.inactivityPenaltiesAmountPWei = _inactivityPenaltiesAmountPWei;
_updateLimits(limitsList);
}
/// @notice Returns the allowed ETH amount that might be taken from the withdrawal vault and EL
/// rewards vault during Lido's oracle report processing
/// @param _preTotalPooledEther total amount of ETH controlled by the protocol
/// @param _preTotalShares total amount of minted stETH shares
/// @param _preCLBalance sum of all Lido validators' balances on the Consensus Layer before the
/// current oracle report
/// @param _postCLBalance sum of all Lido validators' balances on the Consensus Layer after the
/// current oracle report
/// @param _withdrawalVaultBalance withdrawal vault balance on Execution Layer for the report calculation moment
/// @param _elRewardsVaultBalance elRewards vault balance on Execution Layer for the report calculation moment
/// @param _sharesRequestedToBurn shares requested to burn through Burner for the report calculation moment
/// @param _etherToLockForWithdrawals ether to lock on withdrawals queue contract
/// @param _newSharesToBurnForWithdrawals new shares to burn due to withdrawal request finalization
/// @return withdrawals ETH amount allowed to be taken from the withdrawals vault
/// @return elRewards ETH amount allowed to be taken from the EL rewards vault
/// @return simulatedSharesToBurn simulated amount to be burnt (if no ether locked on withdrawals)
/// @return sharesToBurn amount to be burnt (accounting for withdrawals finalization)
function smoothenTokenRebase(
uint256 _preTotalPooledEther,
uint256 _preTotalShares,
uint256 _preCLBalance,
uint256 _postCLBalance,
uint256 _withdrawalVaultBalance,
uint256 _elRewardsVaultBalance,
uint256 _sharesRequestedToBurn,
uint256 _etherToLockForWithdrawals,
uint256 _newSharesToBurnForWithdrawals
) external view returns (
uint256 withdrawals,
uint256 elRewards,
uint256 simulatedSharesToBurn,
uint256 sharesToBurn
) {
TokenRebaseLimiterData memory tokenRebaseLimiter = PositiveTokenRebaseLimiter.initLimiterState(
getMaxPositiveTokenRebase(),
_preTotalPooledEther,
_preTotalShares
);
if (_postCLBalance < _preCLBalance) {
tokenRebaseLimiter.decreaseEther(_preCLBalance - _postCLBalance);
} else {
tokenRebaseLimiter.increaseEther(_postCLBalance - _preCLBalance);
}
withdrawals = tokenRebaseLimiter.increaseEther(_withdrawalVaultBalance);
elRewards = tokenRebaseLimiter.increaseEther(_elRewardsVaultBalance);
// determining the shares to burn limit that would have been
// if no withdrawals finalized during the report
// it's used to check later the provided `simulatedShareRate` value
// after the off-chain calculation via `eth_call` of `Lido.handleOracleReport()`
// see also step 9 of the `Lido._handleOracleReport()`
simulatedSharesToBurn = Math256.min(tokenRebaseLimiter.getSharesToBurnLimit(), _sharesRequestedToBurn);
// remove ether to lock for withdrawals from total pooled ether
tokenRebaseLimiter.decreaseEther(_etherToLockForWithdrawals);
// re-evaluate shares to burn after TVL was updated due to withdrawals finalization
sharesToBurn = Math256.min(
tokenRebaseLimiter.getSharesToBurnLimit(),
_newSharesToBurnForWithdrawals + _sharesRequestedToBurn
);
}
/// @notice Applies sanity checks to the accounting params of Lido's oracle report
/// WARNING. The function has side effects and modifies the state of the contract.
/// It's needed to keep information about exited validators counts and negative rebase values over time.
/// The function called from Lido contract that uses the 'old' Solidity version (0.4.24) and will do a correct
/// call to this method even it's declared as "view" in interface there.
/// @param _timeElapsed time elapsed since the previous oracle report
/// @param _preCLBalance sum of all Lido validators' balances on the Consensus Layer before the
/// current oracle report (NB: also include the initial balance of newly appeared validators)
/// @param _postCLBalance sum of all Lido validators' balances on the Consensus Layer after the
/// current oracle report
/// @param _withdrawalVaultBalance withdrawal vault balance on Execution Layer for the report reference slot
/// @param _elRewardsVaultBalance el rewards vault balance on Execution Layer for the report reference slot
/// @param _sharesRequestedToBurn shares requested to burn for the report reference slot
/// @param _preCLValidators Lido-participating validators on the CL side before the current oracle report
/// @param _postCLValidators Lido-participating validators on the CL side after the current oracle report
function checkAccountingOracleReport(
uint256 _timeElapsed,
uint256 _preCLBalance,
uint256 _postCLBalance,
uint256 _withdrawalVaultBalance,
uint256 _elRewardsVaultBalance,
uint256 _sharesRequestedToBurn,
uint256 _preCLValidators,
uint256 _postCLValidators
) external {
if (msg.sender != LIDO_ADDRESS) {
revert CalledNotFromLido();
}
LimitsList memory limitsList = _limits.unpack();
uint256 refSlot = IBaseOracle(LIDO_LOCATOR.accountingOracle()).getLastProcessingRefSlot();
address withdrawalVault = LIDO_LOCATOR.withdrawalVault();
// 1. Withdrawals vault reported balance
_checkWithdrawalVaultBalance(withdrawalVault.balance, _withdrawalVaultBalance);
address elRewardsVault = LIDO_LOCATOR.elRewardsVault();
// 2. EL rewards vault reported balance
_checkELRewardsVaultBalance(elRewardsVault.balance, _elRewardsVaultBalance);
// 3. Burn requests
_checkSharesRequestedToBurn(_sharesRequestedToBurn);
// 4. Consensus Layer balance decrease
_checkCLBalanceDecrease(limitsList, _preCLBalance,
_postCLBalance, _withdrawalVaultBalance, _postCLValidators, refSlot);
// 5. Consensus Layer annual balances increase
_checkAnnualBalancesIncrease(limitsList, _preCLBalance, _postCLBalance, _timeElapsed);
// 6. Appeared validators increase
if (_postCLValidators > _preCLValidators) {
_checkAppearedValidatorsChurnLimit(limitsList, (_postCLValidators - _preCLValidators), _timeElapsed);
}
}
/// @notice Applies sanity checks to the number of validator exit requests supplied to ValidatorExitBusOracle
/// @param _exitRequestsCount Number of validator exit requests supplied per oracle report
function checkExitBusOracleReport(uint256 _exitRequestsCount)
external
view
{
uint256 limit = _limits.unpack().maxValidatorExitRequestsPerReport;
if (_exitRequestsCount > limit) {
revert IncorrectNumberOfExitRequestsPerReport(limit);
}
}
/// @notice Check rate of exited validators per day
/// @param _exitedValidatorsCount Number of validator exited per oracle report
function checkExitedValidatorsRatePerDay(uint256 _exitedValidatorsCount)
external
view
{
uint256 exitedValidatorsLimit = _limits.unpack().exitedValidatorsPerDayLimit;
if (_exitedValidatorsCount > exitedValidatorsLimit) {
revert ExitedValidatorsLimitExceeded(exitedValidatorsLimit, _exitedValidatorsCount);
}
}
/// @notice check the number of node operators reported per extra data item in the accounting oracle report.
/// @param _itemIndex Index of item in extra data
/// @param _nodeOperatorsCount Number of validator exit requests supplied per oracle report
function checkNodeOperatorsPerExtraDataItemCount(uint256 _itemIndex, uint256 _nodeOperatorsCount)
external
view
{
uint256 limit = _limits.unpack().maxNodeOperatorsPerExtraDataItem;
if (_nodeOperatorsCount > limit) {
revert TooManyNodeOpsPerExtraDataItem(_itemIndex, _nodeOperatorsCount);
}
}
/// @notice Check the number of extra data list items per transaction in the accounting oracle report.
/// @param _extraDataListItemsCount Number of items per single transaction in the accounting oracle report
function checkExtraDataItemsCountPerTransaction(uint256 _extraDataListItemsCount)
external
view
{
uint256 limit = _limits.unpack().maxItemsPerExtraDataTransaction;
if (_extraDataListItemsCount > limit) {
revert TooManyItemsPerExtraDataTransaction(limit, _extraDataListItemsCount);
}
}
/// @notice Applies sanity checks to the withdrawal requests finalization
/// @param _lastFinalizableRequestId last finalizable withdrawal request id
/// @param _reportTimestamp timestamp when the originated oracle report was submitted
function checkWithdrawalQueueOracleReport(
uint256 _lastFinalizableRequestId,
uint256 _reportTimestamp
)
external
view
{
LimitsList memory limitsList = _limits.unpack();
address withdrawalQueue = LIDO_LOCATOR.withdrawalQueue();
_checkLastFinalizableId(limitsList, withdrawalQueue, _lastFinalizableRequestId, _reportTimestamp);
}
/// @notice Applies sanity checks to the simulated share rate for withdrawal requests finalization
/// @param _postTotalPooledEther total pooled ether after report applied
/// @param _postTotalShares total shares after report applied
/// @param _etherLockedOnWithdrawalQueue ether locked on withdrawal queue for the current oracle report
/// @param _sharesBurntDueToWithdrawals shares burnt due to withdrawals finalization
/// @param _simulatedShareRate share rate provided with the oracle report (simulated via off-chain "eth_call")
function checkSimulatedShareRate(
uint256 _postTotalPooledEther,
uint256 _postTotalShares,
uint256 _etherLockedOnWithdrawalQueue,
uint256 _sharesBurntDueToWithdrawals,
uint256 _simulatedShareRate
) external view {
LimitsList memory limitsList = _limits.unpack();
// Pretending that withdrawals were not processed
// virtually return locked ether back to `_postTotalPooledEther`
// virtually return burnt just finalized withdrawals shares back to `_postTotalShares`
_checkSimulatedShareRate(
limitsList,
_postTotalPooledEther + _etherLockedOnWithdrawalQueue,
_postTotalShares + _sharesBurntDueToWithdrawals,
_simulatedShareRate
);
}
function _checkWithdrawalVaultBalance(
uint256 _actualWithdrawalVaultBalance,
uint256 _reportedWithdrawalVaultBalance
) internal pure {
if (_reportedWithdrawalVaultBalance > _actualWithdrawalVaultBalance) {
revert IncorrectWithdrawalsVaultBalance(_actualWithdrawalVaultBalance);
}
}
function _checkELRewardsVaultBalance(
uint256 _actualELRewardsVaultBalance,
uint256 _reportedELRewardsVaultBalance
) internal pure {
if (_reportedELRewardsVaultBalance > _actualELRewardsVaultBalance) {
revert IncorrectELRewardsVaultBalance(_actualELRewardsVaultBalance);
}
}
function _checkSharesRequestedToBurn(uint256 _sharesRequestedToBurn) internal view {
(uint256 coverShares, uint256 nonCoverShares) = IBurner(LIDO_LOCATOR.burner()).getSharesRequestedToBurn();
uint256 actualSharesToBurn = coverShares + nonCoverShares;
if (_sharesRequestedToBurn > actualSharesToBurn) {
revert IncorrectSharesRequestedToBurn(actualSharesToBurn);
}
}
function _addReportData(uint256 _timestamp, uint256 _exitedValidatorsCount, uint256 _negativeCLRebase) internal {
reportData.push(ReportData(
SafeCast.toUint64(_timestamp),
SafeCast.toUint64(_exitedValidatorsCount),
SafeCast.toUint128(_negativeCLRebase)
));
}
function _sumNegativeRebasesNotOlderThan(uint256 _timestamp) internal view returns (uint256) {
uint256 sum;
for (int256 index = int256(reportData.length) - 1; index >= 0; index--) {
if (reportData[uint256(index)].timestamp > SafeCast.toUint64(_timestamp)) {
sum += reportData[uint256(index)].negativeCLRebaseWei;
} else {
break;
}
}
return sum;
}
function _exitedValidatorsAtTimestamp(uint256 _timestamp) internal view returns (uint256) {
for (int256 index = int256(reportData.length) - 1; index >= 0; index--) {
if (reportData[uint256(index)].timestamp <= SafeCast.toUint64(_timestamp)) {
return reportData[uint256(index)].totalExitedValidators;
}
}
return 0;
}
function _checkCLBalanceDecrease(
LimitsList memory _limitsList,
uint256 _preCLBalance,
uint256 _postCLBalance,
uint256 _withdrawalVaultBalance,
uint256 _postCLValidators,
uint256 _refSlot
) internal {
uint256 reportTimestamp = GENESIS_TIME + _refSlot * SECONDS_PER_SLOT;
// Checking exitedValidators against StakingRouter
StakingRouter stakingRouter = StakingRouter(payable(LIDO_LOCATOR.stakingRouter()));
uint256[] memory ids = stakingRouter.getStakingModuleIds();
uint256 stakingRouterExitedValidators;
for (uint256 i = 0; i < ids.length; i++) {
StakingRouter.StakingModule memory module = stakingRouter.getStakingModule(ids[i]);
stakingRouterExitedValidators += module.exitedValidatorsCount;
}
if (_preCLBalance <= _postCLBalance + _withdrawalVaultBalance) {
_addReportData(reportTimestamp, stakingRouterExitedValidators, 0);
// If the CL balance is not decreased, we don't need to check anything here
return;
}
_addReportData(reportTimestamp, stakingRouterExitedValidators, _preCLBalance - (_postCLBalance + _withdrawalVaultBalance));
// NOTE. Values of 18 and 54 days are taken from spec. Check the details here
// https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-23.md
uint256 negativeCLRebaseSum = _sumNegativeRebasesNotOlderThan(reportTimestamp - 18 days);
uint256 maxAllowedCLRebaseNegativeSum =
_limitsList.initialSlashingAmountPWei * ONE_PWEI * (_postCLValidators - _exitedValidatorsAtTimestamp(reportTimestamp - 18 days)) +
_limitsList.inactivityPenaltiesAmountPWei * ONE_PWEI * (_postCLValidators - _exitedValidatorsAtTimestamp(reportTimestamp - 54 days));
if (negativeCLRebaseSum <= maxAllowedCLRebaseNegativeSum) {
// If the rebase diff is less or equal max allowed sum, we accept the report
emit NegativeCLRebaseAccepted(_refSlot, _postCLBalance + _withdrawalVaultBalance, negativeCLRebaseSum, maxAllowedCLRebaseNegativeSum);
return;
}
// If there is no negative rebase oracle, then we don't need to check it's report
if (address(secondOpinionOracle) == address(0)) {
// If there is no oracle and the diff is more than limit, we revert
revert IncorrectCLBalanceDecrease(negativeCLRebaseSum, maxAllowedCLRebaseNegativeSum);
}
_askSecondOpinion(_refSlot, _postCLBalance, _withdrawalVaultBalance, _limitsList);
}
function _askSecondOpinion(uint256 _refSlot, uint256 _postCLBalance, uint256 _withdrawalVaultBalance, LimitsList memory _limitsList) internal {
(bool success, uint256 clOracleBalanceGwei, uint256 oracleWithdrawalVaultBalanceWei,,) = secondOpinionOracle.getReport(_refSlot);
if (success) {
uint256 clBalanceWei = clOracleBalanceGwei * 1 gwei;
if (clBalanceWei < _postCLBalance) {
revert NegativeRebaseFailedCLBalanceMismatch(_postCLBalance, clBalanceWei, _limitsList.clBalanceOraclesErrorUpperBPLimit);
}
if (MAX_BASIS_POINTS * (clBalanceWei - _postCLBalance) >
_limitsList.clBalanceOraclesErrorUpperBPLimit * clBalanceWei) {
revert NegativeRebaseFailedCLBalanceMismatch(_postCLBalance, clBalanceWei, _limitsList.clBalanceOraclesErrorUpperBPLimit);
}
if (oracleWithdrawalVaultBalanceWei != _withdrawalVaultBalance) {
revert NegativeRebaseFailedWithdrawalVaultBalanceMismatch(_withdrawalVaultBalance, oracleWithdrawalVaultBalanceWei);
}
emit NegativeCLRebaseConfirmed(_refSlot, _postCLBalance, _withdrawalVaultBalance);
} else {
revert NegativeRebaseFailedSecondOpinionReportIsNotReady();
}
}
function _checkAnnualBalancesIncrease(
LimitsList memory _limitsList,
uint256 _preCLBalance,
uint256 _postCLBalance,
uint256 _timeElapsed
) internal pure {
// allow zero values for scratch deploy
// NB: annual increase have to be large enough for scratch deploy
if (_preCLBalance == 0) {
_preCLBalance = DEFAULT_CL_BALANCE;
}
if (_preCLBalance >= _postCLBalance) return;
if (_timeElapsed == 0) {
_timeElapsed = DEFAULT_TIME_ELAPSED;
}
uint256 balanceIncrease = _postCLBalance - _preCLBalance;
uint256 annualBalanceIncrease = ((365 days * MAX_BASIS_POINTS * balanceIncrease) /
_preCLBalance) /
_timeElapsed;
if (annualBalanceIncrease > _limitsList.annualBalanceIncreaseBPLimit) {
revert IncorrectCLBalanceIncrease(annualBalanceIncrease);
}
}
function _checkAppearedValidatorsChurnLimit(
LimitsList memory _limitsList,
uint256 _appearedValidators,
uint256 _timeElapsed
) internal pure {
if (_timeElapsed == 0) {
_timeElapsed = DEFAULT_TIME_ELAPSED;
}
uint256 appearedLimit = (_limitsList.appearedValidatorsPerDayLimit * _timeElapsed) / SECONDS_PER_DAY;
if (_appearedValidators > appearedLimit) revert IncorrectAppearedValidators(_appearedValidators);
}
function _checkLastFinalizableId(
LimitsList memory _limitsList,
address _withdrawalQueue,
uint256 _lastFinalizableId,
uint256 _reportTimestamp
) internal view {
uint256[] memory requestIds = new uint256[](1);
requestIds[0] = _lastFinalizableId;
IWithdrawalQueue.WithdrawalRequestStatus[] memory statuses = IWithdrawalQueue(_withdrawalQueue)
.getWithdrawalStatus(requestIds);
if (_reportTimestamp < statuses[0].timestamp + _limitsList.requestTimestampMargin)
revert IncorrectRequestFinalization(statuses[0].timestamp);
}
function _checkSimulatedShareRate(
LimitsList memory _limitsList,
uint256 _noWithdrawalsPostTotalPooledEther,
uint256 _noWithdrawalsPostTotalShares,
uint256 _simulatedShareRate
) internal pure {
uint256 actualShareRate = (
_noWithdrawalsPostTotalPooledEther * SHARE_RATE_PRECISION_E27
) / _noWithdrawalsPostTotalShares;
if (actualShareRate == 0) {
// can't finalize anything if the actual share rate is zero
revert ActualShareRateIsZero();
}
// the simulated share rate can be either higher or lower than the actual one
// in case of new user-submitted ether & minted `stETH` between the oracle reference slot
// and the actual report delivery slot
//
// it happens because the oracle daemon snapshots rewards or losses at the reference slot,
// and then calculates simulated share rate, but if new ether was submitted together with minting new `stETH`
// after the reference slot passed, the oracle daemon still submits the same amount of rewards or losses,
// which now is applicable to more 'shareholders', lowering the impact per a single share
// (i.e, changing the actual share rate)
//
// simulated share rate ≤ actual share rate can be for a negative token rebase
// simulated share rate ≥ actual share rate can be for a positive token rebase
//
// Given that:
// 1) CL one-off balance decrease ≤ token rebase ≤ max positive token rebase
// 2) user-submitted ether & minted `stETH` don't exceed the current staking rate limit
// (see Lido.getCurrentStakeLimit())
//
// can conclude that `simulatedShareRateDeviationBPLimit` (L) should be set as follows:
// L = (2 * SRL) * max(CLD, MPR),
// where:
// - CLD is consensus layer one-off balance decrease (as BP),
// - MPR is max positive token rebase (as BP),
// - SRL is staking rate limit normalized by TVL (`maxStakeLimit / totalPooledEther`)
// totalPooledEther should be chosen as a reasonable lower bound of the protocol TVL
//
uint256 simulatedShareDiff = Math256.absDiff(actualShareRate, _simulatedShareRate);
uint256 simulatedShareDeviation = (MAX_BASIS_POINTS * simulatedShareDiff) / actualShareRate;
if (simulatedShareDeviation > _limitsList.simulatedShareRateDeviationBPLimit) {
revert IncorrectSimulatedShareRate(_simulatedShareRate, actualShareRate);
}
}
function _updateLimits(LimitsList memory _newLimitsList) internal {
LimitsList memory _oldLimitsList = _limits.unpack();
if (_oldLimitsList.exitedValidatorsPerDayLimit != _newLimitsList.exitedValidatorsPerDayLimit) {
_checkLimitValue(_newLimitsList.exitedValidatorsPerDayLimit, 0, type(uint16).max);
emit ExitedValidatorsPerDayLimitSet(_newLimitsList.exitedValidatorsPerDayLimit);
}
if (_oldLimitsList.appearedValidatorsPerDayLimit != _newLimitsList.appearedValidatorsPerDayLimit) {
_checkLimitValue(_newLimitsList.appearedValidatorsPerDayLimit, 0, type(uint16).max);
emit AppearedValidatorsPerDayLimitSet(_newLimitsList.appearedValidatorsPerDayLimit);
}
if (_oldLimitsList.annualBalanceIncreaseBPLimit != _newLimitsList.annualBalanceIncreaseBPLimit) {
_checkLimitValue(_newLimitsList.annualBalanceIncreaseBPLimit, 0, MAX_BASIS_POINTS);
emit AnnualBalanceIncreaseBPLimitSet(_newLimitsList.annualBalanceIncreaseBPLimit);
}
if (_oldLimitsList.simulatedShareRateDeviationBPLimit != _newLimitsList.simulatedShareRateDeviationBPLimit) {
_checkLimitValue(_newLimitsList.simulatedShareRateDeviationBPLimit, 0, MAX_BASIS_POINTS);
emit SimulatedShareRateDeviationBPLimitSet(_newLimitsList.simulatedShareRateDeviationBPLimit);
}
if (_oldLimitsList.maxValidatorExitRequestsPerReport != _newLimitsList.maxValidatorExitRequestsPerReport) {
_checkLimitValue(_newLimitsList.maxValidatorExitRequestsPerReport, 0, type(uint16).max);
emit MaxValidatorExitRequestsPerReportSet(_newLimitsList.maxValidatorExitRequestsPerReport);
}
if (_oldLimitsList.maxItemsPerExtraDataTransaction != _newLimitsList.maxItemsPerExtraDataTransaction) {
_checkLimitValue(_newLimitsList.maxItemsPerExtraDataTransaction, 0, type(uint16).max);
emit MaxItemsPerExtraDataTransactionSet(_newLimitsList.maxItemsPerExtraDataTransaction);
}
if (_oldLimitsList.maxNodeOperatorsPerExtraDataItem != _newLimitsList.maxNodeOperatorsPerExtraDataItem) {
_checkLimitValue(_newLimitsList.maxNodeOperatorsPerExtraDataItem, 0, type(uint16).max);
emit MaxNodeOperatorsPerExtraDataItemSet(_newLimitsList.maxNodeOperatorsPerExtraDataItem);
}
if (_oldLimitsList.requestTimestampMargin != _newLimitsList.requestTimestampMargin) {
_checkLimitValue(_newLimitsList.requestTimestampMargin, 0, type(uint32).max);
emit RequestTimestampMarginSet(_newLimitsList.requestTimestampMargin);
}
if (_oldLimitsList.maxPositiveTokenRebase != _newLimitsList.maxPositiveTokenRebase) {
_checkLimitValue(_newLimitsList.maxPositiveTokenRebase, 1, type(uint64).max);
emit MaxPositiveTokenRebaseSet(_newLimitsList.maxPositiveTokenRebase);
}
if (_oldLimitsList.initialSlashingAmountPWei != _newLimitsList.initialSlashingAmountPWei) {
_checkLimitValue(_newLimitsList.initialSlashingAmountPWei, 0, type(uint16).max);
emit InitialSlashingAmountSet(_newLimitsList.initialSlashingAmountPWei);
}
if (_oldLimitsList.inactivityPenaltiesAmountPWei != _newLimitsList.inactivityPenaltiesAmountPWei) {
_checkLimitValue(_newLimitsList.inactivityPenaltiesAmountPWei, 0, type(uint16).max);
emit InactivityPenaltiesAmountSet(_newLimitsList.inactivityPenaltiesAmountPWei);
}
if (_oldLimitsList.clBalanceOraclesErrorUpperBPLimit != _newLimitsList.clBalanceOraclesErrorUpperBPLimit) {
_checkLimitValue(_newLimitsList.clBalanceOraclesErrorUpperBPLimit, 0, MAX_BASIS_POINTS);
emit CLBalanceOraclesErrorUpperBPLimitSet(_newLimitsList.clBalanceOraclesErrorUpperBPLimit);
}
_limits = _newLimitsList.pack();
}
function _checkLimitValue(uint256 _value, uint256 _minAllowedValue, uint256 _maxAllowedValue) internal pure {
if (_value > _maxAllowedValue || _value < _minAllowedValue) {
revert IncorrectLimitValue(_value, _minAllowedValue, _maxAllowedValue);
}
}
event ExitedValidatorsPerDayLimitSet(uint256 exitedValidatorsPerDayLimit);
event AppearedValidatorsPerDayLimitSet(uint256 appearedValidatorsPerDayLimit);
event SecondOpinionOracleChanged(ISecondOpinionOracle indexed secondOpinionOracle);
event AnnualBalanceIncreaseBPLimitSet(uint256 annualBalanceIncreaseBPLimit);
event SimulatedShareRateDeviationBPLimitSet(uint256 simulatedShareRateDeviationBPLimit);
event MaxPositiveTokenRebaseSet(uint256 maxPositiveTokenRebase);
event MaxValidatorExitRequestsPerReportSet(uint256 maxValidatorExitRequestsPerReport);
event MaxItemsPerExtraDataTransactionSet(uint256 maxItemsPerExtraDataTransaction);
event MaxNodeOperatorsPerExtraDataItemSet(uint256 maxNodeOperatorsPerExtraDataItem);
event RequestTimestampMarginSet(uint256 requestTimestampMargin);
event InitialSlashingAmountSet(uint256 initialSlashingAmountPWei);
event InactivityPenaltiesAmountSet(uint256 inactivityPenaltiesAmountPWei);
event CLBalanceOraclesErrorUpperBPLimitSet(uint256 clBalanceOraclesErrorUpperBPLimit);
event NegativeCLRebaseConfirmed(uint256 refSlot, uint256 clBalanceWei, uint256 withdrawalVaultBalance);
event NegativeCLRebaseAccepted(uint256 refSlot, uint256 clTotalBalance, uint256 clBalanceDecrease, uint256 maxAllowedCLRebaseNegativeSum);
error IncorrectLimitValue(uint256 value, uint256 minAllowedValue, uint256 maxAllowedValue);
error IncorrectWithdrawalsVaultBalance(uint256 actualWithdrawalVaultBalance);
error IncorrectELRewardsVaultBalance(uint256 actualELRewardsVaultBalance);
error IncorrectSharesRequestedToBurn(uint256 actualSharesToBurn);
error IncorrectCLBalanceIncrease(uint256 annualBalanceDiff);
error IncorrectAppearedValidators(uint256 appearedValidatorsLimit);
error IncorrectNumberOfExitRequestsPerReport(uint256 maxRequestsCount);
error IncorrectExitedValidators(uint256 exitedValidatorsLimit);
error IncorrectRequestFinalization(uint256 requestCreationBlock);
error ActualShareRateIsZero();
error IncorrectSimulatedShareRate(uint256 simulatedShareRate, uint256 actualShareRate);
error TooManyItemsPerExtraDataTransaction(uint256 maxItemsCount, uint256 receivedItemsCount);
error ExitedValidatorsLimitExceeded(uint256 limitPerDay, uint256 exitedPerDay);
error TooManyNodeOpsPerExtraDataItem(uint256 itemIndex, uint256 nodeOpsCount);
error AdminCannotBeZero();
error IncorrectCLBalanceDecrease(uint256 negativeCLRebaseSum, uint256 maxNegativeCLRebaseSum);
error NegativeRebaseFailedCLBalanceMismatch(uint256 reportedValue, uint256 provedValue, uint256 limitBP);
error NegativeRebaseFailedWithdrawalVaultBalanceMismatch(uint256 reportedValue, uint256 provedValue);
error NegativeRebaseFailedSecondOpinionReportIsNotReady();
error CalledNotFromLido();
}
library LimitsListPacker {
function pack(LimitsList memory _limitsList) internal pure returns (LimitsListPacked memory res) {
res.exitedValidatorsPerDayLimit = SafeCast.toUint16(_limitsList.exitedValidatorsPerDayLimit);
res.appearedValidatorsPerDayLimit = SafeCast.toUint16(_limitsList.appearedValidatorsPerDayLimit);
res.annualBalanceIncreaseBPLimit = _toBasisPoints(_limitsList.annualBalanceIncreaseBPLimit);
res.simulatedShareRateDeviationBPLimit = _toBasisPoints(_limitsList.simulatedShareRateDeviationBPLimit);
res.requestTimestampMargin = SafeCast.toUint32(_limitsList.requestTimestampMargin);
res.maxPositiveTokenRebase = SafeCast.toUint64(_limitsList.maxPositiveTokenRebase);
res.maxValidatorExitRequestsPerReport = SafeCast.toUint16(_limitsList.maxValidatorExitRequestsPerReport);
res.maxItemsPerExtraDataTransaction = SafeCast.toUint16(_limitsList.maxItemsPerExtraDataTransaction);
res.maxNodeOperatorsPerExtraDataItem = SafeCast.toUint16(_limitsList.maxNodeOperatorsPerExtraDataItem);
res.initialSlashingAmountPWei = SafeCast.toUint16(_limitsList.initialSlashingAmountPWei);
res.inactivityPenaltiesAmountPWei = SafeCast.toUint16(_limitsList.inactivityPenaltiesAmountPWei);
res.clBalanceOraclesErrorUpperBPLimit = _toBasisPoints(_limitsList.clBalanceOraclesErrorUpperBPLimit);
}
function _toBasisPoints(uint256 _value) private pure returns (uint16) {
require(_value <= MAX_BASIS_POINTS, "BASIS_POINTS_OVERFLOW");
return uint16(_value);
}
}
library LimitsListUnpacker {
function unpack(LimitsListPacked memory _limitsList) internal pure returns (LimitsList memory res) {
res.exitedValidatorsPerDayLimit = _limitsList.exitedValidatorsPerDayLimit;
res.appearedValidatorsPerDayLimit = _limitsList.appearedValidatorsPerDayLimit;
res.annualBalanceIncreaseBPLimit = _limitsList.annualBalanceIncreaseBPLimit;
res.simulatedShareRateDeviationBPLimit = _limitsList.simulatedShareRateDeviationBPLimit;
res.requestTimestampMargin = _limitsList.requestTimestampMargin;
res.maxPositiveTokenRebase = _limitsList.maxPositiveTokenRebase;
res.maxValidatorExitRequestsPerReport = _limitsList.maxValidatorExitRequestsPerReport;
res.maxItemsPerExtraDataTransaction = _limitsList.maxItemsPerExtraDataTransaction;
res.maxNodeOperatorsPerExtraDataItem = _limitsList.maxNodeOperatorsPerExtraDataItem;
res.initialSlashingAmountPWei = _limitsList.initialSlashingAmountPWei;
res.inactivityPenaltiesAmountPWei = _limitsList.inactivityPenaltiesAmountPWei;
res.clBalanceOraclesErrorUpperBPLimit = _limitsList.clBalanceOraclesErrorUpperBPLimit;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol)
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
return int128(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
return int64(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
return int32(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
// See contracts/COMPILERS.md
pragma solidity 0.8.9;
import {MemUtils} from "../common/lib/MemUtils.sol";
interface IDepositContract {
function get_deposit_root() external view returns (bytes32 rootHash);
function deposit(
bytes calldata pubkey, // 48 bytes
bytes calldata withdrawal_credentials, // 32 bytes
bytes calldata signature, // 96 bytes
bytes32 deposit_data_root
) external payable;
}
contract BeaconChainDepositor {
uint256 internal constant PUBLIC_KEY_LENGTH = 48;
uint256 internal constant SIGNATURE_LENGTH = 96;
uint256 internal constant DEPOSIT_SIZE = 32 ether;
/// @dev deposit amount 32eth in gweis converted to little endian uint64
/// DEPOSIT_SIZE_IN_GWEI_LE64 = toLittleEndian64(32 ether / 1 gwei)
uint64 internal constant DEPOSIT_SIZE_IN_GWEI_LE64 = 0x0040597307000000;
IDepositContract public immutable DEPOSIT_CONTRACT;
constructor(address _depositContract) {
if (_depositContract == address(0)) revert DepositContractZeroAddress();
DEPOSIT_CONTRACT = IDepositContract(_depositContract);
}
/// @dev Invokes a deposit call to the official Beacon Deposit contract
/// @param _keysCount amount of keys to deposit
/// @param _withdrawalCredentials Commitment to a public key for withdrawals
/// @param _publicKeysBatch A BLS12-381 public keys batch
/// @param _signaturesBatch A BLS12-381 signatures batch
function _makeBeaconChainDeposits32ETH(
uint256 _keysCount,
bytes memory _withdrawalCredentials,
bytes memory _publicKeysBatch,
bytes memory _signaturesBatch
) internal {
if (_publicKeysBatch.length != PUBLIC_KEY_LENGTH * _keysCount) {
revert InvalidPublicKeysBatchLength(_publicKeysBatch.length, PUBLIC_KEY_LENGTH * _keysCount);
}
if (_signaturesBatch.length != SIGNATURE_LENGTH * _keysCount) {
revert InvalidSignaturesBatchLength(_signaturesBatch.length, SIGNATURE_LENGTH * _keysCount);
}
bytes memory publicKey = MemUtils.unsafeAllocateBytes(PUBLIC_KEY_LENGTH);
bytes memory signature = MemUtils.unsafeAllocateBytes(SIGNATURE_LENGTH);
for (uint256 i; i < _keysCount;) {
MemUtils.copyBytes(_publicKeysBatch, publicKey, i * PUBLIC_KEY_LENGTH, 0, PUBLIC_KEY_LENGTH);
MemUtils.copyBytes(_signaturesBatch, signature, i * SIGNATURE_LENGTH, 0, SIGNATURE_LENGTH);
DEPOSIT_CONTRACT.deposit{value: DEPOSIT_SIZE}(
publicKey, _withdrawalCredentials, signature, _computeDepositDataRoot(_withdrawalCredentials, publicKey, signature)
);
unchecked {
++i;
}
}
}
/// @dev computes the deposit_root_hash required by official Beacon Deposit contract
/// @param _publicKey A BLS12-381 public key.
/// @param _signature A BLS12-381 signature
function _computeDepositDataRoot(bytes memory _withdrawalCredentials, bytes memory _publicKey, bytes memory _signature)
private
pure
returns (bytes32)
{
// Compute deposit data root (`DepositData` hash tree root) according to deposit_contract.sol
bytes memory sigPart1 = MemUtils.unsafeAllocateBytes(64);
bytes memory sigPart2 = MemUtils.unsafeAllocateBytes(SIGNATURE_LENGTH - 64);
MemUtils.copyBytes(_signature, sigPart1, 0, 0, 64);
MemUtils.copyBytes(_signature, sigPart2, 64, 0, SIGNATURE_LENGTH - 64);
bytes32 publicKeyRoot = sha256(abi.encodePacked(_publicKey, bytes16(0)));
bytes32 signatureRoot = sha256(abi.encodePacked(sha256(abi.encodePacked(sigPart1)), sha256(abi.encodePacked(sigPart2, bytes32(0)))));
return sha256(
abi.encodePacked(
sha256(abi.encodePacked(publicKeyRoot, _withdrawalCredentials)),
sha256(abi.encodePacked(DEPOSIT_SIZE_IN_GWEI_LE64, bytes24(0), signatureRoot))
)
);
}
error DepositContractZeroAddress();
error InvalidPublicKeysBatchLength(uint256 actual, uint256 expected);
error InvalidSignaturesBatchLength(uint256 actual, uint256 expected);
}// SPDX-FileCopyrightText: 2024 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.9;
/// @title Second Opinion Oracle interface for Lido. See LIP-23 for details.
interface ISecondOpinionOracle {
/// @notice Returns second opinion report for the given reference slot
/// @param refSlot is a reference slot to return report for
/// @return success shows whether the report was successfully generated
/// @return clBalanceGwei is a balance of the consensus layer in Gwei for the ref slot
/// @return withdrawalVaultBalanceWei is a balance of the withdrawal vault in Wei for the ref slot
/// @return totalDepositedValidators is a total number of validators deposited with Lido
/// @return totalExitedValidators is a total number of Lido validators in the EXITED state
function getReport(uint256 refSlot)
external
view
returns (
bool success,
uint256 clBalanceGwei,
uint256 withdrawalVaultBalanceWei,
uint256 totalDepositedValidators,
uint256 totalExitedValidators
);
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.9;
/// @title Lido's Staking Module interface
interface IStakingModule {
/// @notice Returns the type of the staking module
function getType() external view returns (bytes32);
/// @notice Returns all-validators summary in the staking module
/// @return totalExitedValidators total number of validators in the EXITED state
/// on the Consensus Layer. This value can't decrease in normal conditions
/// @return totalDepositedValidators total number of validators deposited via the
/// official Deposit Contract. This value is a cumulative counter: even when the validator
/// goes into EXITED state this counter is not decreasing
/// @return depositableValidatorsCount number of validators in the set available for deposit
function getStakingModuleSummary() external view returns (
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
);
/// @notice Returns all-validators summary belonging to the node operator with the given id
/// @param _nodeOperatorId id of the operator to return report for
/// @return targetLimitMode shows whether the current target limit applied to the node operator (0 = disabled, 1 = soft mode, 2 = forced mode)
/// @return targetValidatorsCount relative target active validators limit for operator
/// @return stuckValidatorsCount number of validators with an expired request to exit time
/// @return refundedValidatorsCount number of validators that can't be withdrawn, but deposit
/// costs were compensated to the Lido by the node operator
/// @return stuckPenaltyEndTimestamp time when the penalty for stuck validators stops applying
/// to node operator rewards
/// @return totalExitedValidators total number of validators in the EXITED state
/// on the Consensus Layer. This value can't decrease in normal conditions
/// @return totalDepositedValidators total number of validators deposited via the official
/// Deposit Contract. This value is a cumulative counter: even when the validator goes into
/// EXITED state this counter is not decreasing
/// @return depositableValidatorsCount number of validators in the set available for deposit
function getNodeOperatorSummary(uint256 _nodeOperatorId) external view returns (
uint256 targetLimitMode,
uint256 targetValidatorsCount,
uint256 stuckValidatorsCount,
uint256 refundedValidatorsCount,
uint256 stuckPenaltyEndTimestamp,
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
);
/// @notice Returns a counter that MUST change its value whenever the deposit data set changes.
/// Below is the typical list of actions that requires an update of the nonce:
/// 1. a node operator's deposit data is added
/// 2. a node operator's deposit data is removed
/// 3. a node operator's ready-to-deposit data size is changed
/// 4. a node operator was activated/deactivated
/// 5. a node operator's deposit data is used for the deposit
/// Note: Depending on the StakingModule implementation above list might be extended
/// @dev In some scenarios, it's allowed to update nonce without actual change of the deposit
/// data subset, but it MUST NOT lead to the DOS of the staking module via continuous
/// update of the nonce by the malicious actor
function getNonce() external view returns (uint256);
/// @notice Returns total number of node operators
function getNodeOperatorsCount() external view returns (uint256);
/// @notice Returns number of active node operators
function getActiveNodeOperatorsCount() external view returns (uint256);
/// @notice Returns if the node operator with given id is active
/// @param _nodeOperatorId Id of the node operator
function getNodeOperatorIsActive(uint256 _nodeOperatorId) external view returns (bool);
/// @notice Returns up to `_limit` node operator ids starting from the `_offset`. The order of
/// the returned ids is not defined and might change between calls.
/// @dev This view must not revert in case of invalid data passed. When `_offset` exceeds the
/// total node operators count or when `_limit` is equal to 0 MUST be returned empty array.
function getNodeOperatorIds(uint256 _offset, uint256 _limit)
external
view
returns (uint256[] memory nodeOperatorIds);
/// @notice Called by StakingRouter to signal that stETH rewards were minted for this module.
/// @param _totalShares Amount of stETH shares that were minted to reward all node operators.
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onRewardsMinted(uint256 _totalShares) external;
/// @notice Called by StakingRouter to decrease the number of vetted keys for node operator with given id
/// @param _nodeOperatorIds bytes packed array of the node operators id
/// @param _vettedSigningKeysCounts bytes packed array of the new number of vetted keys for the node operators
function decreaseVettedSigningKeysCount(
bytes calldata _nodeOperatorIds,
bytes calldata _vettedSigningKeysCounts
) external;
/// @notice Updates the number of the validators of the given node operator that were requested
/// to exit but failed to do so in the max allowed time
/// @param _nodeOperatorIds bytes packed array of the node operators id
/// @param _stuckValidatorsCounts bytes packed array of the new number of STUCK validators for the node operators
function updateStuckValidatorsCount(
bytes calldata _nodeOperatorIds,
bytes calldata _stuckValidatorsCounts
) external;
/// @notice Updates the number of the validators in the EXITED state for node operator with given id
/// @param _nodeOperatorIds bytes packed array of the node operators id
/// @param _exitedValidatorsCounts bytes packed array of the new number of EXITED validators for the node operators
function updateExitedValidatorsCount(
bytes calldata _nodeOperatorIds,
bytes calldata _exitedValidatorsCounts
) external;
/// @notice Updates the number of the refunded validators for node operator with the given id
/// @param _nodeOperatorId Id of the node operator
/// @param _refundedValidatorsCount New number of refunded validators of the node operator
function updateRefundedValidatorsCount(uint256 _nodeOperatorId, uint256 _refundedValidatorsCount) external;
/// @notice Updates the limit of the validators that can be used for deposit
/// @param _nodeOperatorId Id of the node operator
/// @param _targetLimitMode target limit mode
/// @param _targetLimit Target limit of the node operator
function updateTargetValidatorsLimits(
uint256 _nodeOperatorId,
uint256 _targetLimitMode,
uint256 _targetLimit
) external;
/// @notice Unsafely updates the number of validators in the EXITED/STUCK states for node operator with given id
/// 'unsafely' means that this method can both increase and decrease exited and stuck counters
/// @param _nodeOperatorId Id of the node operator
/// @param _exitedValidatorsCount New number of EXITED validators for the node operator
/// @param _stuckValidatorsCount New number of STUCK validator for the node operator
function unsafeUpdateValidatorsCount(
uint256 _nodeOperatorId,
uint256 _exitedValidatorsCount,
uint256 _stuckValidatorsCount
) external;
/// @notice Obtains deposit data to be used by StakingRouter to deposit to the Ethereum Deposit
/// contract
/// @dev The method MUST revert when the staking module has not enough deposit data items
/// @param _depositsCount Number of deposits to be done
/// @param _depositCalldata Staking module defined data encoded as bytes.
/// IMPORTANT: _depositCalldata MUST NOT modify the deposit data set of the staking module
/// @return publicKeys Batch of the concatenated public validators keys
/// @return signatures Batch of the concatenated deposit signatures for returned public keys
function obtainDepositData(uint256 _depositsCount, bytes calldata _depositCalldata)
external
returns (bytes memory publicKeys, bytes memory signatures);
/// @notice Called by StakingRouter after it finishes updating exited and stuck validators
/// counts for this module's node operators.
///
/// Guaranteed to be called after an oracle report is applied, regardless of whether any node
/// operator in this module has actually received any updated counts as a result of the report
/// but given that the total number of exited validators returned from getStakingModuleSummary
/// is the same as StakingRouter expects based on the total count received from the oracle.
///
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onExitedAndStuckValidatorsCountsUpdated() external;
/// @notice Called by StakingRouter when withdrawal credentials are changed.
/// @dev This method MUST discard all StakingModule's unused deposit data cause they become
/// invalid after the withdrawal credentials are changed
///
/// @dev IMPORTANT: this method SHOULD revert with empty error data ONLY because of "out of gas".
/// Details about error data: https://docs.soliditylang.org/en/v0.8.9/control-structures.html#error-handling-assert-require-revert-and-exceptions
function onWithdrawalCredentialsChanged() external;
/// @dev Event to be emitted on StakingModule's nonce change
event NonceChanged(uint256 nonce);
/// @dev Event to be emitted when a signing key is added to the StakingModule
event SigningKeyAdded(uint256 indexed nodeOperatorId, bytes pubkey);
/// @dev Event to be emitted when a signing key is removed from the StakingModule
event SigningKeyRemoved(uint256 indexed nodeOperatorId, bytes pubkey);
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import {Math256} from "../../common/lib/Math256.sol";
/**
* This library implements positive rebase limiter for `stETH` token.
* One needs to initialize `LimiterState` with the desired parameters:
* - _rebaseLimit (limiter max value, nominated in LIMITER_PRECISION_BASE)
* - _preTotalPooledEther (see `Lido.getTotalPooledEther()`), pre-rebase value
* - _preTotalShares (see `Lido.getTotalShares()`), pre-rebase value
*
* The limiter allows to account for:
* - consensus layer balance updates (can be either positive or negative)
* - total pooled ether changes (withdrawing funds from vaults on execution layer)
* - total shares changes (burning due to coverage, NOR penalization, withdrawals finalization, etc.)
*/
/**
* @dev Internal limiter representation struct (storing in memory)
*/
struct TokenRebaseLimiterData {
uint256 preTotalPooledEther; // pre-rebase total pooled ether
uint256 preTotalShares; // pre-rebase total shares
uint256 currentTotalPooledEther; // intermediate total pooled ether amount while token rebase is in progress
uint256 positiveRebaseLimit; // positive rebase limit (target value) with 1e9 precision (`LIMITER_PRECISION_BASE`)
uint256 maxTotalPooledEther; // maximum total pooled ether that still fits into the positive rebase limit (cached)
}
/**
*
* Two-steps flow: account for total supply changes and then determine the shares allowed to be burnt.
*
* Conventions:
* R - token rebase limit (i.e, {postShareRate / preShareRate - 1} <= R);
* inc - total pooled ether increase;
* dec - total shares decrease.
*
* ### Step 1. Calculating the allowed total pooled ether changes (preTotalShares === postTotalShares)
* Used for `PositiveTokenRebaseLimiter.increaseEther()`, `PositiveTokenRebaseLimiter.decreaseEther()`.
*
* R = ((preTotalPooledEther + inc) / preTotalShares) / (preTotalPooledEther / preTotalShares) - 1
* = ((preTotalPooledEther + inc) / preTotalShares) * (preTotalShares / preTotalPooledEther) - 1
* = (preTotalPooledEther + inc) / preTotalPooledEther) - 1
* = inc/preTotalPooledEther
*
* isolating inc:
*
* ``` inc = R * preTotalPooledEther ```
*
* ### Step 2. Calculating the allowed to burn shares (preTotalPooledEther != currentTotalPooledEther)
* Used for `PositiveTokenRebaseLimiter.getSharesToBurnLimit()`.
*
* R = (currentTotalPooledEther / (preTotalShares - dec)) / (preTotalPooledEther / preTotalShares) - 1,
* let X = currentTotalPooledEther / preTotalPooledEther
*
* then:
* R = X * (preTotalShares / (preTotalShares - dec)) - 1, or
* (R+1) * (preTotalShares - dec) = X * preTotalShares
*
* isolating dec:
* dec * (R + 1) = (R + 1 - X) * preTotalShares =>
*
* ``` dec = preTotalShares * (R + 1 - currentTotalPooledEther/preTotalPooledEther) / (R + 1) ```
*
*/
library PositiveTokenRebaseLimiter {
/// @dev Precision base for the limiter (e.g.: 1e6 - 0.1%; 1e9 - 100%)
uint256 public constant LIMITER_PRECISION_BASE = 10**9;
/// @dev Disabled limit
uint256 public constant UNLIMITED_REBASE = type(uint64).max;
/**
* @dev Initialize the new `LimiterState` structure instance
* @param _rebaseLimit max limiter value (saturation point), see `LIMITER_PRECISION_BASE`
* @param _preTotalPooledEther pre-rebase total pooled ether, see `Lido.getTotalPooledEther()`
* @param _preTotalShares pre-rebase total shares, see `Lido.getTotalShares()`
* @return limiterState newly initialized limiter structure
*/
function initLimiterState(
uint256 _rebaseLimit,
uint256 _preTotalPooledEther,
uint256 _preTotalShares
) internal pure returns (TokenRebaseLimiterData memory limiterState) {
if (_rebaseLimit == 0) revert TooLowTokenRebaseLimit();
if (_rebaseLimit > UNLIMITED_REBASE) revert TooHighTokenRebaseLimit();
// special case
if (_preTotalPooledEther == 0) { _rebaseLimit = UNLIMITED_REBASE; }
limiterState.currentTotalPooledEther = limiterState.preTotalPooledEther = _preTotalPooledEther;
limiterState.preTotalShares = _preTotalShares;
limiterState.positiveRebaseLimit = _rebaseLimit;
limiterState.maxTotalPooledEther = (_rebaseLimit == UNLIMITED_REBASE)
? type(uint256).max
: limiterState.preTotalPooledEther
+ (limiterState.positiveRebaseLimit * limiterState.preTotalPooledEther) / LIMITER_PRECISION_BASE;
}
/**
* @notice check if positive rebase limit is reached
* @param _limiterState limit repr struct
* @return true if limit is reached
*/
function isLimitReached(TokenRebaseLimiterData memory _limiterState) internal pure returns (bool) {
return _limiterState.currentTotalPooledEther >= _limiterState.maxTotalPooledEther;
}
/**
* @notice decrease total pooled ether by the given amount of ether
* @param _limiterState limit repr struct
* @param _etherAmount amount of ether to decrease
*/
function decreaseEther(
TokenRebaseLimiterData memory _limiterState, uint256 _etherAmount
) internal pure {
if (_limiterState.positiveRebaseLimit == UNLIMITED_REBASE) return;
if (_etherAmount > _limiterState.currentTotalPooledEther) revert NegativeTotalPooledEther();
_limiterState.currentTotalPooledEther -= _etherAmount;
}
/**
* @dev increase total pooled ether up to the limit and return the consumed value (not exceeding the limit)
* @param _limiterState limit repr struct
* @param _etherAmount desired ether addition
* @return consumedEther appended ether still not exceeding the limit
*/
function increaseEther(
TokenRebaseLimiterData memory _limiterState, uint256 _etherAmount
)
internal
pure
returns (uint256 consumedEther)
{
if (_limiterState.positiveRebaseLimit == UNLIMITED_REBASE) return _etherAmount;
uint256 prevPooledEther = _limiterState.currentTotalPooledEther;
_limiterState.currentTotalPooledEther += _etherAmount;
_limiterState.currentTotalPooledEther
= Math256.min(_limiterState.currentTotalPooledEther, _limiterState.maxTotalPooledEther);
assert(_limiterState.currentTotalPooledEther >= prevPooledEther);
return _limiterState.currentTotalPooledEther - prevPooledEther;
}
/**
* @dev return shares to burn value not exceeding the limit
* @param _limiterState limit repr struct
* @return maxSharesToBurn allowed to deduct shares to not exceed the limit
*/
function getSharesToBurnLimit(TokenRebaseLimiterData memory _limiterState)
internal
pure
returns (uint256 maxSharesToBurn)
{
if (_limiterState.positiveRebaseLimit == UNLIMITED_REBASE) return _limiterState.preTotalShares;
if (isLimitReached(_limiterState)) return 0;
uint256 rebaseLimitPlus1 = _limiterState.positiveRebaseLimit + LIMITER_PRECISION_BASE;
uint256 pooledEtherRate =
(_limiterState.currentTotalPooledEther * LIMITER_PRECISION_BASE) / _limiterState.preTotalPooledEther;
maxSharesToBurn = (_limiterState.preTotalShares * (rebaseLimitPlus1 - pooledEtherRate)) / rebaseLimitPlus1;
}
error TooLowTokenRebaseLimit();
error TooHighTokenRebaseLimit();
error NegativeTotalPooledEther();
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.9;
/**
* @notice Aragon Unstructured Storage library
*/
library UnstructuredStorage {
function getStorageBool(bytes32 position) internal view returns (bool data) {
assembly { data := sload(position) }
}
function getStorageAddress(bytes32 position) internal view returns (address data) {
assembly { data := sload(position) }
}
function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
assembly { data := sload(position) }
}
function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
assembly { data := sload(position) }
}
function setStorageBool(bytes32 position, bool data) internal {
assembly { sstore(position, data) }
}
function setStorageAddress(bytes32 position, address data) internal {
assembly { sstore(position, data) }
}
function setStorageBytes32(bytes32 position, bytes32 data) internal {
assembly { sstore(position, data) }
}
function setStorageUint256(bytes32 position, uint256 data) internal {
assembly { sstore(position, data) }
}
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import {AccessControlEnumerable} from "./utils/access/AccessControlEnumerable.sol";
import {IStakingModule} from "./interfaces/IStakingModule.sol";
import {Math256} from "../common/lib/Math256.sol";
import {UnstructuredStorage} from "./lib/UnstructuredStorage.sol";
import {MinFirstAllocationStrategy} from "../common/lib/MinFirstAllocationStrategy.sol";
import {BeaconChainDepositor} from "./BeaconChainDepositor.sol";
import {Versioned} from "./utils/Versioned.sol";
contract StakingRouter is AccessControlEnumerable, BeaconChainDepositor, Versioned {
using UnstructuredStorage for bytes32;
/// @dev Events
event StakingModuleAdded(uint256 indexed stakingModuleId, address stakingModule, string name, address createdBy);
event StakingModuleShareLimitSet(uint256 indexed stakingModuleId, uint256 stakeShareLimit, uint256 priorityExitShareThreshold, address setBy);
event StakingModuleFeesSet(uint256 indexed stakingModuleId, uint256 stakingModuleFee, uint256 treasuryFee, address setBy);
event StakingModuleStatusSet(uint256 indexed stakingModuleId, StakingModuleStatus status, address setBy);
event StakingModuleExitedValidatorsIncompleteReporting(uint256 indexed stakingModuleId, uint256 unreportedExitedValidatorsCount);
event StakingModuleMaxDepositsPerBlockSet(
uint256 indexed stakingModuleId, uint256 maxDepositsPerBlock, address setBy
);
event StakingModuleMinDepositBlockDistanceSet(
uint256 indexed stakingModuleId, uint256 minDepositBlockDistance, address setBy
);
event WithdrawalCredentialsSet(bytes32 withdrawalCredentials, address setBy);
event WithdrawalsCredentialsChangeFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
event ExitedAndStuckValidatorsCountsUpdateFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
event RewardsMintedReportFailed(uint256 indexed stakingModuleId, bytes lowLevelRevertData);
/// Emitted when the StakingRouter received ETH
event StakingRouterETHDeposited(uint256 indexed stakingModuleId, uint256 amount);
/// @dev Errors
error ZeroAddressLido();
error ZeroAddressAdmin();
error ZeroAddressStakingModule();
error InvalidStakeShareLimit();
error InvalidFeeSum();
error StakingModuleNotActive();
error EmptyWithdrawalsCredentials();
error DirectETHTransfer();
error InvalidReportData(uint256 code);
error ExitedValidatorsCountCannotDecrease();
error ReportedExitedValidatorsExceedDeposited(
uint256 reportedExitedValidatorsCount,
uint256 depositedValidatorsCount
);
error StakingModulesLimitExceeded();
error StakingModuleUnregistered();
error AppAuthLidoFailed();
error StakingModuleStatusTheSame();
error StakingModuleWrongName();
error UnexpectedCurrentValidatorsCount(
uint256 currentModuleExitedValidatorsCount,
uint256 currentNodeOpExitedValidatorsCount,
uint256 currentNodeOpStuckValidatorsCount
);
error UnexpectedFinalExitedValidatorsCount (
uint256 newModuleTotalExitedValidatorsCount,
uint256 newModuleTotalExitedValidatorsCountInStakingRouter
);
error InvalidDepositsValue(uint256 etherValue, uint256 depositsCount);
error StakingModuleAddressExists();
error ArraysLengthMismatch(uint256 firstArrayLength, uint256 secondArrayLength);
error UnrecoverableModuleError();
error InvalidPriorityExitShareThreshold();
error InvalidMinDepositBlockDistance();
error InvalidMaxDepositPerBlockValue();
enum StakingModuleStatus {
Active, // deposits and rewards allowed
DepositsPaused, // deposits NOT allowed, rewards allowed
Stopped // deposits and rewards NOT allowed
}
struct StakingModule {
/// @notice Unique id of the staking module.
uint24 id;
/// @notice Address of the staking module.
address stakingModuleAddress;
/// @notice Part of the fee taken from staking rewards that goes to the staking module.
uint16 stakingModuleFee;
/// @notice Part of the fee taken from staking rewards that goes to the treasury.
uint16 treasuryFee;
/// @notice Maximum stake share that can be allocated to a module, in BP.
/// @dev Formerly known as `targetShare`.
uint16 stakeShareLimit;
/// @notice Staking module status if staking module can not accept the deposits or can
/// participate in further reward distribution.
uint8 status;
/// @notice Name of the staking module.
string name;
/// @notice block.timestamp of the last deposit of the staking module.
/// @dev NB: lastDepositAt gets updated even if the deposit value was 0 and no actual deposit happened.
uint64 lastDepositAt;
/// @notice block.number of the last deposit of the staking module.
/// @dev NB: lastDepositBlock gets updated even if the deposit value was 0 and no actual deposit happened.
uint256 lastDepositBlock;
/// @notice Number of exited validators.
uint256 exitedValidatorsCount;
/// @notice Module's share threshold, upon crossing which, exits of validators from the module will be prioritized, in BP.
uint16 priorityExitShareThreshold;
/// @notice The maximum number of validators that can be deposited in a single block.
/// @dev Must be harmonized with `OracleReportSanityChecker.appearedValidatorsPerDayLimit`.
/// See docs for the `OracleReportSanityChecker.setAppearedValidatorsPerDayLimit` function.
uint64 maxDepositsPerBlock;
/// @notice The minimum distance between deposits in blocks.
/// @dev Must be harmonized with `OracleReportSanityChecker.appearedValidatorsPerDayLimit`.
/// See docs for the `OracleReportSanityChecker.setAppearedValidatorsPerDayLimit` function).
uint64 minDepositBlockDistance;
}
struct StakingModuleCache {
address stakingModuleAddress;
uint24 stakingModuleId;
uint16 stakingModuleFee;
uint16 treasuryFee;
uint16 stakeShareLimit;
StakingModuleStatus status;
uint256 activeValidatorsCount;
uint256 availableValidatorsCount;
}
bytes32 public constant MANAGE_WITHDRAWAL_CREDENTIALS_ROLE = keccak256("MANAGE_WITHDRAWAL_CREDENTIALS_ROLE");
bytes32 public constant STAKING_MODULE_MANAGE_ROLE = keccak256("STAKING_MODULE_MANAGE_ROLE");
bytes32 public constant STAKING_MODULE_UNVETTING_ROLE = keccak256("STAKING_MODULE_UNVETTING_ROLE");
bytes32 public constant REPORT_EXITED_VALIDATORS_ROLE = keccak256("REPORT_EXITED_VALIDATORS_ROLE");
bytes32 public constant UNSAFE_SET_EXITED_VALIDATORS_ROLE = keccak256("UNSAFE_SET_EXITED_VALIDATORS_ROLE");
bytes32 public constant REPORT_REWARDS_MINTED_ROLE = keccak256("REPORT_REWARDS_MINTED_ROLE");
bytes32 internal constant LIDO_POSITION = keccak256("lido.StakingRouter.lido");
/// @dev Credentials to withdraw ETH on Consensus Layer side.
bytes32 internal constant WITHDRAWAL_CREDENTIALS_POSITION = keccak256("lido.StakingRouter.withdrawalCredentials");
/// @dev Total count of staking modules.
bytes32 internal constant STAKING_MODULES_COUNT_POSITION = keccak256("lido.StakingRouter.stakingModulesCount");
/// @dev Id of the last added staking module. This counter grow on staking modules adding.
bytes32 internal constant LAST_STAKING_MODULE_ID_POSITION = keccak256("lido.StakingRouter.lastStakingModuleId");
/// @dev Mapping is used instead of array to allow to extend the StakingModule.
bytes32 internal constant STAKING_MODULES_MAPPING_POSITION = keccak256("lido.StakingRouter.stakingModules");
/// @dev Position of the staking modules in the `_stakingModules` map, plus 1 because
/// index 0 means a value is not in the set.
bytes32 internal constant STAKING_MODULE_INDICES_MAPPING_POSITION = keccak256("lido.StakingRouter.stakingModuleIndicesOneBased");
uint256 public constant FEE_PRECISION_POINTS = 10 ** 20; // 100 * 10 ** 18
uint256 public constant TOTAL_BASIS_POINTS = 10000;
uint256 public constant MAX_STAKING_MODULES_COUNT = 32;
/// @dev Restrict the name size with 31 bytes to storage in a single slot.
uint256 public constant MAX_STAKING_MODULE_NAME_LENGTH = 31;
constructor(address _depositContract) BeaconChainDepositor(_depositContract) {}
/// @notice Initializes the contract.
/// @param _admin Lido DAO Aragon agent contract address.
/// @param _lido Lido address.
/// @param _withdrawalCredentials Credentials to withdraw ETH on Consensus Layer side.
/// @dev Proxy initialization method.
function initialize(address _admin, address _lido, bytes32 _withdrawalCredentials) external {
if (_admin == address(0)) revert ZeroAddressAdmin();
if (_lido == address(0)) revert ZeroAddressLido();
_initializeContractVersionTo(2);
_setupRole(DEFAULT_ADMIN_ROLE, _admin);
LIDO_POSITION.setStorageAddress(_lido);
WITHDRAWAL_CREDENTIALS_POSITION.setStorageBytes32(_withdrawalCredentials);
emit WithdrawalCredentialsSet(_withdrawalCredentials, msg.sender);
}
/// @dev Prohibit direct transfer to contract.
receive() external payable {
revert DirectETHTransfer();
}
/// @notice Finalizes upgrade to v2 (from v1). Can be called only once.
/// @param _priorityExitShareThresholds Array of priority exit share thresholds.
/// @param _maxDepositsPerBlock Array of max deposits per block.
/// @param _minDepositBlockDistances Array of min deposit block distances.
/// @dev https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-10.md
function finalizeUpgrade_v2(
uint256[] memory _priorityExitShareThresholds,
uint256[] memory _maxDepositsPerBlock,
uint256[] memory _minDepositBlockDistances
) external {
_checkContractVersion(1);
uint256 stakingModulesCount = getStakingModulesCount();
_validateEqualArrayLengths(stakingModulesCount, _priorityExitShareThresholds.length);
_validateEqualArrayLengths(stakingModulesCount, _maxDepositsPerBlock.length);
_validateEqualArrayLengths(stakingModulesCount, _minDepositBlockDistances.length);
for (uint256 i; i < stakingModulesCount; ) {
StakingModule storage stakingModule = _getStakingModuleByIndex(i);
_updateStakingModule(
stakingModule,
stakingModule.id,
stakingModule.stakeShareLimit,
_priorityExitShareThresholds[i],
stakingModule.stakingModuleFee,
stakingModule.treasuryFee,
_maxDepositsPerBlock[i],
_minDepositBlockDistances[i]
);
unchecked {
++i;
}
}
_updateContractVersion(2);
}
/// @notice Returns Lido contract address.
/// @return Lido contract address.
function getLido() public view returns (address) {
return LIDO_POSITION.getStorageAddress();
}
/// @notice Registers a new staking module.
/// @param _name Name of staking module.
/// @param _stakingModuleAddress Address of staking module.
/// @param _stakeShareLimit Maximum share that can be allocated to a module.
/// @param _priorityExitShareThreshold Module's priority exit share threshold.
/// @param _stakingModuleFee Fee of the staking module taken from the staking rewards.
/// @param _treasuryFee Treasury fee.
/// @param _maxDepositsPerBlock The maximum number of validators that can be deposited in a single block.
/// @param _minDepositBlockDistance The minimum distance between deposits in blocks.
/// @dev The function is restricted to the `STAKING_MODULE_MANAGE_ROLE` role.
function addStakingModule(
string calldata _name,
address _stakingModuleAddress,
uint256 _stakeShareLimit,
uint256 _priorityExitShareThreshold,
uint256 _stakingModuleFee,
uint256 _treasuryFee,
uint256 _maxDepositsPerBlock,
uint256 _minDepositBlockDistance
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
if (_stakingModuleAddress == address(0)) revert ZeroAddressStakingModule();
if (bytes(_name).length == 0 || bytes(_name).length > MAX_STAKING_MODULE_NAME_LENGTH) revert StakingModuleWrongName();
uint256 newStakingModuleIndex = getStakingModulesCount();
if (newStakingModuleIndex >= MAX_STAKING_MODULES_COUNT)
revert StakingModulesLimitExceeded();
for (uint256 i; i < newStakingModuleIndex; ) {
if (_stakingModuleAddress == _getStakingModuleByIndex(i).stakingModuleAddress)
revert StakingModuleAddressExists();
unchecked {
++i;
}
}
StakingModule storage newStakingModule = _getStakingModuleByIndex(newStakingModuleIndex);
uint24 newStakingModuleId = uint24(LAST_STAKING_MODULE_ID_POSITION.getStorageUint256()) + 1;
newStakingModule.id = newStakingModuleId;
newStakingModule.name = _name;
newStakingModule.stakingModuleAddress = _stakingModuleAddress;
/// @dev Since `enum` is `uint8` by nature, so the `status` is stored as `uint8` to avoid
/// possible problems when upgrading. But for human readability, we use `enum` as
/// function parameter type. More about conversion in the docs:
/// https://docs.soliditylang.org/en/v0.8.17/types.html#enums
newStakingModule.status = uint8(StakingModuleStatus.Active);
/// @dev Simulate zero value deposit to prevent real deposits into the new StakingModule via
/// DepositSecurityModule just after the addition.
_updateModuleLastDepositState(newStakingModule, newStakingModuleId, 0);
_setStakingModuleIndexById(newStakingModuleId, newStakingModuleIndex);
LAST_STAKING_MODULE_ID_POSITION.setStorageUint256(newStakingModuleId);
STAKING_MODULES_COUNT_POSITION.setStorageUint256(newStakingModuleIndex + 1);
emit StakingModuleAdded(newStakingModuleId, _stakingModuleAddress, _name, msg.sender);
_updateStakingModule(
newStakingModule,
newStakingModuleId,
_stakeShareLimit,
_priorityExitShareThreshold,
_stakingModuleFee,
_treasuryFee,
_maxDepositsPerBlock,
_minDepositBlockDistance
);
}
/// @notice Updates staking module params.
/// @param _stakingModuleId Staking module id.
/// @param _stakeShareLimit Target total stake share.
/// @param _priorityExitShareThreshold Module's priority exit share threshold.
/// @param _stakingModuleFee Fee of the staking module taken from the staking rewards.
/// @param _treasuryFee Treasury fee.
/// @param _maxDepositsPerBlock The maximum number of validators that can be deposited in a single block.
/// @param _minDepositBlockDistance The minimum distance between deposits in blocks.
/// @dev The function is 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 onlyRole(STAKING_MODULE_MANAGE_ROLE) {
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
_updateStakingModule(
stakingModule,
_stakingModuleId,
_stakeShareLimit,
_priorityExitShareThreshold,
_stakingModuleFee,
_treasuryFee,
_maxDepositsPerBlock,
_minDepositBlockDistance
);
}
function _updateStakingModule(
StakingModule storage stakingModule,
uint256 _stakingModuleId,
uint256 _stakeShareLimit,
uint256 _priorityExitShareThreshold,
uint256 _stakingModuleFee,
uint256 _treasuryFee,
uint256 _maxDepositsPerBlock,
uint256 _minDepositBlockDistance
) internal {
if (_stakeShareLimit > TOTAL_BASIS_POINTS) revert InvalidStakeShareLimit();
if (_priorityExitShareThreshold > TOTAL_BASIS_POINTS) revert InvalidPriorityExitShareThreshold();
if (_stakeShareLimit > _priorityExitShareThreshold) revert InvalidPriorityExitShareThreshold();
if (_stakingModuleFee + _treasuryFee > TOTAL_BASIS_POINTS) revert InvalidFeeSum();
if (_minDepositBlockDistance == 0 || _minDepositBlockDistance > type(uint64).max) revert InvalidMinDepositBlockDistance();
if (_maxDepositsPerBlock > type(uint64).max) revert InvalidMaxDepositPerBlockValue();
stakingModule.stakeShareLimit = uint16(_stakeShareLimit);
stakingModule.priorityExitShareThreshold = uint16(_priorityExitShareThreshold);
stakingModule.treasuryFee = uint16(_treasuryFee);
stakingModule.stakingModuleFee = uint16(_stakingModuleFee);
stakingModule.maxDepositsPerBlock = uint64(_maxDepositsPerBlock);
stakingModule.minDepositBlockDistance = uint64(_minDepositBlockDistance);
emit StakingModuleShareLimitSet(_stakingModuleId, _stakeShareLimit, _priorityExitShareThreshold, msg.sender);
emit StakingModuleFeesSet(_stakingModuleId, _stakingModuleFee, _treasuryFee, msg.sender);
emit StakingModuleMaxDepositsPerBlockSet(_stakingModuleId, _maxDepositsPerBlock, msg.sender);
emit StakingModuleMinDepositBlockDistanceSet(_stakingModuleId, _minDepositBlockDistance, msg.sender);
}
/// @notice Updates the limit of the validators that can be used for deposit.
/// @param _stakingModuleId Id of the staking module.
/// @param _nodeOperatorId Id of the node operator.
/// @param _targetLimitMode Target limit mode.
/// @param _targetLimit Target limit of the node operator.
/// @dev The function is restricted to the `STAKING_MODULE_MANAGE_ROLE` role.
function updateTargetValidatorsLimits(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
uint256 _targetLimitMode,
uint256 _targetLimit
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
_getIStakingModuleById(_stakingModuleId).updateTargetValidatorsLimits(
_nodeOperatorId, _targetLimitMode, _targetLimit
);
}
/// @notice Updates the number of the refunded validators in the staking module with the given
/// node operator id.
/// @param _stakingModuleId Id of the staking module.
/// @param _nodeOperatorId Id of the node operator.
/// @param _refundedValidatorsCount New number of refunded validators of the node operator.
/// @dev The function is restricted to the `STAKING_MODULE_MANAGE_ROLE` role.
function updateRefundedValidatorsCount(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
uint256 _refundedValidatorsCount
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
_getIStakingModuleById(_stakingModuleId).updateRefundedValidatorsCount(
_nodeOperatorId, _refundedValidatorsCount
);
}
/// @notice Reports the minted rewards to the staking modules with the specified ids.
/// @param _stakingModuleIds Ids of the staking modules.
/// @param _totalShares Total shares minted for the staking modules.
/// @dev The function is restricted to the `REPORT_REWARDS_MINTED_ROLE` role.
function reportRewardsMinted(uint256[] calldata _stakingModuleIds, uint256[] calldata _totalShares)
external
onlyRole(REPORT_REWARDS_MINTED_ROLE)
{
_validateEqualArrayLengths(_stakingModuleIds.length, _totalShares.length);
for (uint256 i = 0; i < _stakingModuleIds.length; ) {
if (_totalShares[i] > 0) {
try _getIStakingModuleById(_stakingModuleIds[i]).onRewardsMinted(_totalShares[i]) {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onRewardsMinted() reverts because of the
/// "out of gas" error. Here we assume that the onRewardsMinted() method doesn't
/// have reverts with empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
emit RewardsMintedReportFailed(
_stakingModuleIds[i],
lowLevelRevertData
);
}
}
unchecked {
++i;
}
}
}
/// @notice Updates total numbers of exited validators for staking modules with the specified module ids.
/// @param _stakingModuleIds Ids of the staking modules to be updated.
/// @param _exitedValidatorsCounts New counts of exited validators for the specified staking modules.
/// @return The total increase in the aggregate number of exited validators across all updated modules.
///
/// @dev The total numbers are stored in the staking router and can differ from the totals obtained by calling
/// `IStakingModule.getStakingModuleSummary()`. The overall process of updating validator counts is the following:
///
/// 1. In the first data submission phase, the oracle calls `updateExitedValidatorsCountByStakingModule` on the
/// staking router, passing the totals by module. The staking router stores these totals and uses them to
/// distribute new stake and staking fees between the modules. There can only be single call of this function
/// per oracle reporting frame.
///
/// 2. In the first part of the second data submission phase, the oracle calls
/// `StakingRouter.reportStakingModuleStuckValidatorsCountByNodeOperator` on the staking router which passes the
/// counts by node operator to the staking module by calling `IStakingModule.updateStuckValidatorsCount`.
/// This can be done multiple times for the same module, passing data for different subsets of node operators.
///
/// 3. In the second part of the second data submission phase, the oracle calls
/// `StakingRouter.reportStakingModuleExitedValidatorsCountByNodeOperator` on the staking router which passes
/// the counts by node operator to the staking module by calling `IStakingModule.updateExitedValidatorsCount`.
/// This can be done multiple times for the same module, passing data for different subsets of node
/// operators.
///
/// 4. At the end of the second data submission phase, it's expected for the aggregate exited validators count
/// across all module's node operators (stored in the module) to match the total count for this module
/// (stored in the staking router). However, it might happen that the second phase of data submission doesn't
/// finish until the new oracle reporting frame is started, in which case staking router will emit a warning
/// event `StakingModuleExitedValidatorsIncompleteReporting` when the first data submission phase is performed
/// for a new reporting frame. This condition will result in the staking module having an incomplete data about
/// the exited and maybe stuck validator counts during the whole reporting frame. Handling this condition is
/// the responsibility of each staking module.
///
/// 5. When the second reporting phase is finished, i.e. when the oracle submitted the complete data on the stuck
/// and exited validator counts per node operator for the current reporting frame, the oracle calls
/// `StakingRouter.onValidatorsCountsByNodeOperatorReportingFinished` which, in turn, calls
/// `IStakingModule.onExitedAndStuckValidatorsCountsUpdated` on all modules.
///
/// @dev The function is restricted to the `REPORT_EXITED_VALIDATORS_ROLE` role.
function updateExitedValidatorsCountByStakingModule(
uint256[] calldata _stakingModuleIds,
uint256[] calldata _exitedValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
returns (uint256)
{
_validateEqualArrayLengths(_stakingModuleIds.length, _exitedValidatorsCounts.length);
uint256 newlyExitedValidatorsCount;
for (uint256 i = 0; i < _stakingModuleIds.length; ) {
uint256 stakingModuleId = _stakingModuleIds[i];
StakingModule storage stakingModule = _getStakingModuleById(stakingModuleId);
uint256 prevReportedExitedValidatorsCount = stakingModule.exitedValidatorsCount;
if (_exitedValidatorsCounts[i] < prevReportedExitedValidatorsCount) {
revert ExitedValidatorsCountCannotDecrease();
}
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
/* uint256 depositableValidatorsCount */
) = _getStakingModuleSummary(IStakingModule(stakingModule.stakingModuleAddress));
if (_exitedValidatorsCounts[i] > totalDepositedValidators) {
revert ReportedExitedValidatorsExceedDeposited(
_exitedValidatorsCounts[i],
totalDepositedValidators
);
}
newlyExitedValidatorsCount += _exitedValidatorsCounts[i] - prevReportedExitedValidatorsCount;
if (totalExitedValidators < prevReportedExitedValidatorsCount) {
// not all of the exited validators were async reported to the module
emit StakingModuleExitedValidatorsIncompleteReporting(
stakingModuleId,
prevReportedExitedValidatorsCount - totalExitedValidators
);
}
stakingModule.exitedValidatorsCount = _exitedValidatorsCounts[i];
unchecked {
++i;
}
}
return newlyExitedValidatorsCount;
}
/// @notice Updates exited validators counts per node operator for the staking module with
/// the specified id. See the docs for `updateExitedValidatorsCountByStakingModule` for the
/// description of the overall update process.
///
/// @param _stakingModuleId The id of the staking modules to be updated.
/// @param _nodeOperatorIds Ids of the node operators to be updated.
/// @param _exitedValidatorsCounts New counts of exited validators for the specified node operators.
///
/// @dev The function is restricted to the `REPORT_EXITED_VALIDATORS_ROLE` role.
function reportStakingModuleExitedValidatorsCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _exitedValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
_checkValidatorsByNodeOperatorReportData(_nodeOperatorIds, _exitedValidatorsCounts);
_getIStakingModuleById(_stakingModuleId).updateExitedValidatorsCount(_nodeOperatorIds, _exitedValidatorsCounts);
}
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 expected current number of stuck validators of the node operator
/// that is being corrected.
uint256 currentNodeOperatorStuckValidatorsCount;
/// @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;
/// @notice The corrected number of stuck validators of the node operator.
uint256 newNodeOperatorStuckValidatorsCount;
}
/// @notice 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.
///
/// @param _stakingModuleId Id of the staking module.
/// @param _nodeOperatorId Id of the node operator.
/// @param _triggerUpdateFinish Whether to call `onExitedAndStuckValidatorsCountsUpdated` on the module
/// after applying the corrections.
/// @param _correction See the docs for the `ValidatorsCountsCorrection` struct.
///
/// @dev Reverts if the current numbers of exited and stuck validators of the module and node operator
/// don't match the supplied expected current values.
///
/// @dev The function is restricted to the `UNSAFE_SET_EXITED_VALIDATORS_ROLE` role.
function unsafeSetExitedValidatorsCount(
uint256 _stakingModuleId,
uint256 _nodeOperatorId,
bool _triggerUpdateFinish,
ValidatorsCountsCorrection memory _correction
)
external
onlyRole(UNSAFE_SET_EXITED_VALIDATORS_ROLE)
{
StakingModule storage stakingModuleState = _getStakingModuleById(_stakingModuleId);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
(
/* uint256 targetLimitMode */,
/* uint256 targetValidatorsCount */,
uint256 stuckValidatorsCount,
/* uint256 refundedValidatorsCount */,
/* uint256 stuckPenaltyEndTimestamp */,
uint256 totalExitedValidators,
/* uint256 totalDepositedValidators */,
/* uint256 depositableValidatorsCount */
) = stakingModule.getNodeOperatorSummary(_nodeOperatorId);
if (_correction.currentModuleExitedValidatorsCount != stakingModuleState.exitedValidatorsCount ||
_correction.currentNodeOperatorExitedValidatorsCount != totalExitedValidators ||
_correction.currentNodeOperatorStuckValidatorsCount != stuckValidatorsCount
) {
revert UnexpectedCurrentValidatorsCount(
stakingModuleState.exitedValidatorsCount,
totalExitedValidators,
stuckValidatorsCount
);
}
stakingModuleState.exitedValidatorsCount = _correction.newModuleExitedValidatorsCount;
stakingModule.unsafeUpdateValidatorsCount(
_nodeOperatorId,
_correction.newNodeOperatorExitedValidatorsCount,
_correction.newNodeOperatorStuckValidatorsCount
);
(
uint256 moduleTotalExitedValidators,
uint256 moduleTotalDepositedValidators,
) = _getStakingModuleSummary(stakingModule);
if (_correction.newModuleExitedValidatorsCount > moduleTotalDepositedValidators) {
revert ReportedExitedValidatorsExceedDeposited(
_correction.newModuleExitedValidatorsCount,
moduleTotalDepositedValidators
);
}
if (_triggerUpdateFinish) {
if (moduleTotalExitedValidators != _correction.newModuleExitedValidatorsCount) {
revert UnexpectedFinalExitedValidatorsCount(
moduleTotalExitedValidators,
_correction.newModuleExitedValidatorsCount
);
}
stakingModule.onExitedAndStuckValidatorsCountsUpdated();
}
}
/// @notice Updates stuck validators counts per node operator for the staking module with
/// the specified id. See the docs for `updateExitedValidatorsCountByStakingModule` for the
/// description of the overall update process.
///
/// @param _stakingModuleId The id of the staking modules to be updated.
/// @param _nodeOperatorIds Ids of the node operators to be updated.
/// @param _stuckValidatorsCounts New counts of stuck validators for the specified node operators.
///
/// @dev The function is restricted to the `REPORT_EXITED_VALIDATORS_ROLE` role.
function reportStakingModuleStuckValidatorsCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _stuckValidatorsCounts
)
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
_checkValidatorsByNodeOperatorReportData(_nodeOperatorIds, _stuckValidatorsCounts);
_getIStakingModuleById(_stakingModuleId).updateStuckValidatorsCount(_nodeOperatorIds, _stuckValidatorsCounts);
}
/// @notice Finalizes the reporting of the exited and stuck validators counts for the current
/// reporting frame.
///
/// @dev Called by the oracle when the second phase of data reporting finishes, i.e. when the
/// oracle submitted the complete data on the stuck and exited validator counts per node operator
/// for the current reporting frame. See the docs for `updateExitedValidatorsCountByStakingModule`
/// for the description of the overall update process.
///
/// @dev The function is restricted to the `REPORT_EXITED_VALIDATORS_ROLE` role.
function onValidatorsCountsByNodeOperatorReportingFinished()
external
onlyRole(REPORT_EXITED_VALIDATORS_ROLE)
{
uint256 stakingModulesCount = getStakingModulesCount();
StakingModule storage stakingModule;
IStakingModule moduleContract;
for (uint256 i; i < stakingModulesCount; ) {
stakingModule = _getStakingModuleByIndex(i);
moduleContract = IStakingModule(stakingModule.stakingModuleAddress);
(uint256 exitedValidatorsCount, , ) = _getStakingModuleSummary(moduleContract);
if (exitedValidatorsCount == stakingModule.exitedValidatorsCount) {
// oracle finished updating exited validators for all node ops
try moduleContract.onExitedAndStuckValidatorsCountsUpdated() {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onExitedAndStuckValidatorsCountsUpdated()
/// reverts because of the "out of gas" error. Here we assume that the
/// onExitedAndStuckValidatorsCountsUpdated() method doesn't have reverts with
/// empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
emit ExitedAndStuckValidatorsCountsUpdateFailed(
stakingModule.id,
lowLevelRevertData
);
}
}
unchecked {
++i;
}
}
}
/// @notice Decreases vetted signing keys counts per node operator for the staking module with
/// the specified id.
/// @param _stakingModuleId The id of the staking module to be updated.
/// @param _nodeOperatorIds Ids of the node operators to be updated.
/// @param _vettedSigningKeysCounts New counts of vetted signing keys for the specified node operators.
/// @dev The function is restricted to the `STAKING_MODULE_UNVETTING_ROLE` role.
function decreaseStakingModuleVettedKeysCountByNodeOperator(
uint256 _stakingModuleId,
bytes calldata _nodeOperatorIds,
bytes calldata _vettedSigningKeysCounts
) external onlyRole(STAKING_MODULE_UNVETTING_ROLE) {
_checkValidatorsByNodeOperatorReportData(_nodeOperatorIds, _vettedSigningKeysCounts);
_getIStakingModuleById(_stakingModuleId).decreaseVettedSigningKeysCount(_nodeOperatorIds, _vettedSigningKeysCounts);
}
/// @notice Returns all registered staking modules.
/// @return res Array of staking modules.
function getStakingModules() external view returns (StakingModule[] memory res) {
uint256 stakingModulesCount = getStakingModulesCount();
res = new StakingModule[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
res[i] = _getStakingModuleByIndex(i);
unchecked {
++i;
}
}
}
/// @notice Returns the ids of all registered staking modules.
/// @return stakingModuleIds Array of staking module ids.
function getStakingModuleIds() public view returns (uint256[] memory stakingModuleIds) {
uint256 stakingModulesCount = getStakingModulesCount();
stakingModuleIds = new uint256[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
stakingModuleIds[i] = _getStakingModuleByIndex(i).id;
unchecked {
++i;
}
}
}
/// @notice Returns the staking module by its id.
/// @param _stakingModuleId Id of the staking module.
/// @return Staking module data.
function getStakingModule(uint256 _stakingModuleId)
public
view
returns (StakingModule memory)
{
return _getStakingModuleById(_stakingModuleId);
}
/// @notice Returns total number of staking modules.
/// @return Total number of staking modules.
function getStakingModulesCount() public view returns (uint256) {
return STAKING_MODULES_COUNT_POSITION.getStorageUint256();
}
/// @notice Returns true if staking module with the given id was registered via `addStakingModule`, false otherwise.
/// @param _stakingModuleId Id of the staking module.
/// @return True if staking module with the given id was registered, false otherwise.
function hasStakingModule(uint256 _stakingModuleId) external view returns (bool) {
return _getStorageStakingIndicesMapping()[_stakingModuleId] != 0;
}
/// @notice Returns status of staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return Status of the staking module.
function getStakingModuleStatus(uint256 _stakingModuleId)
public
view
returns (StakingModuleStatus)
{
return StakingModuleStatus(_getStakingModuleById(_stakingModuleId).status);
}
/// @notice A summary of the staking module's validators.
struct StakingModuleSummary {
/// @notice The total number of validators in the EXITED state on the Consensus Layer.
/// @dev This value can't decrease in normal conditions.
uint256 totalExitedValidators;
/// @notice The total number of validators deposited via the official Deposit Contract.
/// @dev This value is a cumulative counter: even when the validator goes into EXITED state this
/// counter is not decreasing.
uint256 totalDepositedValidators;
/// @notice The number of validators in the set available for deposit
uint256 depositableValidatorsCount;
}
/// @notice A summary of node operator and its validators.
struct NodeOperatorSummary {
/// @notice Shows whether the current target limit applied to the node operator.
uint256 targetLimitMode;
/// @notice Relative target active validators limit for operator.
uint256 targetValidatorsCount;
/// @notice The number of validators with an expired request to exit time.
uint256 stuckValidatorsCount;
/// @notice The number of validators that can't be withdrawn, but deposit costs were
/// compensated to the Lido by the node operator.
uint256 refundedValidatorsCount;
/// @notice A time when the penalty for stuck validators stops applying to node operator rewards.
uint256 stuckPenaltyEndTimestamp;
/// @notice The total number of validators in the EXITED state on the Consensus Layer.
/// @dev This value can't decrease in normal conditions.
uint256 totalExitedValidators;
/// @notice The total number of validators deposited via the official Deposit Contract.
/// @dev This value is a cumulative counter: even when the validator goes into EXITED state this
/// counter is not decreasing.
uint256 totalDepositedValidators;
/// @notice The number of validators in the set available for deposit.
uint256 depositableValidatorsCount;
}
/// @notice Returns all-validators summary in the staking module.
/// @param _stakingModuleId Id of the staking module to return summary for.
/// @return summary Staking module summary.
function getStakingModuleSummary(uint256 _stakingModuleId)
public
view
returns (StakingModuleSummary memory summary)
{
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleId);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
(
summary.totalExitedValidators,
summary.totalDepositedValidators,
summary.depositableValidatorsCount
) = _getStakingModuleSummary(stakingModule);
}
/// @notice Returns node operator summary from the staking module.
/// @param _stakingModuleId Id of the staking module where node operator is onboarded.
/// @param _nodeOperatorId Id of the node operator to return summary for.
/// @return summary Node operator summary.
function getNodeOperatorSummary(uint256 _stakingModuleId, uint256 _nodeOperatorId)
public
view
returns (NodeOperatorSummary memory summary)
{
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleId);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
/// @dev using intermediate variables below due to "Stack too deep" error in case of
/// assigning directly into the NodeOperatorSummary struct
(
uint256 targetLimitMode,
uint256 targetValidatorsCount,
uint256 stuckValidatorsCount,
uint256 refundedValidatorsCount,
uint256 stuckPenaltyEndTimestamp,
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
) = stakingModule.getNodeOperatorSummary(_nodeOperatorId);
summary.targetLimitMode = targetLimitMode;
summary.targetValidatorsCount = targetValidatorsCount;
summary.stuckValidatorsCount = stuckValidatorsCount;
summary.refundedValidatorsCount = refundedValidatorsCount;
summary.stuckPenaltyEndTimestamp = stuckPenaltyEndTimestamp;
summary.totalExitedValidators = totalExitedValidators;
summary.totalDepositedValidators = totalDepositedValidators;
summary.depositableValidatorsCount = depositableValidatorsCount;
}
/// @notice A collection of the staking module data stored across the StakingRouter and the
/// staking module contract.
///
/// @dev This data, first of all, is designed for off-chain usage and might be redundant for
/// on-chain calls. Give preference for dedicated methods for gas-efficient on-chain calls.
struct StakingModuleDigest {
/// @notice The number of node operators registered in the staking module.
uint256 nodeOperatorsCount;
/// @notice The number of node operators registered in the staking module in active state.
uint256 activeNodeOperatorsCount;
/// @notice The current state of the staking module taken from the StakingRouter.
StakingModule state;
/// @notice A summary of the staking module's validators.
StakingModuleSummary summary;
}
/// @notice A collection of the node operator data stored in the staking module.
/// @dev This data, first of all, is designed for off-chain usage and might be redundant for
/// on-chain calls. Give preference for dedicated methods for gas-efficient on-chain calls.
struct NodeOperatorDigest {
/// @notice Id of the node operator.
uint256 id;
/// @notice Shows whether the node operator is active or not.
bool isActive;
/// @notice A summary of node operator and its validators.
NodeOperatorSummary summary;
}
/// @notice Returns staking module digest for each staking module registered in the staking router.
/// @return Array of staking module digests.
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation.
function getAllStakingModuleDigests() external view returns (StakingModuleDigest[] memory) {
return getStakingModuleDigests(getStakingModuleIds());
}
/// @notice Returns staking module digest for passed staking module ids.
/// @param _stakingModuleIds Ids of the staking modules to return data for.
/// @return digests Array of staking module digests.
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation.
function getStakingModuleDigests(uint256[] memory _stakingModuleIds)
public
view
returns (StakingModuleDigest[] memory digests)
{
digests = new StakingModuleDigest[](_stakingModuleIds.length);
for (uint256 i = 0; i < _stakingModuleIds.length; ) {
StakingModule memory stakingModuleState = getStakingModule(_stakingModuleIds[i]);
IStakingModule stakingModule = IStakingModule(stakingModuleState.stakingModuleAddress);
digests[i] = StakingModuleDigest({
nodeOperatorsCount: stakingModule.getNodeOperatorsCount(),
activeNodeOperatorsCount: stakingModule.getActiveNodeOperatorsCount(),
state: stakingModuleState,
summary: getStakingModuleSummary(_stakingModuleIds[i])
});
unchecked {
++i;
}
}
}
/// @notice Returns node operator digest for each node operator registered in the given staking module.
/// @param _stakingModuleId Id of the staking module to return data for.
/// @return Array of node operator digests.
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation.
function getAllNodeOperatorDigests(uint256 _stakingModuleId) external view returns (NodeOperatorDigest[] memory) {
return getNodeOperatorDigests(
_stakingModuleId, 0, _getIStakingModuleById(_stakingModuleId).getNodeOperatorsCount()
);
}
/// @notice Returns node operator digest for passed node operator ids in the given staking module.
/// @param _stakingModuleId Id of the staking module where node operators registered.
/// @param _offset Node operators offset starting with 0.
/// @param _limit The max number of node operators to return.
/// @return Array of node operator digests.
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation.
function getNodeOperatorDigests(
uint256 _stakingModuleId,
uint256 _offset,
uint256 _limit
) public view returns (NodeOperatorDigest[] memory) {
return getNodeOperatorDigests(
_stakingModuleId, _getIStakingModuleById(_stakingModuleId).getNodeOperatorIds(_offset, _limit)
);
}
/// @notice Returns node operator digest for a slice of node operators registered in the given
/// staking module.
/// @param _stakingModuleId Id of the staking module where node operators registered.
/// @param _nodeOperatorIds Ids of the node operators to return data for.
/// @return digests Array of node operator digests.
/// @dev WARNING: This method is not supposed to be used for onchain calls due to high gas costs
/// for data aggregation.
function getNodeOperatorDigests(uint256 _stakingModuleId, uint256[] memory _nodeOperatorIds)
public
view
returns (NodeOperatorDigest[] memory digests)
{
IStakingModule stakingModule = _getIStakingModuleById(_stakingModuleId);
digests = new NodeOperatorDigest[](_nodeOperatorIds.length);
for (uint256 i = 0; i < _nodeOperatorIds.length; ) {
digests[i] = NodeOperatorDigest({
id: _nodeOperatorIds[i],
isActive: stakingModule.getNodeOperatorIsActive(_nodeOperatorIds[i]),
summary: getNodeOperatorSummary(_stakingModuleId, _nodeOperatorIds[i])
});
unchecked {
++i;
}
}
}
/// @notice Sets the staking module status flag for participation in further deposits and/or reward distribution.
/// @param _stakingModuleId Id of the staking module to be updated.
/// @param _status New status of the staking module.
/// @dev The function is restricted to the `STAKING_MODULE_MANAGE_ROLE` role.
function setStakingModuleStatus(
uint256 _stakingModuleId,
StakingModuleStatus _status
) external onlyRole(STAKING_MODULE_MANAGE_ROLE) {
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) == _status) revert StakingModuleStatusTheSame();
_setStakingModuleStatus(stakingModule, _status);
}
/// @notice Returns whether the staking module is stopped.
/// @param _stakingModuleId Id of the staking module.
/// @return True if the staking module is stopped, false otherwise.
function getStakingModuleIsStopped(uint256 _stakingModuleId) external view returns (bool)
{
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.Stopped;
}
/// @notice Returns whether the deposits are paused for the staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return True if the deposits are paused, false otherwise.
function getStakingModuleIsDepositsPaused(uint256 _stakingModuleId)
external
view
returns (bool)
{
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.DepositsPaused;
}
/// @notice Returns whether the staking module is active.
/// @param _stakingModuleId Id of the staking module.
/// @return True if the staking module is active, false otherwise.
function getStakingModuleIsActive(uint256 _stakingModuleId) external view returns (bool) {
return getStakingModuleStatus(_stakingModuleId) == StakingModuleStatus.Active;
}
/// @notice Returns staking module nonce.
/// @param _stakingModuleId Id of the staking module.
/// @return Staking module nonce.
function getStakingModuleNonce(uint256 _stakingModuleId) external view returns (uint256) {
return _getIStakingModuleById(_stakingModuleId).getNonce();
}
/// @notice Returns the last deposit block for the staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return Last deposit block for the staking module.
function getStakingModuleLastDepositBlock(uint256 _stakingModuleId)
external
view
returns (uint256)
{
return _getStakingModuleById(_stakingModuleId).lastDepositBlock;
}
/// @notice Returns the min deposit block distance for the staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return Min deposit block distance for the staking module.
function getStakingModuleMinDepositBlockDistance(uint256 _stakingModuleId) external view returns (uint256) {
return _getStakingModuleById(_stakingModuleId).minDepositBlockDistance;
}
/// @notice Returns the max deposits count per block for the staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return Max deposits count per block for the staking module.
function getStakingModuleMaxDepositsPerBlock(uint256 _stakingModuleId) external view returns (uint256) {
return _getStakingModuleById(_stakingModuleId).maxDepositsPerBlock;
}
/// @notice Returns active validators count for the staking module.
/// @param _stakingModuleId Id of the staking module.
/// @return activeValidatorsCount Active validators count for the staking module.
function getStakingModuleActiveValidatorsCount(uint256 _stakingModuleId)
external
view
returns (uint256 activeValidatorsCount)
{
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
/* uint256 depositableValidatorsCount */
) = _getStakingModuleSummary(IStakingModule(stakingModule.stakingModuleAddress));
activeValidatorsCount = totalDepositedValidators - Math256.max(
stakingModule.exitedValidatorsCount, totalExitedValidators
);
}
/// @notice Returns the max count of deposits which the staking module can provide data for based
/// on the passed `_maxDepositsValue` amount.
/// @param _stakingModuleId Id of the staking module to be deposited.
/// @param _maxDepositsValue Max amount of ether that might be used for deposits count calculation.
/// @return Max number of deposits might be done using the given staking module.
function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
public
view
returns (uint256)
{
(
/* uint256 allocated */,
uint256[] memory newDepositsAllocation,
StakingModuleCache[] memory stakingModulesCache
) = _getDepositsAllocation(_maxDepositsValue / DEPOSIT_SIZE);
uint256 stakingModuleIndex = _getStakingModuleIndexById(_stakingModuleId);
return
newDepositsAllocation[stakingModuleIndex] - stakingModulesCache[stakingModuleIndex].activeValidatorsCount;
}
/// @notice Returns the aggregate fee distribution proportion.
/// @return modulesFee Modules aggregate fee in base precision.
/// @return treasuryFee Treasury fee in base precision.
/// @return basePrecision Base precision: a value corresponding to the full fee.
function getStakingFeeAggregateDistribution() public view returns (
uint96 modulesFee,
uint96 treasuryFee,
uint256 basePrecision
) {
uint96[] memory moduleFees;
uint96 totalFee;
(, , moduleFees, totalFee, basePrecision) = getStakingRewardsDistribution();
for (uint256 i; i < moduleFees.length; ) {
modulesFee += moduleFees[i];
unchecked {
++i;
}
}
treasuryFee = totalFee - modulesFee;
}
/// @notice Return shares table.
/// @return recipients Rewards recipient addresses corresponding to each module.
/// @return stakingModuleIds Module IDs.
/// @return stakingModuleFees Fee of each recipient.
/// @return totalFee Total fee to mint for each staking module and treasury.
/// @return precisionPoints Base precision number, which constitutes 100% fee.
function getStakingRewardsDistribution()
public
view
returns (
address[] memory recipients,
uint256[] memory stakingModuleIds,
uint96[] memory stakingModuleFees,
uint96 totalFee,
uint256 precisionPoints
)
{
(uint256 totalActiveValidators, StakingModuleCache[] memory stakingModulesCache) = _loadStakingModulesCache();
uint256 stakingModulesCount = stakingModulesCache.length;
/// @dev Return empty response if there are no staking modules or active validators yet.
if (stakingModulesCount == 0 || totalActiveValidators == 0) {
return (new address[](0), new uint256[](0), new uint96[](0), 0, FEE_PRECISION_POINTS);
}
precisionPoints = FEE_PRECISION_POINTS;
stakingModuleIds = new uint256[](stakingModulesCount);
recipients = new address[](stakingModulesCount);
stakingModuleFees = new uint96[](stakingModulesCount);
uint256 rewardedStakingModulesCount = 0;
uint256 stakingModuleValidatorsShare;
uint96 stakingModuleFee;
for (uint256 i; i < stakingModulesCount; ) {
/// @dev Skip staking modules which have no active validators.
if (stakingModulesCache[i].activeValidatorsCount > 0) {
stakingModuleIds[rewardedStakingModulesCount] = stakingModulesCache[i].stakingModuleId;
stakingModuleValidatorsShare = ((stakingModulesCache[i].activeValidatorsCount * precisionPoints) / totalActiveValidators);
recipients[rewardedStakingModulesCount] = address(stakingModulesCache[i].stakingModuleAddress);
stakingModuleFee = uint96((stakingModuleValidatorsShare * stakingModulesCache[i].stakingModuleFee) / TOTAL_BASIS_POINTS);
/// @dev If the staking module has the `Stopped` status for some reason, then
/// the staking module's rewards go to the treasury, so that the DAO has ability
/// to manage them (e.g. to compensate the staking module in case of an error, etc.)
if (stakingModulesCache[i].status != StakingModuleStatus.Stopped) {
stakingModuleFees[rewardedStakingModulesCount] = stakingModuleFee;
}
// Else keep stakingModuleFees[rewardedStakingModulesCount] = 0, but increase totalFee.
totalFee += (uint96((stakingModuleValidatorsShare * stakingModulesCache[i].treasuryFee) / TOTAL_BASIS_POINTS) + stakingModuleFee);
unchecked {
rewardedStakingModulesCount++;
}
}
unchecked {
++i;
}
}
// Total fee never exceeds 100%.
assert(totalFee <= precisionPoints);
/// @dev Shrink arrays.
if (rewardedStakingModulesCount < stakingModulesCount) {
assembly {
mstore(stakingModuleIds, rewardedStakingModulesCount)
mstore(recipients, rewardedStakingModulesCount)
mstore(stakingModuleFees, rewardedStakingModulesCount)
}
}
}
/// @notice Returns the same as getStakingRewardsDistribution() but in reduced, 1e4 precision (DEPRECATED).
/// @dev Helper only for Lido contract. Use getStakingRewardsDistribution() instead.
/// @return totalFee Total fee to mint for each staking module and treasury in reduced, 1e4 precision.
function getTotalFeeE4Precision() external view returns (uint16 totalFee) {
/// @dev The logic is placed here but in Lido contract to save Lido bytecode.
(, , , uint96 totalFeeInHighPrecision, uint256 precision) = getStakingRewardsDistribution();
// Here we rely on (totalFeeInHighPrecision <= precision).
totalFee = _toE4Precision(totalFeeInHighPrecision, precision);
}
/// @notice Returns the same as getStakingFeeAggregateDistribution() but in reduced, 1e4 precision (DEPRECATED).
/// @dev Helper only for Lido contract. Use getStakingFeeAggregateDistribution() instead.
/// @return modulesFee Modules aggregate fee in reduced, 1e4 precision.
/// @return treasuryFee Treasury fee in reduced, 1e4 precision.
function getStakingFeeAggregateDistributionE4Precision()
external view
returns (uint16 modulesFee, uint16 treasuryFee)
{
/// @dev The logic is placed here but in Lido contract to save Lido bytecode.
(
uint256 modulesFeeHighPrecision,
uint256 treasuryFeeHighPrecision,
uint256 precision
) = getStakingFeeAggregateDistribution();
// Here we rely on ({modules,treasury}FeeHighPrecision <= precision).
modulesFee = _toE4Precision(modulesFeeHighPrecision, precision);
treasuryFee = _toE4Precision(treasuryFeeHighPrecision, precision);
}
/// @notice Returns new deposits allocation after the distribution of the `_depositsCount` deposits.
/// @param _depositsCount The maximum number of deposits to be allocated.
/// @return allocated Number of deposits allocated to the staking modules.
/// @return allocations Array of new deposits allocation to the staking modules.
function getDepositsAllocation(uint256 _depositsCount) external view returns (uint256 allocated, uint256[] memory allocations) {
(allocated, allocations, ) = _getDepositsAllocation(_depositsCount);
}
/// @notice Invokes a deposit call to the official Deposit contract.
/// @param _depositsCount Number of deposits to make.
/// @param _stakingModuleId Id of the staking module to be deposited.
/// @param _depositCalldata Staking module calldata.
/// @dev Only the Lido contract is allowed to call this method.
function deposit(
uint256 _depositsCount,
uint256 _stakingModuleId,
bytes calldata _depositCalldata
) external payable {
if (msg.sender != LIDO_POSITION.getStorageAddress()) revert AppAuthLidoFailed();
bytes32 withdrawalCredentials = getWithdrawalCredentials();
if (withdrawalCredentials == 0) revert EmptyWithdrawalsCredentials();
StakingModule storage stakingModule = _getStakingModuleById(_stakingModuleId);
if (StakingModuleStatus(stakingModule.status) != StakingModuleStatus.Active)
revert StakingModuleNotActive();
/// @dev Firstly update the local state of the contract to prevent a reentrancy attack
/// even though the staking modules are trusted contracts.
uint256 depositsValue = msg.value;
if (depositsValue != _depositsCount * DEPOSIT_SIZE) revert InvalidDepositsValue(depositsValue, _depositsCount);
_updateModuleLastDepositState(stakingModule, _stakingModuleId, depositsValue);
if (_depositsCount > 0) {
(bytes memory publicKeysBatch, bytes memory signaturesBatch) =
IStakingModule(stakingModule.stakingModuleAddress)
.obtainDepositData(_depositsCount, _depositCalldata);
uint256 etherBalanceBeforeDeposits = address(this).balance;
_makeBeaconChainDeposits32ETH(
_depositsCount,
abi.encodePacked(withdrawalCredentials),
publicKeysBatch,
signaturesBatch
);
uint256 etherBalanceAfterDeposits = address(this).balance;
/// @dev All sent ETH must be deposited and self balance stay the same.
assert(etherBalanceBeforeDeposits - etherBalanceAfterDeposits == depositsValue);
}
}
/// @notice Set credentials to withdraw ETH on Consensus Layer side.
/// @param _withdrawalCredentials withdrawal credentials field as defined in the Consensus Layer specs.
/// @dev Note that setWithdrawalCredentials discards all unused deposits data as the signatures are invalidated.
/// @dev The function is restricted to the `MANAGE_WITHDRAWAL_CREDENTIALS_ROLE` role.
function setWithdrawalCredentials(bytes32 _withdrawalCredentials) external onlyRole(MANAGE_WITHDRAWAL_CREDENTIALS_ROLE) {
WITHDRAWAL_CREDENTIALS_POSITION.setStorageBytes32(_withdrawalCredentials);
uint256 stakingModulesCount = getStakingModulesCount();
for (uint256 i; i < stakingModulesCount; ) {
StakingModule storage stakingModule = _getStakingModuleByIndex(i);
unchecked {
++i;
}
try IStakingModule(stakingModule.stakingModuleAddress)
.onWithdrawalCredentialsChanged() {}
catch (bytes memory lowLevelRevertData) {
/// @dev This check is required to prevent incorrect gas estimation of the method.
/// Without it, Ethereum nodes that use binary search for gas estimation may
/// return an invalid value when the onWithdrawalCredentialsChanged()
/// reverts because of the "out of gas" error. Here we assume that the
/// onWithdrawalCredentialsChanged() method doesn't have reverts with
/// empty error data except "out of gas".
if (lowLevelRevertData.length == 0) revert UnrecoverableModuleError();
_setStakingModuleStatus(stakingModule, StakingModuleStatus.DepositsPaused);
emit WithdrawalsCredentialsChangeFailed(stakingModule.id, lowLevelRevertData);
}
}
emit WithdrawalCredentialsSet(_withdrawalCredentials, msg.sender);
}
/// @notice Returns current credentials to withdraw ETH on Consensus Layer side.
/// @return Withdrawal credentials.
function getWithdrawalCredentials() public view returns (bytes32) {
return WITHDRAWAL_CREDENTIALS_POSITION.getStorageBytes32();
}
function _checkValidatorsByNodeOperatorReportData(
bytes calldata _nodeOperatorIds,
bytes calldata _validatorsCounts
) internal pure {
if (_nodeOperatorIds.length % 8 != 0 || _validatorsCounts.length % 16 != 0) {
revert InvalidReportData(3);
}
uint256 nodeOperatorsCount = _nodeOperatorIds.length / 8;
if (_validatorsCounts.length / 16 != nodeOperatorsCount) {
revert InvalidReportData(2);
}
if (nodeOperatorsCount == 0) {
revert InvalidReportData(1);
}
}
/// @dev Save the last deposit state for the staking module and emit the event
/// @param stakingModule staking module storage ref
/// @param stakingModuleId id of the staking module to be deposited
/// @param depositsValue value to deposit
function _updateModuleLastDepositState(
StakingModule storage stakingModule,
uint256 stakingModuleId,
uint256 depositsValue
) internal {
stakingModule.lastDepositAt = uint64(block.timestamp);
stakingModule.lastDepositBlock = block.number;
emit StakingRouterETHDeposited(stakingModuleId, depositsValue);
}
/// @dev Loads modules into a memory cache.
/// @return totalActiveValidators Total active validators across all modules.
/// @return stakingModulesCache Array of StakingModuleCache structs.
function _loadStakingModulesCache() internal view returns (
uint256 totalActiveValidators,
StakingModuleCache[] memory stakingModulesCache
) {
uint256 stakingModulesCount = getStakingModulesCount();
stakingModulesCache = new StakingModuleCache[](stakingModulesCount);
for (uint256 i; i < stakingModulesCount; ) {
stakingModulesCache[i] = _loadStakingModulesCacheItem(i);
totalActiveValidators += stakingModulesCache[i].activeValidatorsCount;
unchecked {
++i;
}
}
}
function _loadStakingModulesCacheItem(uint256 _stakingModuleIndex)
internal
view
returns (StakingModuleCache memory cacheItem)
{
StakingModule storage stakingModuleData = _getStakingModuleByIndex(_stakingModuleIndex);
cacheItem.stakingModuleAddress = stakingModuleData.stakingModuleAddress;
cacheItem.stakingModuleId = stakingModuleData.id;
cacheItem.stakingModuleFee = stakingModuleData.stakingModuleFee;
cacheItem.treasuryFee = stakingModuleData.treasuryFee;
cacheItem.stakeShareLimit = stakingModuleData.stakeShareLimit;
cacheItem.status = StakingModuleStatus(stakingModuleData.status);
(
uint256 totalExitedValidators,
uint256 totalDepositedValidators,
uint256 depositableValidatorsCount
) = _getStakingModuleSummary(IStakingModule(cacheItem.stakingModuleAddress));
cacheItem.availableValidatorsCount = cacheItem.status == StakingModuleStatus.Active
? depositableValidatorsCount
: 0;
// The module might not receive all exited validators data yet => we need to replacing
// the exitedValidatorsCount with the one that the staking router is aware of.
cacheItem.activeValidatorsCount =
totalDepositedValidators -
Math256.max(totalExitedValidators, stakingModuleData.exitedValidatorsCount);
}
function _setStakingModuleStatus(StakingModule storage _stakingModule, StakingModuleStatus _status) internal {
StakingModuleStatus prevStatus = StakingModuleStatus(_stakingModule.status);
if (prevStatus != _status) {
_stakingModule.status = uint8(_status);
emit StakingModuleStatusSet(_stakingModule.id, _status, msg.sender);
}
}
function _getDepositsAllocation(
uint256 _depositsToAllocate
) internal view returns (uint256 allocated, uint256[] memory allocations, StakingModuleCache[] memory stakingModulesCache) {
// Calculate total used validators for operators.
uint256 totalActiveValidators;
(totalActiveValidators, stakingModulesCache) = _loadStakingModulesCache();
uint256 stakingModulesCount = stakingModulesCache.length;
allocations = new uint256[](stakingModulesCount);
if (stakingModulesCount > 0) {
/// @dev New estimated active validators count.
totalActiveValidators += _depositsToAllocate;
uint256[] memory capacities = new uint256[](stakingModulesCount);
uint256 targetValidators;
for (uint256 i; i < stakingModulesCount; ) {
allocations[i] = stakingModulesCache[i].activeValidatorsCount;
targetValidators = (stakingModulesCache[i].stakeShareLimit * totalActiveValidators) / TOTAL_BASIS_POINTS;
capacities[i] = Math256.min(targetValidators, stakingModulesCache[i].activeValidatorsCount + stakingModulesCache[i].availableValidatorsCount);
unchecked {
++i;
}
}
(allocated, allocations) = MinFirstAllocationStrategy.allocate(allocations, capacities, _depositsToAllocate);
}
}
function _getStakingModuleIndexById(uint256 _stakingModuleId) internal view returns (uint256) {
mapping(uint256 => uint256) storage _stakingModuleIndicesOneBased = _getStorageStakingIndicesMapping();
uint256 indexOneBased = _stakingModuleIndicesOneBased[_stakingModuleId];
if (indexOneBased == 0) revert StakingModuleUnregistered();
return indexOneBased - 1;
}
function _setStakingModuleIndexById(uint256 _stakingModuleId, uint256 _stakingModuleIndex) internal {
mapping(uint256 => uint256) storage _stakingModuleIndicesOneBased = _getStorageStakingIndicesMapping();
_stakingModuleIndicesOneBased[_stakingModuleId] = _stakingModuleIndex + 1;
}
function _getStakingModuleById(uint256 _stakingModuleId) internal view returns (StakingModule storage) {
return _getStakingModuleByIndex(_getStakingModuleIndexById(_stakingModuleId));
}
function _getIStakingModuleById(uint256 _stakingModuleId) internal view returns (IStakingModule) {
return IStakingModule(_getStakingModuleAddressById(_stakingModuleId));
}
function _getStakingModuleByIndex(uint256 _stakingModuleIndex) internal view returns (StakingModule storage) {
mapping(uint256 => StakingModule) storage _stakingModules = _getStorageStakingModulesMapping();
return _stakingModules[_stakingModuleIndex];
}
function _getStakingModuleAddressById(uint256 _stakingModuleId) internal view returns (address) {
return _getStakingModuleById(_stakingModuleId).stakingModuleAddress;
}
function _getStorageStakingModulesMapping() internal pure returns (mapping(uint256 => StakingModule) storage result) {
bytes32 position = STAKING_MODULES_MAPPING_POSITION;
assembly {
result.slot := position
}
}
function _getStorageStakingIndicesMapping() internal pure returns (mapping(uint256 => uint256) storage result) {
bytes32 position = STAKING_MODULE_INDICES_MAPPING_POSITION;
assembly {
result.slot := position
}
}
function _toE4Precision(uint256 _value, uint256 _precision) internal pure returns (uint16) {
return uint16((_value * TOTAL_BASIS_POINTS) / _precision);
}
function _validateEqualArrayLengths(uint256 firstArrayLength, uint256 secondArrayLength) internal pure {
if (firstArrayLength != secondArrayLength) {
revert ArraysLengthMismatch(firstArrayLength, secondArrayLength);
}
}
/// @dev Optimizes contract deployment size by wrapping the 'stakingModule.getStakingModuleSummary' function.
function _getStakingModuleSummary(IStakingModule stakingModule) internal view returns (uint256, uint256, uint256) {
return stakingModule.getStakingModuleSummary();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControl.sol)
//
// A modified AccessControl contract using unstructured storage. Copied from tree:
// https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access
//
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import "@openzeppelin/contracts-v4.4/access/IAccessControl.sol";
import "@openzeppelin/contracts-v4.4/utils/Context.sol";
import "@openzeppelin/contracts-v4.4/utils/Strings.sol";
import "@openzeppelin/contracts-v4.4/utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
/// @dev Storage slot: mapping(bytes32 => RoleData) _roles
bytes32 private constant ROLES_POSITION = keccak256("openzeppelin.AccessControl._roles");
function _storageRoles() private pure returns (mapping(bytes32 => RoleData) storage _roles) {
bytes32 position = ROLES_POSITION;
assembly { _roles.slot := position }
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _storageRoles()[role].members[account];
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _storageRoles()[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_storageRoles()[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_storageRoles()[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_storageRoles()[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/AccessControlEnumerable.sol)
//
// A modified AccessControlEnumerable contract using unstructured storage. Copied from tree:
// https://github.com/OpenZeppelin/openzeppelin-contracts/tree/6bd6b76/contracts/access
//
/* See contracts/COMPILERS.md */
pragma solidity 0.8.9;
import "@openzeppelin/contracts-v4.4/access/IAccessControlEnumerable.sol";
import "@openzeppelin/contracts-v4.4/utils/structs/EnumerableSet.sol";
import "./AccessControl.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
/// @dev Storage slot: mapping(bytes32 => EnumerableSet.AddressSet) _roleMembers
bytes32 private constant ROLE_MEMBERS_POSITION = keccak256("openzeppelin.AccessControlEnumerable._roleMembers");
function _storageRoleMembers() private pure returns (
mapping(bytes32 => EnumerableSet.AddressSet) storage _roleMembers
) {
bytes32 position = ROLE_MEMBERS_POSITION;
assembly { _roleMembers.slot := position }
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
return _storageRoleMembers()[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
return _storageRoleMembers()[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_storageRoleMembers()[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_storageRoleMembers()[role].remove(account);
}
}// SPDX-FileCopyrightText: 2022 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.9;
import "../lib/UnstructuredStorage.sol";
contract Versioned {
using UnstructuredStorage for bytes32;
event ContractVersionSet(uint256 version);
error NonZeroContractVersionOnInit();
error InvalidContractVersionIncrement();
error UnexpectedContractVersion(uint256 expected, uint256 received);
/// @dev Storage slot: uint256 version
/// Version of the initialized contract storage.
/// The version stored in CONTRACT_VERSION_POSITION equals to:
/// - 0 right after the deployment, before an initializer is invoked (and only at that moment);
/// - N after calling initialize(), where N is the initially deployed contract version;
/// - N after upgrading contract by calling finalizeUpgrade_vN().
bytes32 internal constant CONTRACT_VERSION_POSITION = keccak256("lido.Versioned.contractVersion");
uint256 internal constant PETRIFIED_VERSION_MARK = type(uint256).max;
constructor() {
// lock version in the implementation's storage to prevent initialization
CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK);
}
/// @notice Returns the current contract version.
function getContractVersion() public view returns (uint256) {
return CONTRACT_VERSION_POSITION.getStorageUint256();
}
function _checkContractVersion(uint256 version) internal view {
uint256 expectedVersion = getContractVersion();
if (version != expectedVersion) {
revert UnexpectedContractVersion(expectedVersion, version);
}
}
/// @dev Sets the contract version to N. Should be called from the initialize() function.
function _initializeContractVersionTo(uint256 version) internal {
if (getContractVersion() != 0) revert NonZeroContractVersionOnInit();
_setContractVersion(version);
}
/// @dev Updates the contract version. Should be called from a finalizeUpgrade_vN() function.
function _updateContractVersion(uint256 newVersion) internal {
if (newVersion != getContractVersion() + 1) revert InvalidContractVersionIncrement();
_setContractVersion(newVersion);
}
function _setContractVersion(uint256 version) private {
CONTRACT_VERSION_POSITION.setStorageUint256(version);
emit ContractVersionSet(version);
}
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
interface IBurner {
/**
* Commit cover/non-cover burning requests and logs cover/non-cover shares amount just burnt.
*
* NB: The real burn enactment to be invoked after the call (via internal Lido._burnShares())
*/
function commitSharesToBurn(uint256 _stETHSharesToBurn) external;
/**
* Request burn shares
*/
function requestBurnShares(address _from, uint256 _sharesAmount) external;
/**
* Returns the current amount of shares locked on the contract to be burnt.
*/
function getSharesRequestedToBurn() external view returns (uint256 coverShares, uint256 nonCoverShares);
/**
* Returns the total cover shares ever burnt.
*/
function getCoverSharesBurnt() external view returns (uint256);
/**
* Returns the total non-cover shares ever burnt.
*/
function getNonCoverSharesBurnt() external view returns (uint256);
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
interface ILidoLocator {
function accountingOracle() external view returns(address);
function depositSecurityModule() external view returns(address);
function elRewardsVault() external view returns(address);
function legacyOracle() external view returns(address);
function lido() external view returns(address);
function oracleReportSanityChecker() external view returns(address);
function burner() external view returns(address);
function stakingRouter() external view returns(address);
function treasury() external view returns(address);
function validatorsExitBusOracle() external view returns(address);
function withdrawalQueue() external view returns(address);
function withdrawalVault() external view returns(address);
function postTokenRebaseReceiver() external view returns(address);
function oracleDaemonConfig() external view returns(address);
function coreComponents() external view returns(
address elRewardsVault,
address oracleReportSanityChecker,
address stakingRouter,
address treasury,
address withdrawalQueue,
address withdrawalVault
);
function oracleReportComponentsForLido() external view returns(
address accountingOracle,
address elRewardsVault,
address oracleReportSanityChecker,
address burner,
address withdrawalQueue,
address withdrawalVault,
address postTokenRebaseReceiver
);
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: MIT
// Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol
// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
library Math256 {
/// @dev Returns the largest of two numbers.
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/// @dev Returns the smallest of two numbers.
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/// @dev Returns the largest of two numbers.
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/// @dev Returns the smallest of two numbers.
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/// @dev Returns the ceiling of the division of two numbers.
///
/// This differs from standard division with `/` in that it rounds up instead
/// of rounding down.
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/// @dev Returns absolute difference of two numbers.
function absDiff(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : b - a;
}
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
// solhint-disable-next-line lido/fixed-compiler-version
pragma solidity >=0.4.24 <0.9.0;
library MemUtils {
/**
* @dev Allocates a memory byte array of `_len` bytes without zeroing it out.
*/
function unsafeAllocateBytes(uint256 _len) internal pure returns (bytes memory result) {
assembly {
result := mload(0x40)
mstore(result, _len)
let freeMemPtr := add(add(result, 32), _len)
// align free mem ptr to 32 bytes as the compiler does now
mstore(0x40, and(add(freeMemPtr, 31), not(31)))
}
}
/**
* Performs a memory copy of `_len` bytes from position `_src` to position `_dst`.
*/
function memcpy(uint256 _src, uint256 _dst, uint256 _len) internal pure {
assembly {
// while al least 32 bytes left, copy in 32-byte chunks
for { } gt(_len, 31) { } {
mstore(_dst, mload(_src))
_src := add(_src, 32)
_dst := add(_dst, 32)
_len := sub(_len, 32)
}
if gt(_len, 0) {
// read the next 32-byte chunk from _dst, replace the first N bytes
// with those left in the _src, and write the transformed chunk back
let mask := sub(shl(mul(8, sub(32, _len)), 1), 1) // 2 ** (8 * (32 - _len)) - 1
let srcMasked := and(mload(_src), not(mask))
let dstMasked := and(mload(_dst), mask)
mstore(_dst, or(dstMasked, srcMasked))
}
}
}
/**
* Copies `_len` bytes from `_src`, starting at position `_srcStart`, into `_dst`, starting at position `_dstStart` into `_dst`.
*/
function copyBytes(bytes memory _src, bytes memory _dst, uint256 _srcStart, uint256 _dstStart, uint256 _len) internal pure {
require(_srcStart + _len <= _src.length && _dstStart + _len <= _dst.length, "BYTES_ARRAY_OUT_OF_BOUNDS");
uint256 srcStartPos;
uint256 dstStartPos;
assembly {
srcStartPos := add(add(_src, 32), _srcStart)
dstStartPos := add(add(_dst, 32), _dstStart)
}
memcpy(srcStartPos, dstStartPos, _len);
}
/**
* Copies bytes from `_src` to `_dst`, starting at position `_dstStart` into `_dst`.
*/
function copyBytes(bytes memory _src, bytes memory _dst, uint256 _dstStart) internal pure {
copyBytes(_src, _dst, 0, _dstStart, _src.length);
}
}// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
/* See contracts/COMPILERS.md */
// solhint-disable-next-line
pragma solidity >=0.4.24 <0.9.0;
import {Math256} from "./Math256.sol";
/// @notice Library with methods to calculate "proportional" allocations among buckets with different
/// capacity and level of filling.
/// @dev The current implementation favors buckets with the least fill factor
library MinFirstAllocationStrategy {
uint256 private constant MAX_UINT256 = 2**256 - 1;
/// @notice Allocates passed maxAllocationSize among the buckets. The resulting allocation doesn't exceed the
/// capacities of the buckets. An algorithm starts filling from the least populated buckets to equalize the fill factor.
/// For example, for buckets: [9998, 70, 0], capacities: [10000, 101, 100], and maxAllocationSize: 101, the allocation happens
/// following way:
/// 1. top up the bucket with index 2 on 70. Intermediate state of the buckets: [9998, 70, 70]. According to the definition,
/// the rest allocation must be proportionally split among the buckets with the same values.
/// 2. top up the bucket with index 1 on 15. Intermediate state of the buckets: [9998, 85, 70].
/// 3. top up the bucket with index 2 on 15. Intermediate state of the buckets: [9998, 85, 85].
/// 4. top up the bucket with index 1 on 1. Nothing to distribute. The final state of the buckets: [9998, 86, 85]
/// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation.
/// @param buckets The array of current allocations in the buckets
/// @param capacities The array of capacities of the buckets
/// @param allocationSize The desired value to allocate among the buckets
/// @return allocated The total value allocated among the buckets. Can't exceed the allocationSize value
function allocate(
uint256[] memory buckets,
uint256[] memory capacities,
uint256 allocationSize
) public pure returns (uint256 allocated, uint256[] memory) {
uint256 allocatedToBestCandidate = 0;
while (allocated < allocationSize) {
allocatedToBestCandidate = allocateToBestCandidate(buckets, capacities, allocationSize - allocated);
if (allocatedToBestCandidate == 0) {
break;
}
allocated += allocatedToBestCandidate;
}
return (allocated, buckets);
}
/// @notice Allocates the max allowed value not exceeding allocationSize to the bucket with the least value.
/// The candidate search happens according to the following algorithm:
/// 1. Find the first least filled bucket which has free space. Count the number of such buckets.
/// 2. If no buckets are found terminate the search - no free buckets
/// 3. Find the first bucket with free space, which has the least value greater
/// than the bucket found in step 1. To preserve proportional allocation the resulting allocation can't exceed this value.
/// 4. Calculate the allocation size as:
/// min(
/// (count of least filling buckets > 1 ? ceilDiv(allocationSize, count of least filling buckets) : allocationSize),
/// fill factor of the bucket found in step 3,
/// free space of the least filled bucket
/// )
/// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation.
/// @param buckets The array of current allocations in the buckets
/// @param capacities The array of capacities of the buckets
/// @param allocationSize The desired value to allocate to the bucket
/// @return allocated The total value allocated to the bucket. Can't exceed the allocationSize value
function allocateToBestCandidate(
uint256[] memory buckets,
uint256[] memory capacities,
uint256 allocationSize
) internal pure returns (uint256 allocated) {
uint256 bestCandidateIndex = buckets.length;
uint256 bestCandidateAllocation = MAX_UINT256;
uint256 bestCandidatesCount = 0;
if (allocationSize == 0) {
return 0;
}
for (uint256 i = 0; i < buckets.length; ++i) {
if (buckets[i] >= capacities[i]) {
continue;
} else if (bestCandidateAllocation > buckets[i]) {
bestCandidateIndex = i;
bestCandidatesCount = 1;
bestCandidateAllocation = buckets[i];
} else if (bestCandidateAllocation == buckets[i]) {
bestCandidatesCount += 1;
}
}
if (bestCandidatesCount == 0) {
return 0;
}
// cap the allocation by the smallest larger allocation than the found best one
uint256 allocationSizeUpperBound = MAX_UINT256;
for (uint256 j = 0; j < buckets.length; ++j) {
if (buckets[j] >= capacities[j]) {
continue;
} else if (buckets[j] > bestCandidateAllocation && buckets[j] < allocationSizeUpperBound) {
allocationSizeUpperBound = buckets[j];
}
}
allocated = Math256.min(
bestCandidatesCount > 1 ? Math256.ceilDiv(allocationSize, bestCandidatesCount) : allocationSize,
Math256.min(allocationSizeUpperBound, capacities[bestCandidateIndex]) - bestCandidateAllocation
);
buckets[bestCandidateIndex] += allocated;
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "istanbul",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_lidoLocator","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"components":[{"internalType":"uint256","name":"exitedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"appearedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"annualBalanceIncreaseBPLimit","type":"uint256"},{"internalType":"uint256","name":"simulatedShareRateDeviationBPLimit","type":"uint256"},{"internalType":"uint256","name":"maxValidatorExitRequestsPerReport","type":"uint256"},{"internalType":"uint256","name":"maxItemsPerExtraDataTransaction","type":"uint256"},{"internalType":"uint256","name":"maxNodeOperatorsPerExtraDataItem","type":"uint256"},{"internalType":"uint256","name":"requestTimestampMargin","type":"uint256"},{"internalType":"uint256","name":"maxPositiveTokenRebase","type":"uint256"},{"internalType":"uint256","name":"initialSlashingAmountPWei","type":"uint256"},{"internalType":"uint256","name":"inactivityPenaltiesAmountPWei","type":"uint256"},{"internalType":"uint256","name":"clBalanceOraclesErrorUpperBPLimit","type":"uint256"}],"internalType":"struct LimitsList","name":"_limitsList","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ActualShareRateIsZero","type":"error"},{"inputs":[],"name":"AdminCannotBeZero","type":"error"},{"inputs":[],"name":"CalledNotFromLido","type":"error"},{"inputs":[{"internalType":"uint256","name":"limitPerDay","type":"uint256"},{"internalType":"uint256","name":"exitedPerDay","type":"uint256"}],"name":"ExitedValidatorsLimitExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"appearedValidatorsLimit","type":"uint256"}],"name":"IncorrectAppearedValidators","type":"error"},{"inputs":[{"internalType":"uint256","name":"negativeCLRebaseSum","type":"uint256"},{"internalType":"uint256","name":"maxNegativeCLRebaseSum","type":"uint256"}],"name":"IncorrectCLBalanceDecrease","type":"error"},{"inputs":[{"internalType":"uint256","name":"annualBalanceDiff","type":"uint256"}],"name":"IncorrectCLBalanceIncrease","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualELRewardsVaultBalance","type":"uint256"}],"name":"IncorrectELRewardsVaultBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"exitedValidatorsLimit","type":"uint256"}],"name":"IncorrectExitedValidators","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"minAllowedValue","type":"uint256"},{"internalType":"uint256","name":"maxAllowedValue","type":"uint256"}],"name":"IncorrectLimitValue","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxRequestsCount","type":"uint256"}],"name":"IncorrectNumberOfExitRequestsPerReport","type":"error"},{"inputs":[{"internalType":"uint256","name":"requestCreationBlock","type":"uint256"}],"name":"IncorrectRequestFinalization","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualSharesToBurn","type":"uint256"}],"name":"IncorrectSharesRequestedToBurn","type":"error"},{"inputs":[{"internalType":"uint256","name":"simulatedShareRate","type":"uint256"},{"internalType":"uint256","name":"actualShareRate","type":"uint256"}],"name":"IncorrectSimulatedShareRate","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualWithdrawalVaultBalance","type":"uint256"}],"name":"IncorrectWithdrawalsVaultBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"reportedValue","type":"uint256"},{"internalType":"uint256","name":"provedValue","type":"uint256"},{"internalType":"uint256","name":"limitBP","type":"uint256"}],"name":"NegativeRebaseFailedCLBalanceMismatch","type":"error"},{"inputs":[],"name":"NegativeRebaseFailedSecondOpinionReportIsNotReady","type":"error"},{"inputs":[{"internalType":"uint256","name":"reportedValue","type":"uint256"},{"internalType":"uint256","name":"provedValue","type":"uint256"}],"name":"NegativeRebaseFailedWithdrawalVaultBalanceMismatch","type":"error"},{"inputs":[],"name":"NegativeTotalPooledEther","type":"error"},{"inputs":[],"name":"TooHighTokenRebaseLimit","type":"error"},{"inputs":[],"name":"TooLowTokenRebaseLimit","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxItemsCount","type":"uint256"},{"internalType":"uint256","name":"receivedItemsCount","type":"uint256"}],"name":"TooManyItemsPerExtraDataTransaction","type":"error"},{"inputs":[{"internalType":"uint256","name":"itemIndex","type":"uint256"},{"internalType":"uint256","name":"nodeOpsCount","type":"uint256"}],"name":"TooManyNodeOpsPerExtraDataItem","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"annualBalanceIncreaseBPLimit","type":"uint256"}],"name":"AnnualBalanceIncreaseBPLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"appearedValidatorsPerDayLimit","type":"uint256"}],"name":"AppearedValidatorsPerDayLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"clBalanceOraclesErrorUpperBPLimit","type":"uint256"}],"name":"CLBalanceOraclesErrorUpperBPLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"exitedValidatorsPerDayLimit","type":"uint256"}],"name":"ExitedValidatorsPerDayLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inactivityPenaltiesAmountPWei","type":"uint256"}],"name":"InactivityPenaltiesAmountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"initialSlashingAmountPWei","type":"uint256"}],"name":"InitialSlashingAmountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxItemsPerExtraDataTransaction","type":"uint256"}],"name":"MaxItemsPerExtraDataTransactionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxNodeOperatorsPerExtraDataItem","type":"uint256"}],"name":"MaxNodeOperatorsPerExtraDataItemSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxPositiveTokenRebase","type":"uint256"}],"name":"MaxPositiveTokenRebaseSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxValidatorExitRequestsPerReport","type":"uint256"}],"name":"MaxValidatorExitRequestsPerReportSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"refSlot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"clTotalBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"clBalanceDecrease","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxAllowedCLRebaseNegativeSum","type":"uint256"}],"name":"NegativeCLRebaseAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"refSlot","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"clBalanceWei","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdrawalVaultBalance","type":"uint256"}],"name":"NegativeCLRebaseConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestTimestampMargin","type":"uint256"}],"name":"RequestTimestampMarginSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract ISecondOpinionOracle","name":"secondOpinionOracle","type":"address"}],"name":"SecondOpinionOracleChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"simulatedShareRateDeviationBPLimit","type":"uint256"}],"name":"SimulatedShareRateDeviationBPLimitSet","type":"event"},{"inputs":[],"name":"ALL_LIMITS_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ANNUAL_BALANCE_INCREASE_LIMIT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"APPEARED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXITED_VALIDATORS_PER_DAY_LIMIT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_SLASHING_AND_PENALTIES_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ITEMS_PER_EXTRA_DATA_TRANSACTION_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_NODE_OPERATORS_PER_EXTRA_DATA_ITEM_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_POSITIVE_TOKEN_REBASE_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_VALIDATOR_EXIT_REQUESTS_PER_REPORT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REQUEST_TIMESTAMP_MARGIN_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SECOND_OPINION_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHARE_RATE_DEVIATION_LIMIT_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timeElapsed","type":"uint256"},{"internalType":"uint256","name":"_preCLBalance","type":"uint256"},{"internalType":"uint256","name":"_postCLBalance","type":"uint256"},{"internalType":"uint256","name":"_withdrawalVaultBalance","type":"uint256"},{"internalType":"uint256","name":"_elRewardsVaultBalance","type":"uint256"},{"internalType":"uint256","name":"_sharesRequestedToBurn","type":"uint256"},{"internalType":"uint256","name":"_preCLValidators","type":"uint256"},{"internalType":"uint256","name":"_postCLValidators","type":"uint256"}],"name":"checkAccountingOracleReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_exitRequestsCount","type":"uint256"}],"name":"checkExitBusOracleReport","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_exitedValidatorsCount","type":"uint256"}],"name":"checkExitedValidatorsRatePerDay","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_extraDataListItemsCount","type":"uint256"}],"name":"checkExtraDataItemsCountPerTransaction","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_itemIndex","type":"uint256"},{"internalType":"uint256","name":"_nodeOperatorsCount","type":"uint256"}],"name":"checkNodeOperatorsPerExtraDataItemCount","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_postTotalPooledEther","type":"uint256"},{"internalType":"uint256","name":"_postTotalShares","type":"uint256"},{"internalType":"uint256","name":"_etherLockedOnWithdrawalQueue","type":"uint256"},{"internalType":"uint256","name":"_sharesBurntDueToWithdrawals","type":"uint256"},{"internalType":"uint256","name":"_simulatedShareRate","type":"uint256"}],"name":"checkSimulatedShareRate","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lastFinalizableRequestId","type":"uint256"},{"internalType":"uint256","name":"_reportTimestamp","type":"uint256"}],"name":"checkWithdrawalQueueOracleReport","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLidoLocator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxPositiveTokenRebase","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleReportLimits","outputs":[{"components":[{"internalType":"uint256","name":"exitedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"appearedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"annualBalanceIncreaseBPLimit","type":"uint256"},{"internalType":"uint256","name":"simulatedShareRateDeviationBPLimit","type":"uint256"},{"internalType":"uint256","name":"maxValidatorExitRequestsPerReport","type":"uint256"},{"internalType":"uint256","name":"maxItemsPerExtraDataTransaction","type":"uint256"},{"internalType":"uint256","name":"maxNodeOperatorsPerExtraDataItem","type":"uint256"},{"internalType":"uint256","name":"requestTimestampMargin","type":"uint256"},{"internalType":"uint256","name":"maxPositiveTokenRebase","type":"uint256"},{"internalType":"uint256","name":"initialSlashingAmountPWei","type":"uint256"},{"internalType":"uint256","name":"inactivityPenaltiesAmountPWei","type":"uint256"},{"internalType":"uint256","name":"clBalanceOraclesErrorUpperBPLimit","type":"uint256"}],"internalType":"struct LimitsList","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReportDataCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"reportData","outputs":[{"internalType":"uint64","name":"timestamp","type":"uint64"},{"internalType":"uint64","name":"totalExitedValidators","type":"uint64"},{"internalType":"uint128","name":"negativeCLRebaseWei","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"secondOpinionOracle","outputs":[{"internalType":"contract ISecondOpinionOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_annualBalanceIncreaseBPLimit","type":"uint256"}],"name":"setAnnualBalanceIncreaseBPLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_appearedValidatorsPerDayLimit","type":"uint256"}],"name":"setAppearedValidatorsPerDayLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_exitedValidatorsPerDayLimit","type":"uint256"}],"name":"setExitedValidatorsPerDayLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_initialSlashingAmountPWei","type":"uint256"},{"internalType":"uint256","name":"_inactivityPenaltiesAmountPWei","type":"uint256"}],"name":"setInitialSlashingAndPenaltiesAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxValidatorExitRequestsPerReport","type":"uint256"}],"name":"setMaxExitRequestsPerOracleReport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxItemsPerExtraDataTransaction","type":"uint256"}],"name":"setMaxItemsPerExtraDataTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxNodeOperatorsPerExtraDataItem","type":"uint256"}],"name":"setMaxNodeOperatorsPerExtraDataItem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxPositiveTokenRebase","type":"uint256"}],"name":"setMaxPositiveTokenRebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"exitedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"appearedValidatorsPerDayLimit","type":"uint256"},{"internalType":"uint256","name":"annualBalanceIncreaseBPLimit","type":"uint256"},{"internalType":"uint256","name":"simulatedShareRateDeviationBPLimit","type":"uint256"},{"internalType":"uint256","name":"maxValidatorExitRequestsPerReport","type":"uint256"},{"internalType":"uint256","name":"maxItemsPerExtraDataTransaction","type":"uint256"},{"internalType":"uint256","name":"maxNodeOperatorsPerExtraDataItem","type":"uint256"},{"internalType":"uint256","name":"requestTimestampMargin","type":"uint256"},{"internalType":"uint256","name":"maxPositiveTokenRebase","type":"uint256"},{"internalType":"uint256","name":"initialSlashingAmountPWei","type":"uint256"},{"internalType":"uint256","name":"inactivityPenaltiesAmountPWei","type":"uint256"},{"internalType":"uint256","name":"clBalanceOraclesErrorUpperBPLimit","type":"uint256"}],"internalType":"struct LimitsList","name":"_limitsList","type":"tuple"},{"internalType":"contract ISecondOpinionOracle","name":"_secondOpinionOracle","type":"address"}],"name":"setOracleReportLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestTimestampMargin","type":"uint256"}],"name":"setRequestTimestampMargin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISecondOpinionOracle","name":"_secondOpinionOracle","type":"address"},{"internalType":"uint256","name":"_clBalanceOraclesErrorUpperBPLimit","type":"uint256"}],"name":"setSecondOpinionOracleAndCLBalanceUpperMargin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_simulatedShareRateDeviationBPLimit","type":"uint256"}],"name":"setSimulatedShareRateDeviationBPLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_preTotalPooledEther","type":"uint256"},{"internalType":"uint256","name":"_preTotalShares","type":"uint256"},{"internalType":"uint256","name":"_preCLBalance","type":"uint256"},{"internalType":"uint256","name":"_postCLBalance","type":"uint256"},{"internalType":"uint256","name":"_withdrawalVaultBalance","type":"uint256"},{"internalType":"uint256","name":"_elRewardsVaultBalance","type":"uint256"},{"internalType":"uint256","name":"_sharesRequestedToBurn","type":"uint256"},{"internalType":"uint256","name":"_etherToLockForWithdrawals","type":"uint256"},{"internalType":"uint256","name":"_newSharesToBurnForWithdrawals","type":"uint256"}],"name":"smoothenTokenRebase","outputs":[{"internalType":"uint256","name":"withdrawals","type":"uint256"},{"internalType":"uint256","name":"elRewards","type":"uint256"},{"internalType":"uint256","name":"simulatedSharesToBurn","type":"uint256"},{"internalType":"uint256","name":"sharesToBurn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b5060405162005e2238038062005e228339810160408190526200003591620010ab565b6001600160a01b0382166200005d57604051636b35b1b760e01b815260040160405180910390fd5b6001600160a01b038316608081905260408051635a2031f960e01b8152905160009291635a2031f9916004808301926020929190829003018186803b158015620000a657600080fd5b505afa158015620000bb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e191906200118b565b9050806001600160a01b031663f28824616040518163ffffffff1660e01b815260040160206040518083038186803b1580156200011d57600080fd5b505afa15801562000132573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001589190620011a9565b60a08181525050806001600160a01b031663304b90716040518163ffffffff1660e01b815260040160206040518083038186803b1580156200019957600080fd5b505afa158015620001ae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001d49190620011a9565b60c081815250506080516001600160a01b03166323509a2d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156200021757600080fd5b505afa1580156200022c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200025291906200118b565b6001600160a01b031660e052620002698262000280565b6200027660008462000a00565b50505050620011c3565b60408051610180810182526000805461ffff8082168452620100008204811660208086019190915264010000000083048216958501959095526601000000000000820481166060850152680100000000000000008204811660808501526a01000000000000000000008204811660a08501526c010000000000000000000000008204811660c085015263ffffffff600160701b83041660e08501526001600160401b03600160901b830416610100850152600160d01b82048116610120850152600160e01b82048116610140850152600160f01b9091041661016083015291620003749190620021f962000a61821b17901c565b8251815191925014620003c95781516200039390600061ffff62000b68565b81516040519081527f3260697ed6d1cd0fb4c36827dcfcbb8ff67bce7c153a2418b2b4880d1c108fdc9060200160405180910390a15b81602001518160200151146200042d576020820151620003ee90600061ffff62000b68565b7f9e09bbe08be12906e0b32e33a76b3c86bcb8746f95e1ce2c328a96d19bb4cb4982602001516040516200042491815260200190565b60405180910390a15b8160400151816040015114620004915760408201516200045290600061271062000b68565b7f072255e549d7af0b66b32d3c8bb3baab05ff0b637bad9912b4a79e351502e6ec82604001516040516200048891815260200190565b60405180910390a15b8160600151816060015114620004f5576060820151620004b690600061271062000b68565b7f564dc5b1cc26875884375bc204781166e6c58ddb1e751f33768aafcb7f4b14a98260600151604051620004ec91815260200190565b60405180910390a15b8160800151816080015114620005595760808201516200051a90600061ffff62000b68565b7f091d4dda52b3b3c65f8b6315c7eb3ed462af65bc87bf2ffcaa5890d4a36c524a82608001516040516200055091815260200190565b60405180910390a15b8160a001518160a0015114620005bd5760a08201516200057e90600061ffff62000b68565b7f489868acea3c923e005d69f081f9c57e8269a8e663f4de3ccd3de649242ce01d8260a00151604051620005b491815260200190565b60405180910390a15b8160c001518160c0015114620006215760c0820151620005e290600061ffff62000b68565b7f51c5a6d08eefb37ef6fce5e13995dca4874bf263809701689be9c2cfcd2f48348260c001516040516200061891815260200190565b60405180910390a15b8160e001518160e0015114620006875760e08201516200064890600063ffffffff62000b68565b7f1ae32ca67bad0d65fa81ce18c6e37fe5e128141e1052f38e9bcd03ec61e0db6f8260e001516040516200067e91815260200190565b60405180910390a15b81610100015181610100015114620006f457610100820151620006b49060016001600160401b0362000b68565b7fc0c9db31c634d95c015e8c34250f174a49a3d4c71f37b7e94a0f69c9874272e2826101000151604051620006eb91815260200190565b60405180910390a15b816101200151816101200151146200075c576101208201516200071c90600061ffff62000b68565b7f2b1d7f7649deb296754b27325830c82ef5929908b1f7b6da30798087cde259da8261012001516040516200075391815260200190565b60405180910390a15b81610140015181610140015114620007c4576101408201516200078490600061ffff62000b68565b7f30a0e4e5b0be51ef176019ac5acef5ec67fb9caa6662bef5c133926b3c4ef53c826101400151604051620007bb91815260200190565b60405180910390a15b816101600151816101600151146200082c57610160820151620007ec90600061271062000b68565b7f85d8c4f159596b99b75b6ef1772d92a5e31fe334a4a72f66da48d6f0feac9c158261016001516040516200082391815260200190565b60405180910390a15b620008428262000bab60201b620022a31760201c565b6000808201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548161ffff021916908361ffff16021790555060608201518160000160066101000a81548161ffff021916908361ffff16021790555060808201518160000160086101000a81548161ffff021916908361ffff16021790555060a082015181600001600a6101000a81548161ffff021916908361ffff16021790555060c082015181600001600c6101000a81548161ffff021916908361ffff16021790555060e082015181600001600e6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160000160126101000a8154816001600160401b0302191690836001600160401b0316021790555061012082015181600001601a6101000a81548161ffff021916908361ffff16021790555061014082015181600001601c6101000a81548161ffff021916908361ffff16021790555061016082015181600001601e6101000a81548161ffff021916908361ffff1602179055509050505050565b62000a17828262000db160201b620024261760201c565b62000a5c817f8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe600085815260209182526040902091906200249c62000e54821b17901c565b505050565b62000ac66040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b815161ffff908116825260208084015182169083015260408084015182169083015260608084015182169083015260e08084015163ffffffff1690830152610100808401516001600160401b03169083015260808084015182169083015260a08084015182169083015260c080840151821690830152610120808401518216908301526101408084015182169083015261016092830151169181019190915290565b8083118062000b7657508183105b1562000a5c576040516309014ed160e41b81526004810184905260248101839052604481018290526064015b60405180910390fd5b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081019190915262000c26826000015162000e7460201b620024b11760201c565b61ffff16815260208083015162000c4691620024b162000e74821b17901c565b61ffff166020820152604082015162000c5f9062000edd565b61ffff166040820152606082015162000c789062000edd565b61ffff16606082015260e082015162000c9d9062000f33602090811b6200251817901c565b63ffffffff1660e082015261010082015162000cc59062000f9a602090811b6200257d17901c565b6001600160401b0316610100820152608082015162000cf09062000e74602090811b620024b117901c565b61ffff16608082015260a082015162000d159062000e74602090811b620024b117901c565b61ffff1660a082015260c082015162000d3a9062000e74602090811b620024b117901c565b61ffff1660c082015261012082015162000d609062000e74602090811b620024b117901c565b61ffff1661012082015261014082015162000d879062000e74602090811b620024b117901c565b61ffff1661014082015261016082015162000da29062000edd565b61ffff16610160820152919050565b600082815260008051602062005e02833981519152602090815260408083206001600160a01b038516845290915290205460ff1662000e5057600082815260008051602062005e02833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45b5050565b600062000e6b836001600160a01b03841662001004565b90505b92915050565b600061ffff82111562000ed95760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201526536206269747360d01b606482015260840162000ba2565b5090565b600061271082111562000ed95760405162461bcd60e51b815260206004820152601560248201527f42415349535f504f494e54535f4f564552464c4f570000000000000000000000604482015260640162000ba2565b600063ffffffff82111562000ed95760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b606482015260840162000ba2565b60006001600160401b0382111562000ed95760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b606482015260840162000ba2565b60008181526001830160205260408120546200104d5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000e6e565b50600062000e6e565b80516001600160a01b03811681146200106e57600080fd5b919050565b60405161018081016001600160401b0381118282101715620010a557634e487b7160e01b600052604160045260246000fd5b60405290565b60008060008385036101c0811215620010c357600080fd5b620010ce8562001056565b9350620010de6020860162001056565b925061018080603f1983011215620010f557600080fd5b620010ff62001073565b915060408601518252606086015160208301526080860151604083015260a0860151606083015260c0860151608083015260e086015160a08301526101008087015160c08401526101208088015160e0850152610140808901518386015261016092508289015182860152838901518186015250506101a0870151818401525050809150509250925092565b6000602082840312156200119e57600080fd5b62000e6b8262001056565b600060208284031215620011bc57600080fd5b5051919050565b60805160a05160c05160e051614bdb6200122760003960006114a301526000613157015260006131820152600081816107a101528181611358015281816115b2015281816116b7015281816117600152818161301b01526131ac0152614bdb6000f3fe608060405234801561001057600080fd5b50600436106102955760003560e01c80638024cca111610167578063b8498a39116100ce578063d547741f11610087578063d547741f14610765578063d853950214610778578063e654ff171461079f578063e6a2d296146107c5578063e72980f4146107d8578063eb9c41d8146107eb57600080fd5b8063b8498a39146106a4578063c2b0f7dd146106d7578063c9bace93146106ea578063ca15c873146106fd578063cc235b2f14610710578063d3c418511461075257600080fd5b806393d338cc1161012057806393d338cc14610598578063a217fddf146105ab578063a3a3fd5d146105b3578063a6e9ebe314610643578063a89c6e391461066a578063a991eccd1461067d57600080fd5b80638024cca11461050557806385d3ecb1146105185780639010d07c1461053f578063901646821461056a57806391d148541461057d578063934ca90b1461059057600080fd5b80633d5f14d11161020b57806357e0f690116101c457806357e0f6901461047f57806363e56b9f1461049257806364beda61146104a55780636a84f2fd146104b857806375ef6222146104cb578063769cc376146104de57600080fd5b80633d5f14d1146103bd5780633e0865dd146103e45780634181e2d1146103f7578063495e0e781461041e5780634979e93b146104455780634a366eac1461045857600080fd5b80632f2ff15d1161025d5780632f2ff15d146103235780632f3e6fbb1461033657806336568abe14610349578063376ac84b1461035c57806337a243b9146103835780633aafc0ad1461039657600080fd5b806301ffc9a71461029a57806304953605146102c2578063248a9ca3146102d757806325665e76146102f857806326656cdf14610310575b600080fd5b6102ad6102a836600461415a565b610812565b60405190151581526020015b60405180910390f35b6102d56102d0366004614184565b61083d565b005b6102ea6102e5366004614184565b61094b565b6040519081526020016102b9565b600054600160901b90046001600160401b03166102ea565b6102d561031e36600461419d565b61096d565b6102d56103313660046141d7565b610a84565b6102d5610344366004614184565b610aa1565b6102d56103573660046141d7565b610ba9565b6102ea7f2f8719116fbba3aba2a39759e34dcd29ea3516f7568c8321695aaea208280cd381565b6102d5610391366004614207565b610c2c565b6102ea7f13445b811594f3a8800b82296d63f371c695d2f86c1069c899ded3d73f523d2e81565b6102ea7f9925400e72399e0a89e9b346878fc47ac0031526d0e060e33ff372d7a5d11ba881565b6102d56103f2366004614184565b610d97565b6102ea7f60b9982471bc0620c7b74959f48a86c55c92c11876fddc5b0b54d1ec47153e5d81565b6102ea7faa0e0f619f5a9368ee035bae759cfe7da8958f44bd71456a05198effc68ea64781565b6102d5610453366004614184565b610e8c565b6102ea7f14ca7b84baa11a976283347b0159b8ddf2dcf5fd5cf613cc567a3423cf51011981565b6102d561048d36600461419d565b610f94565b6102d56104a0366004614233565b611090565b6102d56104b3366004614184565b611183565b6102d56104c636600461419d565b611287565b6102d56104d936600461426e565b6113f5565b6102ea7fb3f2d16385bde8a7121e101a021aff43bbe4d4137a587832caf1f583b9d3439581565b6102d56105133660046142a4565b611498565b6102ea7febfa317a5d279811b024586b17a50f48924bce86f6293b233927322d7209b50781565b61055261054d36600461419d565b611852565b6040516001600160a01b0390911681526020016102b9565b6102d5610578366004614184565b61187e565b6102ad61058b3660046141d7565b611986565b6001546102ea565b6102d56105a6366004614184565b6119be565b6102ea600081565b6105bb611ac6565b6040516102b99190815181526020808301519082015260408083015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0808301519082015261010080830151908201526101208083015190820152610140808301519082015261016091820151918101919091526101800190565b6102ea7f78de2bab4a3a0c88f50b6bb7c2290e0eb46bc61d575eae694d8bffbc2ca98c9281565b6102d5610678366004614184565b611b9d565b6102ea7f5bf88568a012dfc9fe67407ad6775052bddc4ac89902dea1f4373ef5d9f1e35b81565b6106b76106b23660046142f9565b611ca5565b6040805194855260208501939093529183015260608201526080016102b9565b6102d56106e5366004614184565b611d6e565b6102d56106f8366004614184565b611e76565b6102ea61070b366004614184565b611f72565b61072361071e366004614184565b611f96565b604080516001600160401b0394851681529390921660208401526001600160801b0316908201526060016102b9565b6102d5610760366004614184565b611fdb565b6102d56107733660046141d7565b6120e3565b6102ea7f7b21c0949109e9e143f66d6aa1f8a065b3f4ab47ee9f84f6837fd0490eace4d581565b7f0000000000000000000000000000000000000000000000000000000000000000610552565b600254610552906001600160a01b031681565b6102d56107e6366004614184565b612100565b6102ea7f12c02753cd3d584dc4bb965eb0c88392c4c4d7c00433fdb7490d33c61ea5762281565b60006001600160e01b03198216635a05180f60e01b14806108375750610837826125e5565b92915050565b7f78de2bab4a3a0c88f50b6bb7c2290e0eb46bc61d575eae694d8bffbc2ca98c92610868813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610933906121f9565b610100810184905290506109468161267e565b505050565b6000908152600080516020614b86833981519152602052604090206001015490565b7febfa317a5d279811b024586b17a50f48924bce86f6293b233927322d7209b507610998813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610a63906121f9565b610120810185905261014081018490529050610a7e8161267e565b50505050565b610a8d8261094b565b610a97813361261a565b6109468383612d96565b7f7b21c0949109e9e143f66d6aa1f8a065b3f4ab47ee9f84f6837fd0490eace4d5610acc813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610b97906121f9565b6060810184905290506109468161267e565b6001600160a01b0381163314610c1e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610c288282612dc5565b5050565b7faa0e0f619f5a9368ee035bae759cfe7da8958f44bd71456a05198effc68ea647610c57813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610d22906121f9565b61016081018490529050610d358161267e565b6002546001600160a01b03858116911614610a7e57600280546001600160a01b0319166001600160a01b0386169081179091556040517fe7c7c65524cf897e6dc02974dc05011b2c34271d6597e57ed24fc6459c443ce990600090a250505050565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610e62906121f9565b60800151905080821115610c285760405163db49e15d60e01b815260048101829052602401610c15565b7f13445b811594f3a8800b82296d63f371c695d2f86c1069c899ded3d73f523d2e610eb7813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610f82906121f9565b60a0810184905290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b900490921661016082015261105f906121f9565b60c001519050808211156109465760405163a53d262360e01b81526004810184905260248101839052604401610c15565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b900490921661016082015261115b906121f9565b905061117b8161116b868961436e565b611175868961436e565b85612df4565b505050505050565b7f60b9982471bc0620c7b74959f48a86c55c92c11876fddc5b0b54d1ec47153e5d6111ae813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611279906121f9565b83815290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611352906121f9565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166337d5fe996040518163ffffffff1660e01b815260040160206040518083038186803b1580156113af57600080fd5b505afa1580156113c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e79190614396565b9050610a7e82828686612e98565b7f5bf88568a012dfc9fe67407ad6775052bddc4ac89902dea1f4373ef5d9f1e35b611420813361261a565b61143761143236859003850185614467565b61267e565b6002546001600160a01b0383811691161461094657600280546001600160a01b0319166001600160a01b0384169081179091556040517fe7c7c65524cf897e6dc02974dc05011b2c34271d6597e57ed24fc6459c443ce990600090a2505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146114e15760405163246d99eb60e01b815260040160405180910390fd5b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526115ac906121f9565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635a2031f96040518163ffffffff1660e01b815260040160206040518083038186803b15801561160957600080fd5b505afa15801561161d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116419190614396565b6001600160a01b0316633584d59c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561167957600080fd5b505afa15801561168d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b19190614506565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166369d421486040518163ffffffff1660e01b815260040160206040518083038186803b15801561170e57600080fd5b505afa158015611722573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117469190614396565b905061175c816001600160a01b03163189612fce565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663e441d25f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156117b757600080fd5b505afa1580156117cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ef9190614396565b9050611805816001600160a01b03163189612ff2565b61180e87613016565b61181c848c8c8c8988613150565b611828848c8c8f613514565b85851115611844576118448461183e888861451f565b8e6135a2565b505050505050505050505050565b6000828152600080516020614b668339815191526020526040812061187790836135f1565b9392505050565b7f9925400e72399e0a89e9b346878fc47ac0031526d0e060e33ff372d7a5d11ba86118a9813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611974906121f9565b6080810184905290506109468161267e565b6000918252600080516020614b86833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b7fb3f2d16385bde8a7121e101a021aff43bbe4d4137a587832caf1f583b9d343956119e9813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611ab4906121f9565b60c0810184905290506109468161267e565b611ace6140f9565b604080516101808101825260005461ffff808216835262010000820481166020840152600160201b8204811693830193909352600160301b810483166060830152600160401b810483166080830152600160501b8104831660a0830152600160601b8104831660c083015263ffffffff600160701b82041660e08301526001600160401b03600160901b820416610100830152600160d01b81048316610120830152600160e01b81048316610140830152600160f01b9004909116610160820152611b98906121f9565b905090565b7f12c02753cd3d584dc4bb965eb0c88392c4c4d7c00433fdb7490d33c61ea57622611bc8813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611c93906121f9565b6040810184905290506109468161267e565b6000806000806000611cd1611cca6000546001600160401b03600160901b9091041690565b8f8f6135fd565b90508b8b1015611cf457611cef611ce88c8e61451f565b82906136ec565b611d0a565b611d08611d018d8d61451f565b8290613741565b505b611d14818b613741565b9450611d20818a613741565b9350611d34611d2e826137b6565b8961384e565b9250611d4081886136ec565b611d5b611d4c826137b6565b611d568a8961436e565b61384e565b9150509950995099509995505050505050565b7f14ca7b84baa11a976283347b0159b8ddf2dcf5fd5cf613cc567a3423cf510119611d99813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611e64906121f9565b6020810184905290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611f41906121f9565b60a00151905080821115610c28576040516319bad6b360e01b81526004810182905260248101839052604401610c15565b6000818152600080516020614b668339815191526020526040812061083790613864565b60018181548110611fa657600080fd5b6000918252602090912001546001600160401b038082169250600160401b82041690600160801b90046001600160801b031683565b7f2f8719116fbba3aba2a39759e34dcd29ea3516f7568c8321695aaea208280cd3612006813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526120d1906121f9565b60e0810184905290506109468161267e565b6120ec8261094b565b6120f6813361261a565b6109468383612dc5565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526121cb906121f9565b51905080821115610c28576040516312b73f6960e31b81526004810182905260248101839052604401610c15565b6122016140f9565b815161ffff908116825260208084015182169083015260408084015182169083015260608084015182169083015260e08084015163ffffffff1690830152610100808401516001600160401b03169083015260808084015182169083015260a08084015182169083015260c080840151821690830152610120808401518216908301526101408084015182169083015261016092830151169181019190915290565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810191909152815161230f906124b1565b61ffff1681526020820151612323906124b1565b61ffff166020820152604082015161233a9061386e565b61ffff16604082015260608201516123519061386e565b61ffff16606082015260e082015161236890612518565b63ffffffff1660e08201526101008201516123829061257d565b6001600160401b0316610100820152608082015161239f906124b1565b61ffff16608082015260a08201516123b6906124b1565b61ffff1660a082015260c08201516123cd906124b1565b61ffff1660c08201526101208201516123e5906124b1565b61ffff166101208201526101408201516123fe906124b1565b61ffff166101408201526101608201516124179061386e565b61ffff16610160820152919050565b6124308282611986565b610c28576000828152600080516020614b86833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000611877836001600160a01b0384166138ba565b600061ffff8211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201526536206269747360d01b6064820152608401610c15565b5090565b600063ffffffff8211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610c15565b60006001600160401b038211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610c15565b60006001600160e01b03198216637965db0b60e01b148061083757506301ffc9a760e01b6001600160e01b0319831614610837565b6126248282611986565b610c285761263c816001600160a01b03166014613909565b612647836020613909565b604051602001612658929190614562565b60408051601f198184030181529082905262461bcd60e51b8252610c15916004016145d7565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152612749906121f9565b825181519192501461279b57815161276590600061ffff613aa4565b81516040519081527f3260697ed6d1cd0fb4c36827dcfcbb8ff67bce7c153a2418b2b4880d1c108fdc9060200160405180910390a15b81602001518160200151146127fb5760208201516127bd90600061ffff613aa4565b7f9e09bbe08be12906e0b32e33a76b3c86bcb8746f95e1ce2c328a96d19bb4cb4982602001516040516127f291815260200190565b60405180910390a15b816040015181604001511461285a5761281c82604001516000612710613aa4565b7f072255e549d7af0b66b32d3c8bb3baab05ff0b637bad9912b4a79e351502e6ec826040015160405161285191815260200190565b60405180910390a15b81606001518160600151146128b95761287b82606001516000612710613aa4565b7f564dc5b1cc26875884375bc204781166e6c58ddb1e751f33768aafcb7f4b14a982606001516040516128b091815260200190565b60405180910390a15b81608001518160800151146129195760808201516128db90600061ffff613aa4565b7f091d4dda52b3b3c65f8b6315c7eb3ed462af65bc87bf2ffcaa5890d4a36c524a826080015160405161291091815260200190565b60405180910390a15b8160a001518160a00151146129795760a082015161293b90600061ffff613aa4565b7f489868acea3c923e005d69f081f9c57e8269a8e663f4de3ccd3de649242ce01d8260a0015160405161297091815260200190565b60405180910390a15b8160c001518160c00151146129d95760c082015161299b90600061ffff613aa4565b7f51c5a6d08eefb37ef6fce5e13995dca4874bf263809701689be9c2cfcd2f48348260c001516040516129d091815260200190565b60405180910390a15b8160e001518160e0015114612a3b5760e08201516129fd90600063ffffffff613aa4565b7f1ae32ca67bad0d65fa81ce18c6e37fe5e128141e1052f38e9bcd03ec61e0db6f8260e00151604051612a3291815260200190565b60405180910390a15b81610100015181610100015114612aa457610100820151612a659060016001600160401b03613aa4565b7fc0c9db31c634d95c015e8c34250f174a49a3d4c71f37b7e94a0f69c9874272e2826101000151604051612a9b91815260200190565b60405180910390a15b81610120015181610120015114612b0857610120820151612ac990600061ffff613aa4565b7f2b1d7f7649deb296754b27325830c82ef5929908b1f7b6da30798087cde259da826101200151604051612aff91815260200190565b60405180910390a15b81610140015181610140015114612b6c57610140820151612b2d90600061ffff613aa4565b7f30a0e4e5b0be51ef176019ac5acef5ec67fb9caa6662bef5c133926b3c4ef53c826101400151604051612b6391815260200190565b60405180910390a15b81610160015181610160015114612bcf57612b908261016001516000612710613aa4565b7f85d8c4f159596b99b75b6ef1772d92a5e31fe334a4a72f66da48d6f0feac9c15826101600151604051612bc691815260200190565b60405180910390a15b612bd8826122a3565b6000808201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548161ffff021916908361ffff16021790555060608201518160000160066101000a81548161ffff021916908361ffff16021790555060808201518160000160086101000a81548161ffff021916908361ffff16021790555060a082015181600001600a6101000a81548161ffff021916908361ffff16021790555060c082015181600001600c6101000a81548161ffff021916908361ffff16021790555060e082015181600001600e6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160000160126101000a8154816001600160401b0302191690836001600160401b0316021790555061012082015181600001601a6101000a81548161ffff021916908361ffff16021790555061014082015181600001601c6101000a81548161ffff021916908361ffff16021790555061016082015181600001601e6101000a81548161ffff021916908361ffff1602179055509050505050565b612da08282612426565b6000828152600080516020614b6683398151915260205260409020610946908261249c565b612dcf8282613ae0565b6000828152600080516020614b66833981519152602052604090206109469082613b54565b600082612e0d6b033b2e3c9fd0803ce80000008661460a565b612e179190614629565b905080612e375760405163cfaab9c360e01b815260040160405180910390fd5b6000612e438284613b69565b9050600082612e548361271061460a565b612e5e9190614629565b90508660600151811115612e8f57604051635b98172960e11b81526004810185905260248101849052604401610c15565b50505050505050565b604080516001808252818301909252600091602080830190803683370190505090508281600081518110612ece57612ece61464b565b6020908102919091010152604051635c625c2d60e11b81526000906001600160a01b0386169063b8c4b85a90612f08908590600401614661565b60006040518083038186803b158015612f2057600080fd5b505afa158015612f34573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612f5c91908101906146d8565b90508560e0015181600081518110612f7657612f7661464b565b602002602001015160600151612f8c919061436e565b83101561117b5780600081518110612fa657612fa661464b565b602002602001015160600151604051636e1561c760e11b8152600401610c1591815260200190565b81811115610c28576040516317edc0a360e01b815260048101839052602401610c15565b81811115610c28576040516331e07c1d60e11b815260048101839052602401610c15565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166327810b6e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561307257600080fd5b505afa158015613086573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130aa9190614396565b6001600160a01b0316632a369d1a6040518163ffffffff1660e01b8152600401604080518083038186803b1580156130e157600080fd5b505afa1580156130f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311991906147cf565b9092509050600061312a828461436e565b905080841115610a7e57604051634a329c9f60e11b815260048101829052602401610c15565b600061317c7f00000000000000000000000000000000000000000000000000000000000000008361460a565b6131a6907f000000000000000000000000000000000000000000000000000000000000000061436e565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561320357600080fd5b505afa158015613217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323b9190614396565b90506000816001600160a01b031663f2aebb656040518163ffffffff1660e01b815260040160006040518083038186803b15801561327857600080fd5b505afa15801561328c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b491908101906147f3565b90506000805b8251811015613389576000846001600160a01b031663bc1bb1908584815181106132e6576132e661464b565b60200260200101516040518263ffffffff1660e01b815260040161330c91815260200190565b60006040518083038186803b15801561332457600080fd5b505afa158015613338573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526133609190810190614933565b905080610120015183613373919061436e565b925050808061338190614a6b565b9150506132ba565b50613394878961436e565b89116133af576133a684826000613b8b565b5050505061117b565b6133cd84826133be8a8c61436e565b6133c8908d61451f565b613b8b565b60006133e46133df6217bb008761451f565b613c46565b905060006133fd6133f8624731008861451f565b613d03565b613407908961451f565b66038d7ea4c680008d610140015161341f919061460a565b613429919061460a565b6134396133f86217bb008961451f565b613443908a61451f565b66038d7ea4c680008e610120015161345b919061460a565b613465919061460a565b61346f919061436e565b90508082116134d5577f7b98f79345f59407ff687b9b72e4f07cc0f981ec539c28a6525e4c191da9df0e876134a48b8d61436e565b60408051928352602083019190915281018490526060810183905260800160405180910390a150505050505061117b565b6002546001600160a01b0316613508576040516302cfa90d60e51b81526004810183905260248101829052604401610c15565b611844878b8b8f613db1565b8261352157633b9aca0092505b81831061352d57610a7e565b806135375750610e105b6000613543848461451f565b9050600082858361355a6127106301e1338061460a565b613564919061460a565b61356e9190614629565b6135789190614629565b9050856040015181111561117b57604051630e383a8560e41b815260048101829052602401610c15565b806135ac5750610e105b6000620151808285602001516135c2919061460a565b6135cc9190614629565b905080831115610a7e57604051626af66b60e51b815260048101849052602401610c15565b60006118778383613f73565b61362f6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8361364d57604051633b9353cd60e01b815260040160405180910390fd5b6001600160401b038411156136755760405163180c236360e11b815260040160405180910390fd5b82613685576001600160401b0393505b8281526040810183905260208101829052606081018490526001600160401b0384146136dc5780516060820151633b9aca00916136c19161460a565b6136cb9190614629565b81516136d7919061436e565b6136e0565b6000195b60808201529392505050565b60608201516001600160401b031415613703575050565b8160400151811115613728576040516384bd4c9f60e01b815260040160405180910390fd5b808260400181815161373a919061451f565b9052505050565b60608201516000906001600160401b03141561375e575080610837565b604083018051908390613771828461436e565b90525060408401516080850151613788919061384e565b6040850181905281111561379e5761379e614a86565b8084604001516137ae919061451f565b949350505050565b60608101516000906001600160401b0314156137d457506020015190565b60808201516040830151106137eb57506000919050565b6000633b9aca008360600151613801919061436e565b905060008360000151633b9aca00856040015161381e919061460a565b6138289190614629565b905081613835828261451f565b8560200151613844919061460a565b6137ae9190614629565b600081831061385d5781611877565b5090919050565b6000610837825490565b60006127108211156125145760405162461bcd60e51b815260206004820152601560248201527442415349535f504f494e54535f4f564552464c4f5760581b6044820152606401610c15565b600081815260018301602052604081205461390157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610837565b506000610837565b6060600061391883600261460a565b61392390600261436e565b6001600160401b0381111561393a5761393a6143b3565b6040519080825280601f01601f191660200182016040528015613964576020820181803683370190505b509050600360fc1b8160008151811061397f5761397f61464b565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106139ae576139ae61464b565b60200101906001600160f81b031916908160001a90535060006139d284600261460a565b6139dd90600161436e565b90505b6001811115613a55576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613a1157613a1161464b565b1a60f81b828281518110613a2757613a2761464b565b60200101906001600160f81b031916908160001a90535060049490941c93613a4e81614a9c565b90506139e0565b5083156118775760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610c15565b80831180613ab157508183105b15610946576040516309014ed160e41b8152600481018490526024810183905260448101829052606401610c15565b613aea8282611986565b15610c28576000828152600080516020614b86833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611877836001600160a01b038416613f9d565b6000818311613b8157613b7c838361451f565b611877565b611877828461451f565b60016040518060600160405280613ba18661257d565b6001600160401b03168152602001613bb88561257d565b6001600160401b03168152602001613bcf84614090565b6001600160801b0390811690915282546001810184556000938452602093849020835191018054948401516040909401518316600160801b026001600160401b03948516600160401b026fffffffffffffffffffffffffffffffff1990961694909216939093179390931716919091179055505050565b6001805460009182918291613c5a91614ab3565b90505b60008112613cfc57613c6e8461257d565b6001600160401b031660018281548110613c8a57613c8a61464b565b6000918252602090912001546001600160401b03161115613ce55760018181548110613cb857613cb861464b565b600091825260209091200154613cde90600160801b90046001600160801b03168361436e565b9150613cea565b613cfc565b80613cf481614af2565b915050613c5d565b5092915050565b600180546000918291613d169190614ab3565b90505b60008112613da857613d2a8361257d565b6001600160401b031660018281548110613d4657613d4661464b565b6000918252602090912001546001600160401b031611613d965760018181548110613d7357613d7361464b565b600091825260209091200154600160401b90046001600160401b03169392505050565b80613da081614af2565b915050613d19565b50600092915050565b600254604051634e7f9b1960e01b815260048101869052600091829182916001600160a01b031690634e7f9b199060240160a06040518083038186803b158015613dfa57600080fd5b505afa158015613e0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e329190614b08565b50509250925092508215613f5a576000613e5083633b9aca0061460a565b905086811015613e8b57610160850151604051632592a93d60e11b815260048101899052602481018390526044810191909152606401610c15565b80856101600151613e9c919061460a565b613ea6888361451f565b613eb29061271061460a565b1115613ee957610160850151604051632592a93d60e11b815260048101899052602481018390526044810191909152606401610c15565b858214613f135760405163b385071960e01b81526004810187905260248101839052604401610c15565b60408051898152602081018990529081018790527f2eb398b1f77a0d380bde88f991aabff30a4a616b7b3b7961ab94d2b710ea30529060600160405180910390a150612e8f565b60405163a0bdb35760e01b815260040160405180910390fd5b6000826000018281548110613f8a57613f8a61464b565b9060005260206000200154905092915050565b60008181526001830160205260408120548015614086576000613fc160018361451f565b8554909150600090613fd59060019061451f565b905081811461403a576000866000018281548110613ff557613ff561464b565b90600052602060002001549050808760000184815481106140185761401861464b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061404b5761404b614b4f565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610837565b6000915050610837565b60006001600160801b038211156125145760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610c15565b6040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60006020828403121561416c57600080fd5b81356001600160e01b03198116811461187757600080fd5b60006020828403121561419657600080fd5b5035919050565b600080604083850312156141b057600080fd5b50508035926020909101359150565b6001600160a01b03811681146141d457600080fd5b50565b600080604083850312156141ea57600080fd5b8235915060208301356141fc816141bf565b809150509250929050565b6000806040838503121561421a57600080fd5b8235614225816141bf565b946020939093013593505050565b600080600080600060a0868803121561424b57600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b6000808284036101a081121561428357600080fd5b6101808082121561429357600080fd5b84935083013590506141fc816141bf565b600080600080600080600080610100898b0312156142c157600080fd5b505086359860208801359850604088013597606081013597506080810135965060a0810135955060c0810135945060e0013592509050565b60008060008060008060008060006101208a8c03121561431857600080fd5b505087359960208901359950604089013598606081013598506080810135975060a0810135965060c0810135955060e08101359450610100013592509050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561438157614381614358565b500190565b8051614391816141bf565b919050565b6000602082840312156143a857600080fd5b8151611877816141bf565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b03811182821017156143ec576143ec6143b3565b60405290565b60405160c081016001600160401b03811182821017156143ec576143ec6143b3565b6040516101a081016001600160401b03811182821017156143ec576143ec6143b3565b604051601f8201601f191681016001600160401b038111828210171561445f5761445f6143b3565b604052919050565b6000610180828403121561447a57600080fd5b6144826143c9565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e08201526101008084013581830152506101208084013581830152506101408084013581830152506101608084013581830152508091505092915050565b60006020828403121561451857600080fd5b5051919050565b60008282101561453157614531614358565b500390565b60005b83811015614551578181015183820152602001614539565b83811115610a7e5750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161459a816017850160208801614536565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516145cb816028840160208801614536565b01602801949350505050565b60208152600082518060208401526145f6816040850160208701614536565b601f01601f19169190910160400192915050565b600081600019048311821515161561462457614624614358565b500290565b60008261464657634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6020808252825182820181905260009190848201906040850190845b818110156146995783518352928401929184019160010161467d565b50909695505050505050565b60006001600160401b038211156146be576146be6143b3565b5060051b60200190565b8051801515811461439157600080fd5b600060208083850312156146eb57600080fd5b82516001600160401b0381111561470157600080fd5b8301601f8101851361471257600080fd5b8051614725614720826146a5565b614437565b81815260c0918202830184019184820191908884111561474457600080fd5b938501935b838510156147c35780858a0312156147615760008081fd5b6147696143f2565b855181528686015187820152604080870151614784816141bf565b9082015260608681015190820152608061479f8188016146c8565b9082015260a06147b08782016146c8565b9082015283529384019391850191614749565b50979650505050505050565b600080604083850312156147e257600080fd5b505080516020909101519092909150565b6000602080838503121561480657600080fd5b82516001600160401b0381111561481c57600080fd5b8301601f8101851361482d57600080fd5b805161483b614720826146a5565b81815260059190911b8201830190838101908783111561485a57600080fd5b928401925b828410156148785783518252928401929084019061485f565b979650505050505050565b805162ffffff8116811461439157600080fd5b805161ffff8116811461439157600080fd5b805160ff8116811461439157600080fd5b600082601f8301126148ca57600080fd5b81516001600160401b038111156148e3576148e36143b3565b6148f6601f8201601f1916602001614437565b81815284602083860101111561490b57600080fd5b6137ae826020830160208701614536565b80516001600160401b038116811461439157600080fd5b60006020828403121561494557600080fd5b81516001600160401b038082111561495c57600080fd5b908301906101a0828603121561497157600080fd5b614979614414565b61498283614883565b815261499060208401614386565b60208201526149a160408401614896565b60408201526149b260608401614896565b60608201526149c360808401614896565b60808201526149d460a084016148a8565b60a082015260c0830151828111156149eb57600080fd5b6149f7878286016148b9565b60c083015250614a0960e0840161491c565b60e0820152610100838101519082015261012080840151908201526101409150614a34828401614896565b828201526101609150614a4882840161491c565b828201526101809150614a5c82840161491c565b91810191909152949350505050565b6000600019821415614a7f57614a7f614358565b5060010190565b634e487b7160e01b600052600160045260246000fd5b600081614aab57614aab614358565b506000190190565b60008083128015600160ff1b850184121615614ad157614ad1614358565b6001600160ff1b0384018313811615614aec57614aec614358565b50500390565b6000600160ff1b821415614aab57614aab614358565b600080600080600060a08688031215614b2057600080fd5b614b29866146c8565b602087015160408801516060890151608090990151929a91995097965090945092505050565b634e487b7160e01b600052603160045260246000fdfe8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d3a2646970667358221220e24ec0641c2c5b0f94e73838bc40e1288565752c5965056848d7b0eb26a4af5f64736f6c634300080900339a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d3000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c0000000000000000000000000000000000000000000000000000000000002328000000000000000000000000000000000000000000000000000000000000a8c000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000b71b000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000032
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102955760003560e01c80638024cca111610167578063b8498a39116100ce578063d547741f11610087578063d547741f14610765578063d853950214610778578063e654ff171461079f578063e6a2d296146107c5578063e72980f4146107d8578063eb9c41d8146107eb57600080fd5b8063b8498a39146106a4578063c2b0f7dd146106d7578063c9bace93146106ea578063ca15c873146106fd578063cc235b2f14610710578063d3c418511461075257600080fd5b806393d338cc1161012057806393d338cc14610598578063a217fddf146105ab578063a3a3fd5d146105b3578063a6e9ebe314610643578063a89c6e391461066a578063a991eccd1461067d57600080fd5b80638024cca11461050557806385d3ecb1146105185780639010d07c1461053f578063901646821461056a57806391d148541461057d578063934ca90b1461059057600080fd5b80633d5f14d11161020b57806357e0f690116101c457806357e0f6901461047f57806363e56b9f1461049257806364beda61146104a55780636a84f2fd146104b857806375ef6222146104cb578063769cc376146104de57600080fd5b80633d5f14d1146103bd5780633e0865dd146103e45780634181e2d1146103f7578063495e0e781461041e5780634979e93b146104455780634a366eac1461045857600080fd5b80632f2ff15d1161025d5780632f2ff15d146103235780632f3e6fbb1461033657806336568abe14610349578063376ac84b1461035c57806337a243b9146103835780633aafc0ad1461039657600080fd5b806301ffc9a71461029a57806304953605146102c2578063248a9ca3146102d757806325665e76146102f857806326656cdf14610310575b600080fd5b6102ad6102a836600461415a565b610812565b60405190151581526020015b60405180910390f35b6102d56102d0366004614184565b61083d565b005b6102ea6102e5366004614184565b61094b565b6040519081526020016102b9565b600054600160901b90046001600160401b03166102ea565b6102d561031e36600461419d565b61096d565b6102d56103313660046141d7565b610a84565b6102d5610344366004614184565b610aa1565b6102d56103573660046141d7565b610ba9565b6102ea7f2f8719116fbba3aba2a39759e34dcd29ea3516f7568c8321695aaea208280cd381565b6102d5610391366004614207565b610c2c565b6102ea7f13445b811594f3a8800b82296d63f371c695d2f86c1069c899ded3d73f523d2e81565b6102ea7f9925400e72399e0a89e9b346878fc47ac0031526d0e060e33ff372d7a5d11ba881565b6102d56103f2366004614184565b610d97565b6102ea7f60b9982471bc0620c7b74959f48a86c55c92c11876fddc5b0b54d1ec47153e5d81565b6102ea7faa0e0f619f5a9368ee035bae759cfe7da8958f44bd71456a05198effc68ea64781565b6102d5610453366004614184565b610e8c565b6102ea7f14ca7b84baa11a976283347b0159b8ddf2dcf5fd5cf613cc567a3423cf51011981565b6102d561048d36600461419d565b610f94565b6102d56104a0366004614233565b611090565b6102d56104b3366004614184565b611183565b6102d56104c636600461419d565b611287565b6102d56104d936600461426e565b6113f5565b6102ea7fb3f2d16385bde8a7121e101a021aff43bbe4d4137a587832caf1f583b9d3439581565b6102d56105133660046142a4565b611498565b6102ea7febfa317a5d279811b024586b17a50f48924bce86f6293b233927322d7209b50781565b61055261054d36600461419d565b611852565b6040516001600160a01b0390911681526020016102b9565b6102d5610578366004614184565b61187e565b6102ad61058b3660046141d7565b611986565b6001546102ea565b6102d56105a6366004614184565b6119be565b6102ea600081565b6105bb611ac6565b6040516102b99190815181526020808301519082015260408083015190820152606080830151908201526080808301519082015260a0808301519082015260c0808301519082015260e0808301519082015261010080830151908201526101208083015190820152610140808301519082015261016091820151918101919091526101800190565b6102ea7f78de2bab4a3a0c88f50b6bb7c2290e0eb46bc61d575eae694d8bffbc2ca98c9281565b6102d5610678366004614184565b611b9d565b6102ea7f5bf88568a012dfc9fe67407ad6775052bddc4ac89902dea1f4373ef5d9f1e35b81565b6106b76106b23660046142f9565b611ca5565b6040805194855260208501939093529183015260608201526080016102b9565b6102d56106e5366004614184565b611d6e565b6102d56106f8366004614184565b611e76565b6102ea61070b366004614184565b611f72565b61072361071e366004614184565b611f96565b604080516001600160401b0394851681529390921660208401526001600160801b0316908201526060016102b9565b6102d5610760366004614184565b611fdb565b6102d56107733660046141d7565b6120e3565b6102ea7f7b21c0949109e9e143f66d6aa1f8a065b3f4ab47ee9f84f6837fd0490eace4d581565b7f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb610552565b600254610552906001600160a01b031681565b6102d56107e6366004614184565b612100565b6102ea7f12c02753cd3d584dc4bb965eb0c88392c4c4d7c00433fdb7490d33c61ea5762281565b60006001600160e01b03198216635a05180f60e01b14806108375750610837826125e5565b92915050565b7f78de2bab4a3a0c88f50b6bb7c2290e0eb46bc61d575eae694d8bffbc2ca98c92610868813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610933906121f9565b610100810184905290506109468161267e565b505050565b6000908152600080516020614b86833981519152602052604090206001015490565b7febfa317a5d279811b024586b17a50f48924bce86f6293b233927322d7209b507610998813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610a63906121f9565b610120810185905261014081018490529050610a7e8161267e565b50505050565b610a8d8261094b565b610a97813361261a565b6109468383612d96565b7f7b21c0949109e9e143f66d6aa1f8a065b3f4ab47ee9f84f6837fd0490eace4d5610acc813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610b97906121f9565b6060810184905290506109468161267e565b6001600160a01b0381163314610c1e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610c288282612dc5565b5050565b7faa0e0f619f5a9368ee035bae759cfe7da8958f44bd71456a05198effc68ea647610c57813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610d22906121f9565b61016081018490529050610d358161267e565b6002546001600160a01b03858116911614610a7e57600280546001600160a01b0319166001600160a01b0386169081179091556040517fe7c7c65524cf897e6dc02974dc05011b2c34271d6597e57ed24fc6459c443ce990600090a250505050565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610e62906121f9565b60800151905080821115610c285760405163db49e15d60e01b815260048101829052602401610c15565b7f13445b811594f3a8800b82296d63f371c695d2f86c1069c899ded3d73f523d2e610eb7813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152610f82906121f9565b60a0810184905290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b900490921661016082015261105f906121f9565b60c001519050808211156109465760405163a53d262360e01b81526004810184905260248101839052604401610c15565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b900490921661016082015261115b906121f9565b905061117b8161116b868961436e565b611175868961436e565b85612df4565b505050505050565b7f60b9982471bc0620c7b74959f48a86c55c92c11876fddc5b0b54d1ec47153e5d6111ae813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611279906121f9565b83815290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611352906121f9565b905060007f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b03166337d5fe996040518163ffffffff1660e01b815260040160206040518083038186803b1580156113af57600080fd5b505afa1580156113c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e79190614396565b9050610a7e82828686612e98565b7f5bf88568a012dfc9fe67407ad6775052bddc4ac89902dea1f4373ef5d9f1e35b611420813361261a565b61143761143236859003850185614467565b61267e565b6002546001600160a01b0383811691161461094657600280546001600160a01b0319166001600160a01b0384169081179091556040517fe7c7c65524cf897e6dc02974dc05011b2c34271d6597e57ed24fc6459c443ce990600090a2505050565b336001600160a01b037f000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe8416146114e15760405163246d99eb60e01b815260040160405180910390fd5b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526115ac906121f9565b905060007f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b0316635a2031f96040518163ffffffff1660e01b815260040160206040518083038186803b15801561160957600080fd5b505afa15801561161d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116419190614396565b6001600160a01b0316633584d59c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561167957600080fd5b505afa15801561168d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b19190614506565b905060007f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b03166369d421486040518163ffffffff1660e01b815260040160206040518083038186803b15801561170e57600080fd5b505afa158015611722573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117469190614396565b905061175c816001600160a01b03163189612fce565b60007f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b031663e441d25f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156117b757600080fd5b505afa1580156117cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ef9190614396565b9050611805816001600160a01b03163189612ff2565b61180e87613016565b61181c848c8c8c8988613150565b611828848c8c8f613514565b85851115611844576118448461183e888861451f565b8e6135a2565b505050505050505050505050565b6000828152600080516020614b668339815191526020526040812061187790836135f1565b9392505050565b7f9925400e72399e0a89e9b346878fc47ac0031526d0e060e33ff372d7a5d11ba86118a9813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611974906121f9565b6080810184905290506109468161267e565b6000918252600080516020614b86833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b7fb3f2d16385bde8a7121e101a021aff43bbe4d4137a587832caf1f583b9d343956119e9813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611ab4906121f9565b60c0810184905290506109468161267e565b611ace6140f9565b604080516101808101825260005461ffff808216835262010000820481166020840152600160201b8204811693830193909352600160301b810483166060830152600160401b810483166080830152600160501b8104831660a0830152600160601b8104831660c083015263ffffffff600160701b82041660e08301526001600160401b03600160901b820416610100830152600160d01b81048316610120830152600160e01b81048316610140830152600160f01b9004909116610160820152611b98906121f9565b905090565b7f12c02753cd3d584dc4bb965eb0c88392c4c4d7c00433fdb7490d33c61ea57622611bc8813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611c93906121f9565b6040810184905290506109468161267e565b6000806000806000611cd1611cca6000546001600160401b03600160901b9091041690565b8f8f6135fd565b90508b8b1015611cf457611cef611ce88c8e61451f565b82906136ec565b611d0a565b611d08611d018d8d61451f565b8290613741565b505b611d14818b613741565b9450611d20818a613741565b9350611d34611d2e826137b6565b8961384e565b9250611d4081886136ec565b611d5b611d4c826137b6565b611d568a8961436e565b61384e565b9150509950995099509995505050505050565b7f14ca7b84baa11a976283347b0159b8ddf2dcf5fd5cf613cc567a3423cf510119611d99813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611e64906121f9565b6020810184905290506109468161267e565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152611f41906121f9565b60a00151905080821115610c28576040516319bad6b360e01b81526004810182905260248101839052604401610c15565b6000818152600080516020614b668339815191526020526040812061083790613864565b60018181548110611fa657600080fd5b6000918252602090912001546001600160401b038082169250600160401b82041690600160801b90046001600160801b031683565b7f2f8719116fbba3aba2a39759e34dcd29ea3516f7568c8321695aaea208280cd3612006813361261a565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526120d1906121f9565b60e0810184905290506109468161267e565b6120ec8261094b565b6120f6813361261a565b6109468383612dc5565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b90049092166101608201526121cb906121f9565b51905080821115610c28576040516312b73f6960e31b81526004810182905260248101839052604401610c15565b6122016140f9565b815161ffff908116825260208084015182169083015260408084015182169083015260608084015182169083015260e08084015163ffffffff1690830152610100808401516001600160401b03169083015260808084015182169083015260a08084015182169083015260c080840151821690830152610120808401518216908301526101408084015182169083015261016092830151169181019190915290565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810191909152815161230f906124b1565b61ffff1681526020820151612323906124b1565b61ffff166020820152604082015161233a9061386e565b61ffff16604082015260608201516123519061386e565b61ffff16606082015260e082015161236890612518565b63ffffffff1660e08201526101008201516123829061257d565b6001600160401b0316610100820152608082015161239f906124b1565b61ffff16608082015260a08201516123b6906124b1565b61ffff1660a082015260c08201516123cd906124b1565b61ffff1660c08201526101208201516123e5906124b1565b61ffff166101208201526101408201516123fe906124b1565b61ffff166101408201526101608201516124179061386e565b61ffff16610160820152919050565b6124308282611986565b610c28576000828152600080516020614b86833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000611877836001600160a01b0384166138ba565b600061ffff8211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201526536206269747360d01b6064820152608401610c15565b5090565b600063ffffffff8211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610c15565b60006001600160401b038211156125145760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610c15565b60006001600160e01b03198216637965db0b60e01b148061083757506301ffc9a760e01b6001600160e01b0319831614610837565b6126248282611986565b610c285761263c816001600160a01b03166014613909565b612647836020613909565b604051602001612658929190614562565b60408051601f198184030181529082905262461bcd60e51b8252610c15916004016145d7565b60408051610180810182526000805461ffff808216845262010000820481166020850152600160201b8204811694840194909452600160301b810484166060840152600160401b810484166080840152600160501b8104841660a0840152600160601b8104841660c084015263ffffffff600160701b82041660e08401526001600160401b03600160901b820416610100840152600160d01b81048416610120840152600160e01b81048416610140840152600160f01b9004909216610160820152612749906121f9565b825181519192501461279b57815161276590600061ffff613aa4565b81516040519081527f3260697ed6d1cd0fb4c36827dcfcbb8ff67bce7c153a2418b2b4880d1c108fdc9060200160405180910390a15b81602001518160200151146127fb5760208201516127bd90600061ffff613aa4565b7f9e09bbe08be12906e0b32e33a76b3c86bcb8746f95e1ce2c328a96d19bb4cb4982602001516040516127f291815260200190565b60405180910390a15b816040015181604001511461285a5761281c82604001516000612710613aa4565b7f072255e549d7af0b66b32d3c8bb3baab05ff0b637bad9912b4a79e351502e6ec826040015160405161285191815260200190565b60405180910390a15b81606001518160600151146128b95761287b82606001516000612710613aa4565b7f564dc5b1cc26875884375bc204781166e6c58ddb1e751f33768aafcb7f4b14a982606001516040516128b091815260200190565b60405180910390a15b81608001518160800151146129195760808201516128db90600061ffff613aa4565b7f091d4dda52b3b3c65f8b6315c7eb3ed462af65bc87bf2ffcaa5890d4a36c524a826080015160405161291091815260200190565b60405180910390a15b8160a001518160a00151146129795760a082015161293b90600061ffff613aa4565b7f489868acea3c923e005d69f081f9c57e8269a8e663f4de3ccd3de649242ce01d8260a0015160405161297091815260200190565b60405180910390a15b8160c001518160c00151146129d95760c082015161299b90600061ffff613aa4565b7f51c5a6d08eefb37ef6fce5e13995dca4874bf263809701689be9c2cfcd2f48348260c001516040516129d091815260200190565b60405180910390a15b8160e001518160e0015114612a3b5760e08201516129fd90600063ffffffff613aa4565b7f1ae32ca67bad0d65fa81ce18c6e37fe5e128141e1052f38e9bcd03ec61e0db6f8260e00151604051612a3291815260200190565b60405180910390a15b81610100015181610100015114612aa457610100820151612a659060016001600160401b03613aa4565b7fc0c9db31c634d95c015e8c34250f174a49a3d4c71f37b7e94a0f69c9874272e2826101000151604051612a9b91815260200190565b60405180910390a15b81610120015181610120015114612b0857610120820151612ac990600061ffff613aa4565b7f2b1d7f7649deb296754b27325830c82ef5929908b1f7b6da30798087cde259da826101200151604051612aff91815260200190565b60405180910390a15b81610140015181610140015114612b6c57610140820151612b2d90600061ffff613aa4565b7f30a0e4e5b0be51ef176019ac5acef5ec67fb9caa6662bef5c133926b3c4ef53c826101400151604051612b6391815260200190565b60405180910390a15b81610160015181610160015114612bcf57612b908261016001516000612710613aa4565b7f85d8c4f159596b99b75b6ef1772d92a5e31fe334a4a72f66da48d6f0feac9c15826101600151604051612bc691815260200190565b60405180910390a15b612bd8826122a3565b6000808201518160000160006101000a81548161ffff021916908361ffff16021790555060208201518160000160026101000a81548161ffff021916908361ffff16021790555060408201518160000160046101000a81548161ffff021916908361ffff16021790555060608201518160000160066101000a81548161ffff021916908361ffff16021790555060808201518160000160086101000a81548161ffff021916908361ffff16021790555060a082015181600001600a6101000a81548161ffff021916908361ffff16021790555060c082015181600001600c6101000a81548161ffff021916908361ffff16021790555060e082015181600001600e6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160000160126101000a8154816001600160401b0302191690836001600160401b0316021790555061012082015181600001601a6101000a81548161ffff021916908361ffff16021790555061014082015181600001601c6101000a81548161ffff021916908361ffff16021790555061016082015181600001601e6101000a81548161ffff021916908361ffff1602179055509050505050565b612da08282612426565b6000828152600080516020614b6683398151915260205260409020610946908261249c565b612dcf8282613ae0565b6000828152600080516020614b66833981519152602052604090206109469082613b54565b600082612e0d6b033b2e3c9fd0803ce80000008661460a565b612e179190614629565b905080612e375760405163cfaab9c360e01b815260040160405180910390fd5b6000612e438284613b69565b9050600082612e548361271061460a565b612e5e9190614629565b90508660600151811115612e8f57604051635b98172960e11b81526004810185905260248101849052604401610c15565b50505050505050565b604080516001808252818301909252600091602080830190803683370190505090508281600081518110612ece57612ece61464b565b6020908102919091010152604051635c625c2d60e11b81526000906001600160a01b0386169063b8c4b85a90612f08908590600401614661565b60006040518083038186803b158015612f2057600080fd5b505afa158015612f34573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612f5c91908101906146d8565b90508560e0015181600081518110612f7657612f7661464b565b602002602001015160600151612f8c919061436e565b83101561117b5780600081518110612fa657612fa661464b565b602002602001015160600151604051636e1561c760e11b8152600401610c1591815260200190565b81811115610c28576040516317edc0a360e01b815260048101839052602401610c15565b81811115610c28576040516331e07c1d60e11b815260048101839052602401610c15565b6000807f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b03166327810b6e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561307257600080fd5b505afa158015613086573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130aa9190614396565b6001600160a01b0316632a369d1a6040518163ffffffff1660e01b8152600401604080518083038186803b1580156130e157600080fd5b505afa1580156130f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311991906147cf565b9092509050600061312a828461436e565b905080841115610a7e57604051634a329c9f60e11b815260048101829052602401610c15565b600061317c7f000000000000000000000000000000000000000000000000000000000000000c8361460a565b6131a6907f000000000000000000000000000000000000000000000000000000005fc6305761436e565b905060007f000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb6001600160a01b031663ef6c064c6040518163ffffffff1660e01b815260040160206040518083038186803b15801561320357600080fd5b505afa158015613217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323b9190614396565b90506000816001600160a01b031663f2aebb656040518163ffffffff1660e01b815260040160006040518083038186803b15801561327857600080fd5b505afa15801561328c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b491908101906147f3565b90506000805b8251811015613389576000846001600160a01b031663bc1bb1908584815181106132e6576132e661464b565b60200260200101516040518263ffffffff1660e01b815260040161330c91815260200190565b60006040518083038186803b15801561332457600080fd5b505afa158015613338573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526133609190810190614933565b905080610120015183613373919061436e565b925050808061338190614a6b565b9150506132ba565b50613394878961436e565b89116133af576133a684826000613b8b565b5050505061117b565b6133cd84826133be8a8c61436e565b6133c8908d61451f565b613b8b565b60006133e46133df6217bb008761451f565b613c46565b905060006133fd6133f8624731008861451f565b613d03565b613407908961451f565b66038d7ea4c680008d610140015161341f919061460a565b613429919061460a565b6134396133f86217bb008961451f565b613443908a61451f565b66038d7ea4c680008e610120015161345b919061460a565b613465919061460a565b61346f919061436e565b90508082116134d5577f7b98f79345f59407ff687b9b72e4f07cc0f981ec539c28a6525e4c191da9df0e876134a48b8d61436e565b60408051928352602083019190915281018490526060810183905260800160405180910390a150505050505061117b565b6002546001600160a01b0316613508576040516302cfa90d60e51b81526004810183905260248101829052604401610c15565b611844878b8b8f613db1565b8261352157633b9aca0092505b81831061352d57610a7e565b806135375750610e105b6000613543848461451f565b9050600082858361355a6127106301e1338061460a565b613564919061460a565b61356e9190614629565b6135789190614629565b9050856040015181111561117b57604051630e383a8560e41b815260048101829052602401610c15565b806135ac5750610e105b6000620151808285602001516135c2919061460a565b6135cc9190614629565b905080831115610a7e57604051626af66b60e51b815260048101849052602401610c15565b60006118778383613f73565b61362f6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8361364d57604051633b9353cd60e01b815260040160405180910390fd5b6001600160401b038411156136755760405163180c236360e11b815260040160405180910390fd5b82613685576001600160401b0393505b8281526040810183905260208101829052606081018490526001600160401b0384146136dc5780516060820151633b9aca00916136c19161460a565b6136cb9190614629565b81516136d7919061436e565b6136e0565b6000195b60808201529392505050565b60608201516001600160401b031415613703575050565b8160400151811115613728576040516384bd4c9f60e01b815260040160405180910390fd5b808260400181815161373a919061451f565b9052505050565b60608201516000906001600160401b03141561375e575080610837565b604083018051908390613771828461436e565b90525060408401516080850151613788919061384e565b6040850181905281111561379e5761379e614a86565b8084604001516137ae919061451f565b949350505050565b60608101516000906001600160401b0314156137d457506020015190565b60808201516040830151106137eb57506000919050565b6000633b9aca008360600151613801919061436e565b905060008360000151633b9aca00856040015161381e919061460a565b6138289190614629565b905081613835828261451f565b8560200151613844919061460a565b6137ae9190614629565b600081831061385d5781611877565b5090919050565b6000610837825490565b60006127108211156125145760405162461bcd60e51b815260206004820152601560248201527442415349535f504f494e54535f4f564552464c4f5760581b6044820152606401610c15565b600081815260018301602052604081205461390157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610837565b506000610837565b6060600061391883600261460a565b61392390600261436e565b6001600160401b0381111561393a5761393a6143b3565b6040519080825280601f01601f191660200182016040528015613964576020820181803683370190505b509050600360fc1b8160008151811061397f5761397f61464b565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106139ae576139ae61464b565b60200101906001600160f81b031916908160001a90535060006139d284600261460a565b6139dd90600161436e565b90505b6001811115613a55576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110613a1157613a1161464b565b1a60f81b828281518110613a2757613a2761464b565b60200101906001600160f81b031916908160001a90535060049490941c93613a4e81614a9c565b90506139e0565b5083156118775760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610c15565b80831180613ab157508183105b15610946576040516309014ed160e41b8152600481018490526024810183905260448101829052606401610c15565b613aea8282611986565b15610c28576000828152600080516020614b86833981519152602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000611877836001600160a01b038416613f9d565b6000818311613b8157613b7c838361451f565b611877565b611877828461451f565b60016040518060600160405280613ba18661257d565b6001600160401b03168152602001613bb88561257d565b6001600160401b03168152602001613bcf84614090565b6001600160801b0390811690915282546001810184556000938452602093849020835191018054948401516040909401518316600160801b026001600160401b03948516600160401b026fffffffffffffffffffffffffffffffff1990961694909216939093179390931716919091179055505050565b6001805460009182918291613c5a91614ab3565b90505b60008112613cfc57613c6e8461257d565b6001600160401b031660018281548110613c8a57613c8a61464b565b6000918252602090912001546001600160401b03161115613ce55760018181548110613cb857613cb861464b565b600091825260209091200154613cde90600160801b90046001600160801b03168361436e565b9150613cea565b613cfc565b80613cf481614af2565b915050613c5d565b5092915050565b600180546000918291613d169190614ab3565b90505b60008112613da857613d2a8361257d565b6001600160401b031660018281548110613d4657613d4661464b565b6000918252602090912001546001600160401b031611613d965760018181548110613d7357613d7361464b565b600091825260209091200154600160401b90046001600160401b03169392505050565b80613da081614af2565b915050613d19565b50600092915050565b600254604051634e7f9b1960e01b815260048101869052600091829182916001600160a01b031690634e7f9b199060240160a06040518083038186803b158015613dfa57600080fd5b505afa158015613e0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e329190614b08565b50509250925092508215613f5a576000613e5083633b9aca0061460a565b905086811015613e8b57610160850151604051632592a93d60e11b815260048101899052602481018390526044810191909152606401610c15565b80856101600151613e9c919061460a565b613ea6888361451f565b613eb29061271061460a565b1115613ee957610160850151604051632592a93d60e11b815260048101899052602481018390526044810191909152606401610c15565b858214613f135760405163b385071960e01b81526004810187905260248101839052604401610c15565b60408051898152602081018990529081018790527f2eb398b1f77a0d380bde88f991aabff30a4a616b7b3b7961ab94d2b710ea30529060600160405180910390a150612e8f565b60405163a0bdb35760e01b815260040160405180910390fd5b6000826000018281548110613f8a57613f8a61464b565b9060005260206000200154905092915050565b60008181526001830160205260408120548015614086576000613fc160018361451f565b8554909150600090613fd59060019061451f565b905081811461403a576000866000018281548110613ff557613ff561464b565b90600052602060002001549050808760000184815481106140185761401861464b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061404b5761404b614b4f565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610837565b6000915050610837565b60006001600160801b038211156125145760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610c15565b6040518061018001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60006020828403121561416c57600080fd5b81356001600160e01b03198116811461187757600080fd5b60006020828403121561419657600080fd5b5035919050565b600080604083850312156141b057600080fd5b50508035926020909101359150565b6001600160a01b03811681146141d457600080fd5b50565b600080604083850312156141ea57600080fd5b8235915060208301356141fc816141bf565b809150509250929050565b6000806040838503121561421a57600080fd5b8235614225816141bf565b946020939093013593505050565b600080600080600060a0868803121561424b57600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b6000808284036101a081121561428357600080fd5b6101808082121561429357600080fd5b84935083013590506141fc816141bf565b600080600080600080600080610100898b0312156142c157600080fd5b505086359860208801359850604088013597606081013597506080810135965060a0810135955060c0810135945060e0013592509050565b60008060008060008060008060006101208a8c03121561431857600080fd5b505087359960208901359950604089013598606081013598506080810135975060a0810135965060c0810135955060e08101359450610100013592509050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561438157614381614358565b500190565b8051614391816141bf565b919050565b6000602082840312156143a857600080fd5b8151611877816141bf565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b03811182821017156143ec576143ec6143b3565b60405290565b60405160c081016001600160401b03811182821017156143ec576143ec6143b3565b6040516101a081016001600160401b03811182821017156143ec576143ec6143b3565b604051601f8201601f191681016001600160401b038111828210171561445f5761445f6143b3565b604052919050565b6000610180828403121561447a57600080fd5b6144826143c9565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e08201526101008084013581830152506101208084013581830152506101408084013581830152506101608084013581830152508091505092915050565b60006020828403121561451857600080fd5b5051919050565b60008282101561453157614531614358565b500390565b60005b83811015614551578181015183820152602001614539565b83811115610a7e5750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161459a816017850160208801614536565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516145cb816028840160208801614536565b01602801949350505050565b60208152600082518060208401526145f6816040850160208701614536565b601f01601f19169190910160400192915050565b600081600019048311821515161561462457614624614358565b500290565b60008261464657634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6020808252825182820181905260009190848201906040850190845b818110156146995783518352928401929184019160010161467d565b50909695505050505050565b60006001600160401b038211156146be576146be6143b3565b5060051b60200190565b8051801515811461439157600080fd5b600060208083850312156146eb57600080fd5b82516001600160401b0381111561470157600080fd5b8301601f8101851361471257600080fd5b8051614725614720826146a5565b614437565b81815260c0918202830184019184820191908884111561474457600080fd5b938501935b838510156147c35780858a0312156147615760008081fd5b6147696143f2565b855181528686015187820152604080870151614784816141bf565b9082015260608681015190820152608061479f8188016146c8565b9082015260a06147b08782016146c8565b9082015283529384019391850191614749565b50979650505050505050565b600080604083850312156147e257600080fd5b505080516020909101519092909150565b6000602080838503121561480657600080fd5b82516001600160401b0381111561481c57600080fd5b8301601f8101851361482d57600080fd5b805161483b614720826146a5565b81815260059190911b8201830190838101908783111561485a57600080fd5b928401925b828410156148785783518252928401929084019061485f565b979650505050505050565b805162ffffff8116811461439157600080fd5b805161ffff8116811461439157600080fd5b805160ff8116811461439157600080fd5b600082601f8301126148ca57600080fd5b81516001600160401b038111156148e3576148e36143b3565b6148f6601f8201601f1916602001614437565b81815284602083860101111561490b57600080fd5b6137ae826020830160208701614536565b80516001600160401b038116811461439157600080fd5b60006020828403121561494557600080fd5b81516001600160401b038082111561495c57600080fd5b908301906101a0828603121561497157600080fd5b614979614414565b61498283614883565b815261499060208401614386565b60208201526149a160408401614896565b60408201526149b260608401614896565b60608201526149c360808401614896565b60808201526149d460a084016148a8565b60a082015260c0830151828111156149eb57600080fd5b6149f7878286016148b9565b60c083015250614a0960e0840161491c565b60e0820152610100838101519082015261012080840151908201526101409150614a34828401614896565b828201526101609150614a4882840161491c565b828201526101809150614a5c82840161491c565b91810191909152949350505050565b6000600019821415614a7f57614a7f614358565b5060010190565b634e487b7160e01b600052600160045260246000fd5b600081614aab57614aab614358565b506000190190565b60008083128015600160ff1b850184121615614ad157614ad1614358565b6001600160ff1b0384018313811615614aec57614aec614358565b50500390565b6000600160ff1b821415614aab57614aab614358565b600080600080600060a08688031215614b2057600080fd5b614b29866146c8565b602087015160408801516060890151608090990151929a91995097965090945092505050565b634e487b7160e01b600052603160045260246000fdfe8f8c450dae5029cd48cd91dd9db65da48fb742893edfc7941250f6721d93cbbe9a627a5d4aa7c17f87ff26e3fe9a42c2b6c559e8b41a42282d0ecebb17c0e4d3a2646970667358221220e24ec0641c2c5b0f94e73838bc40e1288565752c5965056848d7b0eb26a4af5f64736f6c63430008090033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c0000000000000000000000000000000000000000000000000000000000002328000000000000000000000000000000000000000000000000000000000000a8c000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000258000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000b71b000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000032
-----Decoded View---------------
Arg [0] : _lidoLocator (address): 0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb
Arg [1] : _admin (address): 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c
Arg [2] : _limitsList (tuple):
Arg [1] : exitedValidatorsPerDayLimit (uint256): 9000
Arg [2] : appearedValidatorsPerDayLimit (uint256): 43200
Arg [3] : annualBalanceIncreaseBPLimit (uint256): 1000
Arg [4] : simulatedShareRateDeviationBPLimit (uint256): 50
Arg [5] : maxValidatorExitRequestsPerReport (uint256): 600
Arg [6] : maxItemsPerExtraDataTransaction (uint256): 8
Arg [7] : maxNodeOperatorsPerExtraDataItem (uint256): 24
Arg [8] : requestTimestampMargin (uint256): 7680
Arg [9] : maxPositiveTokenRebase (uint256): 750000
Arg [10] : initialSlashingAmountPWei (uint256): 1000
Arg [11] : inactivityPenaltiesAmountPWei (uint256): 101
Arg [12] : clBalanceOraclesErrorUpperBPLimit (uint256): 50
-----Encoded View---------------
14 Constructor Arguments found :
Arg [0] : 000000000000000000000000c1d0b3de6792bf6b4b37eccdcc24e45978cfd2eb
Arg [1] : 0000000000000000000000003e40d73eb977dc6a537af587d48316fee66e9c8c
Arg [2] : 0000000000000000000000000000000000000000000000000000000000002328
Arg [3] : 000000000000000000000000000000000000000000000000000000000000a8c0
Arg [4] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000258
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000018
Arg [9] : 0000000000000000000000000000000000000000000000000000000000001e00
Arg [10] : 00000000000000000000000000000000000000000000000000000000000b71b0
Arg [11] : 00000000000000000000000000000000000000000000000000000000000003e8
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000065
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000032
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.