Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| Grant Role | 1223084 | 124 days ago | 0 ETH | ||||
| Revoke Role | 1223084 | 124 days ago | 0 ETH | ||||
| Propose Node Ope... | 1223081 | 124 days ago | 0 ETH | ||||
| Propose Node Ope... | 1223079 | 124 days ago | 0 ETH | ||||
| Grant Role | 1223068 | 124 days ago | 0 ETH | ||||
| Revoke Role | 1223068 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222955 | 124 days ago | 0 ETH | ||||
| Update Depositab... | 1222955 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222955 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222955 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222953 | 124 days ago | 0 ETH | ||||
| Update Depositab... | 1222953 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222953 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222953 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222899 | 124 days ago | 0 ETH | ||||
| Update Depositab... | 1222899 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222899 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222897 | 124 days ago | 0 ETH | ||||
| Update Depositab... | 1222897 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222897 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222895 | 124 days ago | 0 ETH | ||||
| Update Depositab... | 1222895 | 124 days ago | 0 ETH | ||||
| Get Node Operato... | 1222895 | 124 days ago | 0 ETH | ||||
| On Exited And St... | 1222673 | 124 days ago | 0 ETH | ||||
| Get Staking Modu... | 1222673 | 124 days ago | 0 ETH |
Loading...
Loading
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
CSModule
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 160 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { AssetRecoverer } from "./abstract/AssetRecoverer.sol"; import { IStakingModule } from "./interfaces/IStakingModule.sol"; import { ILidoLocator } from "./interfaces/ILidoLocator.sol"; import { IStETH } from "./interfaces/IStETH.sol"; import { ICSParametersRegistry } from "./interfaces/ICSParametersRegistry.sol"; import { ICSAccounting } from "./interfaces/ICSAccounting.sol"; import { ICSExitPenalties } from "./interfaces/ICSExitPenalties.sol"; import { ICSModule, NodeOperator, NodeOperatorManagementProperties, ValidatorWithdrawalInfo } from "./interfaces/ICSModule.sol"; import { ExitPenaltyInfo } from "./interfaces/ICSExitPenalties.sol"; import { PausableUntil } from "./lib/utils/PausableUntil.sol"; import { QueueLib, Batch } from "./lib/QueueLib.sol"; import { ValidatorCountsReport } from "./lib/ValidatorCountsReport.sol"; import { NOAddresses } from "./lib/NOAddresses.sol"; import { TransientUintUintMap, TransientUintUintMapLib } from "./lib/TransientUintUintMapLib.sol"; import { SigningKeys } from "./lib/SigningKeys.sol"; contract CSModule is ICSModule, Initializable, AccessControlEnumerableUpgradeable, PausableUntil, AssetRecoverer { using QueueLib for QueueLib.Queue; bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE"); bytes32 public constant STAKING_ROUTER_ROLE = keccak256("STAKING_ROUTER_ROLE"); bytes32 public constant REPORT_EL_REWARDS_STEALING_PENALTY_ROLE = keccak256("REPORT_EL_REWARDS_STEALING_PENALTY_ROLE"); bytes32 public constant SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE = keccak256("SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE"); bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE"); bytes32 public constant RECOVERER_ROLE = keccak256("RECOVERER_ROLE"); bytes32 public constant CREATE_NODE_OPERATOR_ROLE = keccak256("CREATE_NODE_OPERATOR_ROLE"); uint256 public constant DEPOSIT_SIZE = 32 ether; // @dev see IStakingModule.sol uint8 private constant FORCED_TARGET_LIMIT_MODE_ID = 2; // keccak256(abi.encode(uint256(keccak256("OPERATORS_CREATED_IN_TX_MAP_TSLOT")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OPERATORS_CREATED_IN_TX_MAP_TSLOT = 0x1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a500; bytes32 private immutable MODULE_TYPE; ILidoLocator public immutable LIDO_LOCATOR; IStETH public immutable STETH; ICSParametersRegistry public immutable PARAMETERS_REGISTRY; ICSAccounting public immutable ACCOUNTING; ICSExitPenalties public immutable EXIT_PENALTIES; address public immutable FEE_DISTRIBUTOR; /// @dev QUEUE_LOWEST_PRIORITY identifies the range of available priorities: [0; QUEUE_LOWEST_PRIORITY]. uint256 public immutable QUEUE_LOWEST_PRIORITY; /// @dev QUEUE_LEGACY_PRIORITY is the priority for the CSM v1 queue. uint256 public immutable QUEUE_LEGACY_PRIORITY; //////////////////////// // State variables below //////////////////////// /// @custom:oz-renamed-from keyRemovalCharge /// @custom:oz-retyped-from uint256 mapping(uint256 queuePriority => QueueLib.Queue queue) internal _queueByPriority; /// @dev Legacy queue (priority=QUEUE_LEGACY_PRIORITY), that should be removed in the future once there are no more batches in it. /// @custom:oz-renamed-from depositQueue QueueLib.Queue internal _legacyQueue; /// @dev Unused. Nullified in the finalizeUpgradeV2 /// @custom:oz-renamed-from accounting ICSAccounting internal _accountingOld; /// @dev Unused. Nullified in the finalizeUpgradeV2 /// @custom:oz-renamed-from earlyAdoption address internal _earlyAdoption; /// @dev deprecated. Nullified in the finalizeUpgradeV2 /// @custom:oz-renamed-from publicRelease bool internal _publicRelease; uint256 private _nonce; mapping(uint256 => NodeOperator) private _nodeOperators; /// @dev see _keyPointer function for details of noKeyIndexPacked structure mapping(uint256 noKeyIndexPacked => bool) private _isValidatorWithdrawn; /// @dev DEPRECATED! No writes expected after CSM v2 mapping(uint256 noKeyIndexPacked => bool) private _isValidatorSlashed; uint64 private _totalDepositedValidators; uint64 private _totalExitedValidators; uint64 private _depositableValidatorsCount; uint64 private _nodeOperatorsCount; constructor( bytes32 moduleType, address lidoLocator, address parametersRegistry, address _accounting, address exitPenalties ) { if (lidoLocator == address(0)) { revert ZeroLocatorAddress(); } if (parametersRegistry == address(0)) { revert ZeroParametersRegistryAddress(); } if (_accounting == address(0)) { revert ZeroAccountingAddress(); } if (exitPenalties == address(0)) { revert ZeroExitPenaltiesAddress(); } MODULE_TYPE = moduleType; LIDO_LOCATOR = ILidoLocator(lidoLocator); STETH = IStETH(LIDO_LOCATOR.lido()); PARAMETERS_REGISTRY = ICSParametersRegistry(parametersRegistry); QUEUE_LOWEST_PRIORITY = PARAMETERS_REGISTRY.QUEUE_LOWEST_PRIORITY(); QUEUE_LEGACY_PRIORITY = PARAMETERS_REGISTRY.QUEUE_LEGACY_PRIORITY(); ACCOUNTING = ICSAccounting(_accounting); EXIT_PENALTIES = ICSExitPenalties(exitPenalties); FEE_DISTRIBUTOR = address(ACCOUNTING.feeDistributor()); _disableInitializers(); } /// @notice initialize the module from scratch function initialize(address admin) external reinitializer(2) { if (admin == address(0)) { revert ZeroAdminAddress(); } __AccessControlEnumerable_init(); _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(STAKING_ROUTER_ROLE, address(LIDO_LOCATOR.stakingRouter())); // CSM is on pause initially and should be resumed during the vote _pauseFor(PausableUntil.PAUSE_INFINITELY); } /// @dev should be called after update on the proxy function finalizeUpgradeV2() external reinitializer(2) { assembly ("memory-safe") { sstore(_queueByPriority.slot, 0x00) sstore(_earlyAdoption.slot, 0x00) sstore(_accountingOld.slot, 0x00) } } /// @inheritdoc ICSModule function resume() external onlyRole(RESUME_ROLE) { _resume(); } /// @inheritdoc ICSModule function pauseFor(uint256 duration) external onlyRole(PAUSE_ROLE) { _pauseFor(duration); } /// @inheritdoc ICSModule function createNodeOperator( address from, NodeOperatorManagementProperties calldata managementProperties, address referrer ) external onlyRole(CREATE_NODE_OPERATOR_ROLE) whenResumed returns (uint256 nodeOperatorId) { if (from == address(0)) { revert ZeroSenderAddress(); } nodeOperatorId = _nodeOperatorsCount; _recordOperatorCreator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; address managerAddress = managementProperties.managerAddress == address(0) ? from : managementProperties.managerAddress; address rewardAddress = managementProperties.rewardAddress == address(0) ? from : managementProperties.rewardAddress; no.managerAddress = managerAddress; no.rewardAddress = rewardAddress; if (managementProperties.extendedManagerPermissions) { no.extendedManagerPermissions = true; } unchecked { ++_nodeOperatorsCount; } emit NodeOperatorAdded( nodeOperatorId, managerAddress, rewardAddress, managementProperties.extendedManagerPermissions ); if (referrer != address(0)) { emit ReferrerSet(nodeOperatorId, referrer); } _incrementModuleNonce(); } /// @inheritdoc ICSModule function addValidatorKeysETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes calldata publicKeys, bytes calldata signatures ) external payable whenResumed { _checkCanAddKeys(nodeOperatorId, from); if ( msg.value < accounting().getRequiredBondForNextKeys(nodeOperatorId, keysCount) ) { revert InvalidAmount(); } if (msg.value != 0) { accounting().depositETH{ value: msg.value }(from, nodeOperatorId); } _addKeysAndUpdateDepositableValidatorsCount( nodeOperatorId, keysCount, publicKeys, signatures ); } /// @inheritdoc ICSModule function addValidatorKeysStETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes calldata publicKeys, bytes calldata signatures, ICSAccounting.PermitInput calldata permit ) external whenResumed { _checkCanAddKeys(nodeOperatorId, from); uint256 amount = accounting().getRequiredBondForNextKeys( nodeOperatorId, keysCount ); if (amount != 0) { accounting().depositStETH(from, nodeOperatorId, amount, permit); } _addKeysAndUpdateDepositableValidatorsCount( nodeOperatorId, keysCount, publicKeys, signatures ); } /// @inheritdoc ICSModule function addValidatorKeysWstETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes calldata publicKeys, bytes calldata signatures, ICSAccounting.PermitInput calldata permit ) external whenResumed { _checkCanAddKeys(nodeOperatorId, from); uint256 amount = accounting().getRequiredBondForNextKeysWstETH( nodeOperatorId, keysCount ); if (amount != 0) { accounting().depositWstETH(from, nodeOperatorId, amount, permit); } _addKeysAndUpdateDepositableValidatorsCount( nodeOperatorId, keysCount, publicKeys, signatures ); } /// @inheritdoc ICSModule function proposeNodeOperatorManagerAddressChange( uint256 nodeOperatorId, address proposedAddress ) external { NOAddresses.proposeNodeOperatorManagerAddressChange( _nodeOperators, nodeOperatorId, proposedAddress ); } /// @inheritdoc ICSModule function confirmNodeOperatorManagerAddressChange( uint256 nodeOperatorId ) external { NOAddresses.confirmNodeOperatorManagerAddressChange( _nodeOperators, nodeOperatorId ); } /// @inheritdoc ICSModule function proposeNodeOperatorRewardAddressChange( uint256 nodeOperatorId, address proposedAddress ) external { NOAddresses.proposeNodeOperatorRewardAddressChange( _nodeOperators, nodeOperatorId, proposedAddress ); } /// @inheritdoc ICSModule function confirmNodeOperatorRewardAddressChange( uint256 nodeOperatorId ) external { NOAddresses.confirmNodeOperatorRewardAddressChange( _nodeOperators, nodeOperatorId ); } /// @inheritdoc ICSModule function resetNodeOperatorManagerAddress(uint256 nodeOperatorId) external { NOAddresses.resetNodeOperatorManagerAddress( _nodeOperators, nodeOperatorId ); } /// @inheritdoc ICSModule function changeNodeOperatorRewardAddress( uint256 nodeOperatorId, address newAddress ) external { NOAddresses.changeNodeOperatorRewardAddress( _nodeOperators, nodeOperatorId, newAddress ); } /// @inheritdoc IStakingModule /// @dev Passes through the minted stETH shares to the fee distributor function onRewardsMinted( uint256 totalShares ) external onlyRole(STAKING_ROUTER_ROLE) { STETH.transferShares(FEE_DISTRIBUTOR, totalShares); } /// @inheritdoc IStakingModule function updateExitedValidatorsCount( bytes calldata nodeOperatorIds, bytes calldata exitedValidatorsCounts ) external onlyRole(STAKING_ROUTER_ROLE) { uint256 operatorsInReport = ValidatorCountsReport.safeCountOperators( nodeOperatorIds, exitedValidatorsCounts ); for (uint256 i = 0; i < operatorsInReport; ++i) { ( uint256 nodeOperatorId, uint256 exitedValidatorsCount ) = ValidatorCountsReport.next( nodeOperatorIds, exitedValidatorsCounts, i ); _updateExitedValidatorsCount({ nodeOperatorId: nodeOperatorId, exitedValidatorsCount: exitedValidatorsCount, allowDecrease: false }); } _incrementModuleNonce(); } /// @inheritdoc IStakingModule function updateTargetValidatorsLimits( uint256 nodeOperatorId, uint256 targetLimitMode, uint256 targetLimit ) external onlyRole(STAKING_ROUTER_ROLE) { if (targetLimitMode > FORCED_TARGET_LIMIT_MODE_ID) { revert InvalidInput(); } if (targetLimit > type(uint32).max) { revert InvalidInput(); } _onlyExistingNodeOperator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; if (targetLimitMode == 0) { targetLimit = 0; } // NOTE: Bytecode saving trick; increased gas cost in rare cases is fine. // if ( // no.targetLimitMode == targetLimitMode && // no.targetLimit == targetLimit // ) { // return; // } // @dev No need to safe cast due to conditions above no.targetLimitMode = uint8(targetLimitMode); no.targetLimit = uint32(targetLimit); emit TargetValidatorsCountChanged( nodeOperatorId, targetLimitMode, targetLimit ); // Nonce will be updated below even if depositable count was not changed _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: false }); _incrementModuleNonce(); } /// @inheritdoc IStakingModule /// @dev This method is not used in CSM, hence it does nothing /// @dev NOTE: No role checks because of empty body to save bytecode. function onExitedAndStuckValidatorsCountsUpdated() external { // solhint-disable-previous-line no-empty-blocks // Nothing to do, rewards are distributed by a performance oracle. } /// @inheritdoc IStakingModule function unsafeUpdateValidatorsCount( uint256 nodeOperatorId, uint256 exitedValidatorsKeysCount ) external onlyRole(STAKING_ROUTER_ROLE) { _updateExitedValidatorsCount({ nodeOperatorId: nodeOperatorId, exitedValidatorsCount: exitedValidatorsKeysCount, allowDecrease: true }); _incrementModuleNonce(); } /// @inheritdoc IStakingModule function decreaseVettedSigningKeysCount( bytes calldata nodeOperatorIds, bytes calldata vettedSigningKeysCounts ) external onlyRole(STAKING_ROUTER_ROLE) { uint256 operatorsInReport = ValidatorCountsReport.safeCountOperators( nodeOperatorIds, vettedSigningKeysCounts ); for (uint256 i = 0; i < operatorsInReport; ++i) { ( uint256 nodeOperatorId, uint256 vettedSigningKeysCount ) = ValidatorCountsReport.next( nodeOperatorIds, vettedSigningKeysCounts, i ); _onlyExistingNodeOperator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; if (vettedSigningKeysCount >= no.totalVettedKeys) { revert InvalidVetKeysPointer(); } if (vettedSigningKeysCount < no.totalDepositedKeys) { revert InvalidVetKeysPointer(); } // @dev No need to safe cast due to conditions above no.totalVettedKeys = uint32(vettedSigningKeysCount); emit VettedSigningKeysCountChanged( nodeOperatorId, vettedSigningKeysCount ); // @dev separate event for intentional decrease from Staking Router emit VettedSigningKeysCountDecreased(nodeOperatorId); // Nonce will be updated below once _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: false }); } _incrementModuleNonce(); } /// @inheritdoc ICSModule function removeKeys( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external { _onlyNodeOperatorManager(nodeOperatorId, msg.sender); NodeOperator storage no = _nodeOperators[nodeOperatorId]; if (startIndex < no.totalDepositedKeys) { revert SigningKeysInvalidOffset(); } // solhint-disable-next-line func-named-parameters uint256 newTotalSigningKeys = SigningKeys.removeKeysSigs( nodeOperatorId, startIndex, keysCount, no.totalAddedKeys ); // The Node Operator is charged for the every removed key. It's motivated by the fact that the DAO should cleanup // the queue from the empty batches related to the Node Operator. It's possible to have multiple batches with only one // key in it, so it means the DAO should be able to cover removal costs for as much batches as keys removed in this case. uint256 curveId = accounting().getBondCurveId(nodeOperatorId); uint256 amountToCharge = PARAMETERS_REGISTRY.getKeyRemovalCharge( curveId ) * keysCount; if (amountToCharge != 0) { accounting().chargeFee(nodeOperatorId, amountToCharge); emit KeyRemovalChargeApplied(nodeOperatorId); } // @dev No need to safe cast due to checks in the func above no.totalAddedKeys = uint32(newTotalSigningKeys); emit TotalSigningKeysCountChanged(nodeOperatorId, newTotalSigningKeys); // @dev No need to safe cast due to checks in the func above no.totalVettedKeys = uint32(newTotalSigningKeys); emit VettedSigningKeysCountChanged(nodeOperatorId, newTotalSigningKeys); // Nonce is updated below due to keys state change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: false }); _incrementModuleNonce(); } /// @inheritdoc ICSModule function updateDepositableValidatorsCount(uint256 nodeOperatorId) external { _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: true }); } /// @inheritdoc ICSModule function migrateToPriorityQueue(uint256 nodeOperatorId) external { NodeOperator storage no = _nodeOperators[nodeOperatorId]; if (no.usedPriorityQueue) { revert PriorityQueueAlreadyUsed(); } uint256 curveId = accounting().getBondCurveId(nodeOperatorId); (uint32 priority, uint32 maxDeposits) = PARAMETERS_REGISTRY .getQueueConfig(curveId); if (priority == QUEUE_LOWEST_PRIORITY) { revert NotEligibleForPriorityQueue(); } uint32 enqueued = no.enqueuedCount; if (enqueued == 0) { revert NoQueuedKeysToMigrate(); } uint32 deposited = no.totalDepositedKeys; if (maxDeposits <= deposited) { revert PriorityQueueMaxDepositsUsed(); } uint32 toMigrate = uint32(Math.min(enqueued, maxDeposits - deposited)); _enqueueNodeOperatorKeys(nodeOperatorId, priority, toMigrate); no.usedPriorityQueue = true; _incrementModuleNonce(); // An alternative version to fit into the bytecode requirements is below. Please consider // the described caveat of the approach. // NOTE: We allow a node operator (NO) to reset their enqueued counter to zero only once to // migrate their keys from the legacy queue to a priority queue, if any. As a downside, the // node operator effectively can have their seats doubled in the queue. // Let's say we have a priority queue with a maximum of 10 deposits. Imagine a NO has 20 // keys queued in the legacy queue. Then, the NO calls this method and gets their enqueued // counter reset to zero. As a result, the module will place 10 keys into the priority queue // and 10 more keys at the end of the overall queue. The original batches are kept in the // queue, so in total, the NO will have batches with 40 keys queued altogether. // _nodeOperators[nodeOperatorId].enqueuedCount = 0; // _enqueueNodeOperatorKeys(nodeOperatorId); } /// @inheritdoc ICSModule function reportELRewardsStealingPenalty( uint256 nodeOperatorId, bytes32 blockHash, uint256 amount ) external onlyRole(REPORT_EL_REWARDS_STEALING_PENALTY_ROLE) { _onlyExistingNodeOperator(nodeOperatorId); if (amount == 0) { revert InvalidAmount(); } uint256 curveId = accounting().getBondCurveId(nodeOperatorId); uint256 additionalFine = PARAMETERS_REGISTRY .getElRewardsStealingAdditionalFine(curveId); accounting().lockBondETH(nodeOperatorId, amount + additionalFine); emit ELRewardsStealingPenaltyReported( nodeOperatorId, blockHash, amount ); // Nonce should be updated if depositableValidators change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: true }); } /// @inheritdoc ICSModule function cancelELRewardsStealingPenalty( uint256 nodeOperatorId, uint256 amount ) external onlyRole(REPORT_EL_REWARDS_STEALING_PENALTY_ROLE) { _onlyExistingNodeOperator(nodeOperatorId); accounting().releaseLockedBondETH(nodeOperatorId, amount); emit ELRewardsStealingPenaltyCancelled(nodeOperatorId, amount); // Nonce should be updated if depositableValidators change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: true }); } /// @inheritdoc ICSModule function settleELRewardsStealingPenalty( uint256[] calldata nodeOperatorIds ) external onlyRole(SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE) { for (uint256 i; i < nodeOperatorIds.length; ++i) { uint256 nodeOperatorId = nodeOperatorIds[i]; _onlyExistingNodeOperator(nodeOperatorId); // Settled amount might be zero either if the lock expired, or the bond is zero so we // need to check if the penalty was applied. bool applied = accounting().settleLockedBondETH(nodeOperatorId); if (applied) { emit ELRewardsStealingPenaltySettled(nodeOperatorId); // Nonce should be updated if depositableValidators change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: true }); } } } /// @inheritdoc ICSModule function compensateELRewardsStealingPenalty( uint256 nodeOperatorId ) external payable { _onlyNodeOperatorManager(nodeOperatorId, msg.sender); accounting().compensateLockedBondETH{ value: msg.value }( nodeOperatorId ); emit ELRewardsStealingPenaltyCompensated(nodeOperatorId, msg.value); // Nonce should be updated if depositableValidators change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: true }); } /// @inheritdoc ICSModule function submitWithdrawals( ValidatorWithdrawalInfo[] calldata withdrawalsInfo ) external onlyRole(VERIFIER_ROLE) { bool anySubmission = false; for (uint256 i; i < withdrawalsInfo.length; ++i) { ValidatorWithdrawalInfo memory withdrawalInfo = withdrawalsInfo[i]; _onlyExistingNodeOperator(withdrawalInfo.nodeOperatorId); NodeOperator storage no = _nodeOperators[ withdrawalInfo.nodeOperatorId ]; if (withdrawalInfo.keyIndex >= no.totalDepositedKeys) { revert SigningKeysInvalidOffset(); } uint256 pointer = _keyPointer( withdrawalInfo.nodeOperatorId, withdrawalInfo.keyIndex ); if (_isValidatorWithdrawn[pointer]) { continue; } _isValidatorWithdrawn[pointer] = true; unchecked { ++no.totalWithdrawnKeys; } bytes memory pubkey = SigningKeys.loadKeys( withdrawalInfo.nodeOperatorId, withdrawalInfo.keyIndex, 1 ); emit WithdrawalSubmitted( withdrawalInfo.nodeOperatorId, withdrawalInfo.keyIndex, withdrawalInfo.amount, pubkey ); anySubmission = true; // It is safe to use unchecked for penalty sum because it's limited to uint248 in the structure. uint256 penaltySum; bool chargeWithdrawalRequestFee; ExitPenaltyInfo memory exitPenaltyInfo = EXIT_PENALTIES .getExitPenaltyInfo(withdrawalInfo.nodeOperatorId, pubkey); if (exitPenaltyInfo.delayPenalty.isValue) { unchecked { penaltySum += exitPenaltyInfo.delayPenalty.value; } chargeWithdrawalRequestFee = true; } if (exitPenaltyInfo.strikesPenalty.isValue) { unchecked { penaltySum += exitPenaltyInfo.strikesPenalty.value; } chargeWithdrawalRequestFee = true; } // The withdrawal request fee is taken only if the penalty is applied if no penalty, the // fee has been paid by the node operator on the withdrawal trigger, or it is the DAO // decision to withdraw the validator before that the withdrawal request becomes // delayed. if ( chargeWithdrawalRequestFee && exitPenaltyInfo.withdrawalRequestFee.value != 0 ) { accounting().chargeFee( withdrawalInfo.nodeOperatorId, exitPenaltyInfo.withdrawalRequestFee.value ); } if (DEPOSIT_SIZE > withdrawalInfo.amount) { unchecked { penaltySum += DEPOSIT_SIZE - withdrawalInfo.amount; } } if (penaltySum > 0) { accounting().penalize( withdrawalInfo.nodeOperatorId, penaltySum ); } // Nonce will be updated below even if depositable count was not changed _updateDepositableValidatorsCount({ nodeOperatorId: withdrawalInfo.nodeOperatorId, incrementNonceIfUpdated: false }); } if (anySubmission) { _incrementModuleNonce(); } } /// @inheritdoc IStakingModule /// @dev Changing the WC means that the current deposit data in the queue is not valid anymore and can't be deposited. /// DSM will unvet current keys. /// The key removal charge should be reset to 0 to allow Node Operators to remove the keys without any charge. /// After keys removal the DAO should set the new key removal charge. function onWithdrawalCredentialsChanged() external onlyRole(STAKING_ROUTER_ROLE) { _incrementModuleNonce(); } /// @inheritdoc IStakingModule function reportValidatorExitDelay( uint256 nodeOperatorId, uint256 /* proofSlotTimestamp */, bytes calldata publicKey, uint256 eligibleToExitInSec ) external onlyRole(STAKING_ROUTER_ROLE) { _onlyExistingNodeOperator(nodeOperatorId); EXIT_PENALTIES.processExitDelayReport( nodeOperatorId, publicKey, eligibleToExitInSec ); } /// @inheritdoc IStakingModule function onValidatorExitTriggered( uint256 nodeOperatorId, bytes calldata publicKey, uint256 withdrawalRequestPaidFee, uint256 exitType ) external onlyRole(STAKING_ROUTER_ROLE) { _onlyExistingNodeOperator(nodeOperatorId); EXIT_PENALTIES.processTriggeredExit( nodeOperatorId, publicKey, withdrawalRequestPaidFee, exitType ); } /// @inheritdoc IStakingModule /// @notice Get the next `depositsCount` of depositable keys with signatures from the queue /// @dev Second param `depositCalldata` is not used function obtainDepositData( uint256 depositsCount, bytes calldata /* depositCalldata */ ) external onlyRole(STAKING_ROUTER_ROLE) returns (bytes memory publicKeys, bytes memory signatures) { (publicKeys, signatures) = SigningKeys.initKeysSigsBuf(depositsCount); if (depositsCount == 0) { return (publicKeys, signatures); } uint256 depositsLeft = depositsCount; uint256 loadedKeysCount = 0; QueueLib.Queue storage queue; // Note: The highest priority to start iterations with. Priorities are ordered like 0, 1, 2, ... uint256 priority = 0; while (true) { if (priority > QUEUE_LOWEST_PRIORITY || depositsLeft == 0) { break; } queue = _getQueue(priority); unchecked { // Note: unused below ++priority; } for ( Batch item = queue.peek(); !item.isNil(); item = queue.peek() ) { // NOTE: see the `enqueuedCount` note below. unchecked { uint256 noId = item.noId(); uint256 keysInBatch = item.keys(); NodeOperator storage no = _nodeOperators[noId]; uint256 keysCount = Math.min( Math.min(no.depositableValidatorsCount, keysInBatch), depositsLeft ); // `depositsLeft` is non-zero at this point all the time, so the check `depositsLeft > keysCount` // covers the case when no depositable keys on the Node Operator have been left. if (depositsLeft > keysCount || keysCount == keysInBatch) { // NOTE: `enqueuedCount` >= keysInBatch invariant should be checked. // @dev No need to safe cast due to internal logic no.enqueuedCount -= uint32(keysInBatch); // We've consumed all the keys in the batch, so we dequeue it. queue.dequeue(); } else { // This branch covers the case when we stop in the middle of the batch. // We release the amount of keys consumed only, the rest will be kept. // @dev No need to safe cast due to internal logic no.enqueuedCount -= uint32(keysCount); // NOTE: `keysInBatch` can't be less than `keysCount` at this point. // We update the batch with the remaining keys. item = item.setKeys(keysInBatch - keysCount); // Store the updated batch back to the queue. queue.queue[queue.head] = item; } // Note: This condition is located here to allow for the correct removal of the batch for the Node Operators with no depositable keys if (keysCount == 0) { continue; } // solhint-disable-next-line func-named-parameters SigningKeys.loadKeysSigs( noId, no.totalDepositedKeys, keysCount, publicKeys, signatures, loadedKeysCount ); // It's impossible in practice to reach the limit of these variables. loadedKeysCount += keysCount; // @dev No need to safe cast due to internal logic uint32 totalDepositedKeys = no.totalDepositedKeys + uint32(keysCount); no.totalDepositedKeys = totalDepositedKeys; emit DepositedSigningKeysCountChanged( noId, totalDepositedKeys ); // No need for `_updateDepositableValidatorsCount` call since we update the number directly. // `keysCount` is min of `depositableValidatorsCount` and `depositsLeft`. // @dev No need to safe cast due to internal logic uint32 newCount = no.depositableValidatorsCount - uint32(keysCount); no.depositableValidatorsCount = newCount; emit DepositableSigningKeysCountChanged(noId, newCount); depositsLeft -= keysCount; if (depositsLeft == 0) { break; } } } } if (loadedKeysCount != depositsCount) { revert NotEnoughKeys(); } unchecked { // @dev depositsCount can not overflow in practice due to memory and gas limits _depositableValidatorsCount -= uint64(depositsCount); _totalDepositedValidators += uint64(depositsCount); } _incrementModuleNonce(); } /// @inheritdoc ICSModule function cleanDepositQueue( uint256 maxItems ) external returns (uint256 removed, uint256 lastRemovedAtDepth) { removed = 0; lastRemovedAtDepth = 0; if (maxItems == 0) { return (0, 0); } // NOTE: We need one unique hash map per function invocation to be able to track batches of // the same operator across multiple queues. TransientUintUintMap queueLookup = TransientUintUintMapLib.create(); QueueLib.Queue storage queue; uint256 totalVisited = 0; // Note: The highest priority to start iterations with. Priorities are ordered like 0, 1, 2, ... uint256 priority = 0; while (true) { if (priority > QUEUE_LOWEST_PRIORITY) { break; } queue = _getQueue(priority); unchecked { ++priority; } ( uint256 removedPerQueue, uint256 lastRemovedAtDepthPerQueue, uint256 visitedPerQueue, bool reachedOutOfQueue ) = queue.clean(_nodeOperators, maxItems, queueLookup); if (removedPerQueue > 0) { unchecked { // 1234 56 789A <- cumulative depth (A=10) // 1234 12 1234 <- depth per queue // **R*|**|**R* <- queue with [R]emoved elements // // Given that we observed all 3 queues: // totalVisited: 4+2=6 // lastRemovedAtDepthPerQueue: 3 // lastRemovedAtDepth: 6+3=9 lastRemovedAtDepth = totalVisited + lastRemovedAtDepthPerQueue; removed += removedPerQueue; } } // NOTE: If `maxItems` is set to the total length of the queue(s), `reachedOutOfQueue` is equal // to `false`, effectively breaking the cycle, because in `QueueLib.clean` we don't reach // an empty batch after the end of a queue. if (!reachedOutOfQueue) { break; } unchecked { totalVisited += visitedPerQueue; maxItems -= visitedPerQueue; } } } /// @inheritdoc ICSModule function getInitializedVersion() external view returns (uint64) { return _getInitializedVersion(); } /// @inheritdoc ICSModule function depositQueuePointers( uint256 queuePriority ) external view returns (uint128 head, uint128 tail) { QueueLib.Queue storage q = _getQueue(queuePriority); return (q.head, q.tail); } /// @inheritdoc ICSModule function depositQueueItem( uint256 queuePriority, uint128 index ) external view returns (Batch) { return _getQueue(queuePriority).at(index); } /// @inheritdoc ICSModule function isValidatorWithdrawn( uint256 nodeOperatorId, uint256 keyIndex ) external view returns (bool) { return _isValidatorWithdrawn[_keyPointer(nodeOperatorId, keyIndex)]; } /// @inheritdoc IStakingModule function getType() external view returns (bytes32) { return MODULE_TYPE; } /// @inheritdoc IStakingModule function getStakingModuleSummary() external view returns ( uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount ) { totalExitedValidators = _totalExitedValidators; totalDepositedValidators = _totalDepositedValidators; depositableValidatorsCount = _depositableValidatorsCount; } /// @inheritdoc ICSModule function getNodeOperator( uint256 nodeOperatorId ) external view returns (NodeOperator memory) { return _nodeOperators[nodeOperatorId]; } /// @inheritdoc ICSModule function getNodeOperatorManagementProperties( uint256 nodeOperatorId ) external view returns (NodeOperatorManagementProperties memory) { NodeOperator storage no = _nodeOperators[nodeOperatorId]; return ( NodeOperatorManagementProperties( no.managerAddress, no.rewardAddress, no.extendedManagerPermissions ) ); } /// @inheritdoc ICSModule function getNodeOperatorOwner( uint256 nodeOperatorId ) external view returns (address) { NodeOperator storage no = _nodeOperators[nodeOperatorId]; return no.extendedManagerPermissions ? no.managerAddress : no.rewardAddress; } /// @inheritdoc ICSModule function getNodeOperatorNonWithdrawnKeys( uint256 nodeOperatorId ) external view returns (uint256) { NodeOperator storage no = _nodeOperators[nodeOperatorId]; unchecked { return no.totalAddedKeys - no.totalWithdrawnKeys; } } /// @inheritdoc IStakingModule /// @notice depositableValidatorsCount depends on: /// - totalVettedKeys /// - totalDepositedKeys /// - totalExitedKeys /// - targetLimitMode /// - targetValidatorsCount /// - totalUnbondedKeys function getNodeOperatorSummary( uint256 nodeOperatorId ) external view returns ( uint256 targetLimitMode, uint256 targetValidatorsCount, uint256 stuckValidatorsCount, uint256 refundedValidatorsCount, uint256 stuckPenaltyEndTimestamp, uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount ) { _onlyExistingNodeOperator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; uint256 totalUnbondedKeys = accounting().getUnbondedKeysCountToEject( nodeOperatorId ); uint256 totalNonDepositedKeys = no.totalAddedKeys - no.totalDepositedKeys; // Force mode enabled and unbonded deposited keys if ( totalUnbondedKeys > totalNonDepositedKeys && no.targetLimitMode == FORCED_TARGET_LIMIT_MODE_ID ) { targetLimitMode = FORCED_TARGET_LIMIT_MODE_ID; unchecked { targetValidatorsCount = Math.min( no.targetLimit, no.totalAddedKeys - no.totalWithdrawnKeys - totalUnbondedKeys ); } // No force mode enabled but unbonded deposited keys } else if (totalUnbondedKeys > totalNonDepositedKeys) { targetLimitMode = FORCED_TARGET_LIMIT_MODE_ID; unchecked { targetValidatorsCount = no.totalAddedKeys - no.totalWithdrawnKeys - totalUnbondedKeys; } } else { targetLimitMode = no.targetLimitMode; targetValidatorsCount = no.targetLimit; } stuckValidatorsCount = 0; refundedValidatorsCount = 0; stuckPenaltyEndTimestamp = 0; totalExitedValidators = no.totalExitedKeys; totalDepositedValidators = no.totalDepositedKeys; depositableValidatorsCount = no.depositableValidatorsCount; } /// @inheritdoc ICSModule function getNodeOperatorTotalDepositedKeys( uint256 nodeOperatorId ) external view returns (uint256 totalDepositedKeys) { totalDepositedKeys = _nodeOperators[nodeOperatorId].totalDepositedKeys; } /// @inheritdoc ICSModule function getSigningKeys( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external view returns (bytes memory) { _onlyValidIndexRange(nodeOperatorId, startIndex, keysCount); return SigningKeys.loadKeys(nodeOperatorId, startIndex, keysCount); } /// @inheritdoc ICSModule function getSigningKeysWithSignatures( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external view returns (bytes memory keys, bytes memory signatures) { _onlyValidIndexRange(nodeOperatorId, startIndex, keysCount); (keys, signatures) = SigningKeys.initKeysSigsBuf(keysCount); // solhint-disable-next-line func-named-parameters SigningKeys.loadKeysSigs( nodeOperatorId, startIndex, keysCount, keys, signatures, 0 ); } /// @inheritdoc IStakingModule function getNonce() external view returns (uint256) { return _nonce; } /// @inheritdoc IStakingModule function getNodeOperatorsCount() external view returns (uint256) { return _nodeOperatorsCount; } /// @inheritdoc IStakingModule function getActiveNodeOperatorsCount() external view returns (uint256) { return _nodeOperatorsCount; } /// @inheritdoc IStakingModule function getNodeOperatorIsActive( uint256 nodeOperatorId ) external view returns (bool) { return nodeOperatorId < _nodeOperatorsCount; } /// @inheritdoc IStakingModule function getNodeOperatorIds( uint256 offset, uint256 limit ) external view returns (uint256[] memory nodeOperatorIds) { uint256 nodeOperatorsCount = _nodeOperatorsCount; if (offset >= nodeOperatorsCount || limit == 0) { return new uint256[](0); } uint256 idsCount = limit < nodeOperatorsCount - offset ? limit : nodeOperatorsCount - offset; nodeOperatorIds = new uint256[](idsCount); for (uint256 i = 0; i < nodeOperatorIds.length; ++i) { nodeOperatorIds[i] = offset + i; } } /// @inheritdoc IStakingModule function isValidatorExitDelayPenaltyApplicable( uint256 nodeOperatorId, uint256 /* proofSlotTimestamp */, bytes calldata publicKey, uint256 eligibleToExitInSec ) external view returns (bool) { _onlyExistingNodeOperator(nodeOperatorId); return EXIT_PENALTIES.isValidatorExitDelayPenaltyApplicable( nodeOperatorId, publicKey, eligibleToExitInSec ); } /// @inheritdoc IStakingModule function exitDeadlineThreshold( uint256 nodeOperatorId ) external view returns (uint256) { _onlyExistingNodeOperator(nodeOperatorId); return PARAMETERS_REGISTRY.getAllowedExitDelay( accounting().getBondCurveId(nodeOperatorId) ); } /// @dev This function is used to get the accounting contract from immutables to save bytecode and for backwards compatibility function accounting() public view returns (ICSAccounting) { return ACCOUNTING; } function _incrementModuleNonce() internal { unchecked { emit NonceChanged(++_nonce); } } function _addKeysAndUpdateDepositableValidatorsCount( uint256 nodeOperatorId, uint256 keysCount, bytes calldata publicKeys, bytes calldata signatures ) internal { // Do not allow of multiple calls of addValidatorKeys* methods. _forgetOperatorCreator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; uint256 totalAddedKeys = no.totalAddedKeys; uint256 curveId = accounting().getBondCurveId(nodeOperatorId); uint256 keysLimit = PARAMETERS_REGISTRY.getKeysLimit(curveId); unchecked { if ( totalAddedKeys + keysCount - no.totalWithdrawnKeys > keysLimit ) { revert KeysLimitExceeded(); } // solhint-disable-next-line func-named-parameters uint256 newTotalAddedKeys = SigningKeys.saveKeysSigs( nodeOperatorId, totalAddedKeys, keysCount, publicKeys, signatures ); // Optimistic vetting takes place. if (totalAddedKeys == no.totalVettedKeys) { // @dev No need to safe cast due to internal logic uint32 totalVettedKeys = no.totalVettedKeys + uint32(keysCount); no.totalVettedKeys = totalVettedKeys; emit VettedSigningKeysCountChanged( nodeOperatorId, totalVettedKeys ); } // @dev No need to safe cast due to internal logic no.totalAddedKeys = uint32(newTotalAddedKeys); emit TotalSigningKeysCountChanged( nodeOperatorId, newTotalAddedKeys ); } // Nonce is updated below since in case of target limit depositable keys might not change _updateDepositableValidatorsCount({ nodeOperatorId: nodeOperatorId, incrementNonceIfUpdated: false }); _incrementModuleNonce(); } /// @dev Update exited validators count for a single Node Operator /// @dev Allows decrease the count for unsafe updates function _updateExitedValidatorsCount( uint256 nodeOperatorId, uint256 exitedValidatorsCount, bool allowDecrease ) internal { _onlyExistingNodeOperator(nodeOperatorId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; uint32 totalExitedKeys = no.totalExitedKeys; if (exitedValidatorsCount == totalExitedKeys) { return; } if (exitedValidatorsCount > no.totalDepositedKeys) { revert ExitedKeysHigherThanTotalDeposited(); } if (!allowDecrease && exitedValidatorsCount < totalExitedKeys) { revert ExitedKeysDecrease(); } unchecked { // @dev Invariant sum(no.totalExitedKeys for no in nos) == _totalExitedValidators. _totalExitedValidators = (_totalExitedValidators - totalExitedKeys) + uint64(exitedValidatorsCount); } // @dev No need to safe cast due to conditions above no.totalExitedKeys = uint32(exitedValidatorsCount); emit ExitedSigningKeysCountChanged( nodeOperatorId, exitedValidatorsCount ); } function _updateDepositableValidatorsCount( uint256 nodeOperatorId, bool incrementNonceIfUpdated ) internal { NodeOperator storage no = _nodeOperators[nodeOperatorId]; uint32 totalDepositedKeys = no.totalDepositedKeys; uint256 newCount = no.totalVettedKeys - totalDepositedKeys; uint256 unbondedKeys = accounting().getUnbondedKeysCount( nodeOperatorId ); { uint256 nonDeposited = no.totalAddedKeys - totalDepositedKeys; if (unbondedKeys >= nonDeposited) { newCount = 0; } else if (unbondedKeys > no.totalAddedKeys - no.totalVettedKeys) { newCount = nonDeposited - unbondedKeys; } } if (no.targetLimitMode > 0 && newCount > 0) { unchecked { uint256 nonWithdrawnValidators = totalDepositedKeys - no.totalWithdrawnKeys; newCount = Math.min( no.targetLimit > nonWithdrawnValidators ? no.targetLimit - nonWithdrawnValidators : 0, newCount ); } } if (no.depositableValidatorsCount != newCount) { // Updating the global counter. // @dev No need to safe cast due to internal logic unchecked { _depositableValidatorsCount = _depositableValidatorsCount - no.depositableValidatorsCount + uint64(newCount); } // @dev No need to safe cast due to internal logic no.depositableValidatorsCount = uint32(newCount); emit DepositableSigningKeysCountChanged(nodeOperatorId, newCount); if (incrementNonceIfUpdated) { _incrementModuleNonce(); } _enqueueNodeOperatorKeys(nodeOperatorId); } } function _enqueueNodeOperatorKeys(uint256 nodeOperatorId) internal { uint256 curveId = accounting().getBondCurveId(nodeOperatorId); (uint32 priority, uint32 maxDeposits) = PARAMETERS_REGISTRY .getQueueConfig(curveId); NodeOperator storage no = _nodeOperators[nodeOperatorId]; uint32 depositable = no.depositableValidatorsCount; uint32 enqueued = no.enqueuedCount; if (depositable <= enqueued) { return; } uint32 toEnqueue; unchecked { toEnqueue = depositable - enqueued; } if (priority < QUEUE_LOWEST_PRIORITY) { unchecked { uint32 depositedAndQueued = no.totalDepositedKeys + enqueued; if (maxDeposits > depositedAndQueued) { uint32 priorityDepositsLeft = maxDeposits - depositedAndQueued; uint32 count = uint32( Math.min(toEnqueue, priorityDepositsLeft) ); _enqueueNodeOperatorKeys(nodeOperatorId, priority, count); toEnqueue -= count; if (!no.usedPriorityQueue) { no.usedPriorityQueue = true; } } } } if (toEnqueue > 0) { _enqueueNodeOperatorKeys( nodeOperatorId, QUEUE_LOWEST_PRIORITY, toEnqueue ); } } // NOTE: If `count` is 0 an empty batch will be created. function _enqueueNodeOperatorKeys( uint256 nodeOperatorId, uint256 queuePriority, uint32 count ) internal { NodeOperator storage no = _nodeOperators[nodeOperatorId]; no.enqueuedCount += count; QueueLib.Queue storage q = _getQueue(queuePriority); q.enqueue(nodeOperatorId, count); emit BatchEnqueued(queuePriority, nodeOperatorId, count); } function _recordOperatorCreator(uint256 nodeOperatorId) internal { TransientUintUintMap map = TransientUintUintMapLib.load( OPERATORS_CREATED_IN_TX_MAP_TSLOT ); map.set(nodeOperatorId, uint256(uint160(msg.sender))); } function _forgetOperatorCreator(uint256 nodeOperatorId) internal { TransientUintUintMap map = TransientUintUintMapLib.load( OPERATORS_CREATED_IN_TX_MAP_TSLOT ); map.set(nodeOperatorId, 0); } function _getOperatorCreator( uint256 nodeOperatorId ) internal view returns (address) { TransientUintUintMap map = TransientUintUintMapLib.load( OPERATORS_CREATED_IN_TX_MAP_TSLOT ); return address(uint160(map.get(nodeOperatorId))); } /// @dev Acts as a proxy to `_queueByPriority` till `_legacyQueue` deprecation. /// @dev TODO: Remove the method in the next major release. function _getQueue( uint256 priority ) internal view returns (QueueLib.Queue storage q) { if (priority == QUEUE_LEGACY_PRIORITY) { assembly { q.slot := _legacyQueue.slot } } else { q = _queueByPriority[priority]; } } function _checkCanAddKeys( uint256 nodeOperatorId, address who ) internal view { // Most likely a direct call, so check the sender is a manager. if (who == msg.sender) { _onlyNodeOperatorManager(nodeOperatorId, msg.sender); } else { // We're trying to add keys via gate, check if we can do it. _checkRole(CREATE_NODE_OPERATOR_ROLE); if (_getOperatorCreator(nodeOperatorId) != msg.sender) { revert CannotAddKeys(); } } } function _onlyNodeOperatorManager( uint256 nodeOperatorId, address from ) internal view { address managerAddress = _nodeOperators[nodeOperatorId].managerAddress; if (managerAddress == address(0)) { revert NodeOperatorDoesNotExist(); } if (managerAddress != from) { revert SenderIsNotEligible(); } } function _onlyExistingNodeOperator(uint256 nodeOperatorId) internal view { if (nodeOperatorId < _nodeOperatorsCount) { return; } revert NodeOperatorDoesNotExist(); } function _onlyValidIndexRange( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) internal view { if ( startIndex + keysCount > _nodeOperators[nodeOperatorId].totalAddedKeys ) { revert SigningKeysInvalidOffset(); } } function _onlyRecoverer() internal view override { _checkRole(RECOVERER_ROLE); } /// @dev Both nodeOperatorId and keyIndex are limited to uint64 by the contract function _keyPointer( uint256 nodeOperatorId, uint256 keyIndex ) internal pure returns (uint256) { return (nodeOperatorId << 128) | keyIndex; } }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @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 average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/AccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable
struct AccessControlEnumerableStorage {
mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000;
function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) {
assembly {
$.slot := AccessControlEnumerableStorageLocation
}
}
function __AccessControlEnumerable_init() internal onlyInitializing {
}
function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
}
/**
* @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 virtual returns (address) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
return $._roleMembers[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 virtual returns (uint256) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
return $._roleMembers[role].length();
}
/**
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
bool granted = super._grantRole(role, account);
if (granted) {
$._roleMembers[role].add(account);
}
return granted;
}
/**
* @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
bool revoked = super._revokeRole(role, account);
if (revoked) {
$._roleMembers[role].remove(account);
}
return revoked;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { AssetRecovererLib } from "../lib/AssetRecovererLib.sol"; /// @title AssetRecoverer /// @dev Abstract contract providing mechanisms for recovering various asset types (ETH, ERC20, ERC721, ERC1155) from a contract. /// This contract is designed to allow asset recovery by an authorized address by implementing the onlyRecovererRole guardian /// @notice Assets can be sent only to the `msg.sender` abstract contract AssetRecoverer { /// @dev Allows sender to recover Ether held by the contract /// Emits an EtherRecovered event upon success function recoverEther() external { _onlyRecoverer(); AssetRecovererLib.recoverEther(); } /// @dev Allows sender to recover ERC20 tokens held by the contract /// @param token The address of the ERC20 token to recover /// @param amount The amount of the ERC20 token to recover /// Emits an ERC20Recovered event upon success /// Optionally, the inheriting contract can override this function to add additional restrictions function recoverERC20(address token, uint256 amount) external virtual { _onlyRecoverer(); AssetRecovererLib.recoverERC20(token, amount); } /// @dev Allows sender to recover ERC721 tokens held by the contract /// @param token The address of the ERC721 token to recover /// @param tokenId The token ID of the ERC721 token to recover /// Emits an ERC721Recovered event upon success function recoverERC721(address token, uint256 tokenId) external { _onlyRecoverer(); AssetRecovererLib.recoverERC721(token, tokenId); } /// @dev Allows sender to recover ERC1155 tokens held by the contract. /// @param token The address of the ERC1155 token to recover. /// @param tokenId The token ID of the ERC1155 token to recover. /// Emits an ERC1155Recovered event upon success. function recoverERC1155(address token, uint256 tokenId) external { _onlyRecoverer(); AssetRecovererLib.recoverERC1155(token, tokenId); } /// @dev Guardian to restrict access to the recover methods. /// Should be implemented by the inheriting contract function _onlyRecoverer() internal view virtual; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; /// @title Lido's Staking Module interface interface IStakingModule { /// @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); /// @notice Handles tracking and penalization logic for a validator that remains active beyond its eligible exit window. /// @dev This function is called by the StakingRouter to report the current exit-related status of a validator /// belonging to a specific node operator. It accepts a validator's public key, associated /// with the duration (in seconds) it was eligible to exit but has not exited. /// This data could be used to trigger penalties for the node operator if the validator has exceeded the allowed exit window. /// @param _nodeOperatorId The ID of the node operator whose validator's status is being delivered. /// @param _proofSlotTimestamp The timestamp (slot time) when the validator was last known to be in an active ongoing state. /// @param _publicKey The public key of the validator being reported. /// @param _eligibleToExitInSec The duration (in seconds) indicating how long the validator has been eligible to exit but has not exited. function reportValidatorExitDelay( uint256 _nodeOperatorId, uint256 _proofSlotTimestamp, bytes calldata _publicKey, uint256 _eligibleToExitInSec ) external; /// @notice Handles the triggerable exit event for a validator belonging to a specific node operator. /// @dev This function is called by the StakingRouter when a validator is exited using the triggerable /// exit request on the Execution Layer (EL). /// @param _nodeOperatorId The ID of the node operator. /// @param _publicKey The public key of the validator being reported. /// @param _withdrawalRequestPaidFee Fee amount paid to send a withdrawal request on the Execution Layer (EL). /// @param _exitType The type of exit being performed. /// This parameter may be interpreted differently across various staking modules, depending on their specific implementation. function onValidatorExitTriggered( uint256 _nodeOperatorId, bytes calldata _publicKey, uint256 _withdrawalRequestPaidFee, uint256 _exitType ) external; /// @notice Determines whether a validator's exit status should be updated and will have an effect on the Node Operator. /// @param _nodeOperatorId The ID of the node operator. /// @param _proofSlotTimestamp The timestamp (slot time) when the validator was last known to be in an active ongoing state. /// @param _publicKey The public key of the validator. /// @param _eligibleToExitInSec The number of seconds the validator was eligible to exit but did not. /// @return bool Returns true if the contract should receive the updated status of the validator. function isValidatorExitDelayPenaltyApplicable( uint256 _nodeOperatorId, uint256 _proofSlotTimestamp, bytes calldata _publicKey, uint256 _eligibleToExitInSec ) external view returns (bool); /// @notice Returns the number of seconds after which a validator is considered late. /// @param _nodeOperatorId The ID of the node operator. /// @return The exit deadline threshold in seconds. function exitDeadlineThreshold( uint256 _nodeOperatorId ) external view returns (uint256); /// @notice Returns the type of the staking module /// @return Module type 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 (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 Operators with given ids /// @param nodeOperatorIds Bytes packed array of the Node Operator ids /// @param vettedSigningKeysCounts Bytes packed array of the new numbers of vetted keys for the Node Operators function decreaseVettedSigningKeysCount( bytes calldata nodeOperatorIds, bytes calldata vettedSigningKeysCounts ) 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 limit of the validators that can be used for deposit /// @param nodeOperatorId ID of the Node Operator /// @param targetLimitMode Target limit mode for the Node Operator (see https://hackmd.io/@lido/BJXRTxMRp) /// 0 - disabled /// 1 - soft mode /// 2 - forced mode /// @param targetLimit Target limit of validators 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 function unsafeUpdateValidatorsCount( uint256 _nodeOperatorId, uint256 _exitedValidatorsCount ) 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; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface ILidoLocator { error ZeroAddress(); function accountingOracle() external view returns (address); function burner() external view returns (address); function coreComponents() external view returns (address, address, address, address, address, 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 oracleDaemonConfig() external view returns (address); function oracleReportComponentsForLido() external view returns (address, address, address, address, address, address, address); function oracleReportSanityChecker() external view returns (address); function postTokenRebaseReceiver() external view returns (address); function stakingRouter() external view returns (address payable); 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 triggerableWithdrawalsGateway() external view returns (address); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; /** * @title Interface defining ERC20-compatible StETH token */ interface IStETH { /** * @notice Get stETH amount by the provided shares amount * @param _sharesAmount shares amount * @dev dual to `getSharesByPooledEth`. */ function getPooledEthByShares( uint256 _sharesAmount ) external view returns (uint256); /** * @notice Get shares amount by the provided stETH amount * @param _pooledEthAmount stETH amount * @dev dual to `getPooledEthByShares`. */ function getSharesByPooledEth( uint256 _pooledEthAmount ) external view returns (uint256); /** * @notice Get shares amount of the provided account * @param _account provided account address. */ function sharesOf(address _account) external view returns (uint256); function balanceOf(address _account) external view returns (uint256); /** * @notice Transfer `_sharesAmount` stETH shares from `_sender` to `_recipient` using allowance. */ function transferSharesFrom( address _sender, address _recipient, uint256 _sharesAmount ) external returns (uint256); /** * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account. */ function transferShares( address _recipient, uint256 _sharesAmount ) external returns (uint256); /** * @notice Moves `_amount` stETH from the caller's account to the `_recipient` account. */ function transfer( address _recipient, uint256 _amount ) external returns (bool); /** * @notice Moves `_amount` stETH from the `_sender` account to the `_recipient` account. */ function transferFrom( address _sender, address _recipient, uint256 _amount ) external returns (bool); function approve(address _spender, uint256 _amount) external returns (bool); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function allowance( address _owner, address _spender ) external view returns (uint256); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface ICSParametersRegistry { struct MarkedUint248 { uint248 value; bool isValue; } struct QueueConfig { uint32 priority; uint32 maxDeposits; } struct StrikesParams { uint32 lifetime; uint32 threshold; } struct PerformanceCoefficients { uint32 attestationsWeight; uint32 blocksWeight; uint32 syncWeight; } struct InitializationData { uint256 keyRemovalCharge; uint256 elRewardsStealingAdditionalFine; uint256 keysLimit; uint256 rewardShare; uint256 performanceLeeway; uint256 strikesLifetime; uint256 strikesThreshold; uint256 defaultQueuePriority; uint256 defaultQueueMaxDeposits; uint256 badPerformancePenalty; uint256 attestationsWeight; uint256 blocksWeight; uint256 syncWeight; uint256 defaultAllowedExitDelay; uint256 defaultExitDelayPenalty; uint256 defaultMaxWithdrawalRequestFee; } /// @dev Defines a value interval starting from `minKeyNumber`. /// All keys with number >= `minKeyNumber` are assigned the corresponding `value` /// until the next interval begins. Intervals must be sorted by ascending `minKeyNumber` /// and must start from one (i.e., the first interval must have minKeyNumber == 1). /// Example: [{1, 10000}, {11, 8000}] means first 10 keys with 10000, other keys with 8000. struct KeyNumberValueInterval { uint256 minKeyNumber; uint256 value; } event DefaultKeyRemovalChargeSet(uint256 value); event DefaultElRewardsStealingAdditionalFineSet(uint256 value); event DefaultKeysLimitSet(uint256 value); event DefaultRewardShareSet(uint256 value); event DefaultPerformanceLeewaySet(uint256 value); event DefaultStrikesParamsSet(uint256 lifetime, uint256 threshold); event DefaultBadPerformancePenaltySet(uint256 value); event DefaultPerformanceCoefficientsSet( uint256 attestationsWeight, uint256 blocksWeight, uint256 syncWeight ); event DefaultQueueConfigSet(uint256 priority, uint256 maxDeposits); event DefaultAllowedExitDelaySet(uint256 delay); event DefaultExitDelayPenaltySet(uint256 penalty); event DefaultMaxWithdrawalRequestFeeSet(uint256 fee); event KeyRemovalChargeSet( uint256 indexed curveId, uint256 keyRemovalCharge ); event ElRewardsStealingAdditionalFineSet( uint256 indexed curveId, uint256 fine ); event KeysLimitSet(uint256 indexed curveId, uint256 limit); event RewardShareDataSet( uint256 indexed curveId, KeyNumberValueInterval[] data ); event PerformanceLeewayDataSet( uint256 indexed curveId, KeyNumberValueInterval[] data ); event StrikesParamsSet( uint256 indexed curveId, uint256 lifetime, uint256 threshold ); event BadPerformancePenaltySet(uint256 indexed curveId, uint256 penalty); event PerformanceCoefficientsSet( uint256 indexed curveId, uint256 attestationsWeight, uint256 blocksWeight, uint256 syncWeight ); event KeyRemovalChargeUnset(uint256 indexed curveId); event ElRewardsStealingAdditionalFineUnset(uint256 indexed curveId); event KeysLimitUnset(uint256 indexed curveId); event RewardShareDataUnset(uint256 indexed curveId); event PerformanceLeewayDataUnset(uint256 indexed curveId); event StrikesParamsUnset(uint256 indexed curveId); event BadPerformancePenaltyUnset(uint256 indexed curveId); event PerformanceCoefficientsUnset(uint256 indexed curveId); event QueueConfigSet( uint256 indexed curveId, uint256 priority, uint256 maxDeposits ); event QueueConfigUnset(uint256 indexed curveId); event AllowedExitDelaySet(uint256 indexed curveId, uint256 delay); event AllowedExitDelayUnset(uint256 indexed curveId); event ExitDelayPenaltySet(uint256 indexed curveId, uint256 penalty); event ExitDelayPenaltyUnset(uint256 indexed curveId); event MaxWithdrawalRequestFeeSet(uint256 indexed curveId, uint256 fee); event MaxWithdrawalRequestFeeUnset(uint256 indexed curveId); error InvalidRewardShareData(); error InvalidPerformanceLeewayData(); error InvalidKeyNumberValueIntervals(); error InvalidPerformanceCoefficients(); error InvalidStrikesParams(); error ZeroMaxDeposits(); error ZeroAdminAddress(); error QueueCannotBeUsed(); error InvalidAllowedExitDelay(); error ZeroQueueLowestPriority(); /// @notice The lowest priority a deposit queue can be assigned with. function QUEUE_LOWEST_PRIORITY() external view returns (uint256); /// @notice The priority reserved to be used for legacy queue only. function QUEUE_LEGACY_PRIORITY() external view returns (uint256); /// @notice Set default value for the key removal charge. Default value is used if a specific value is not set for the curveId /// @param keyRemovalCharge value to be set as default for the key removal charge function setDefaultKeyRemovalCharge(uint256 keyRemovalCharge) external; /// @notice Get default value for the key removal charge function defaultKeyRemovalCharge() external returns (uint256); /// @notice Set default value for the EL rewards stealing additional fine. Default value is used if a specific value is not set for the curveId /// @param fine value to be set as default for the EL rewards stealing additional fine function setDefaultElRewardsStealingAdditionalFine(uint256 fine) external; /// @notice Get default value for the EL rewards stealing additional fine function defaultElRewardsStealingAdditionalFine() external returns (uint256); /// @notice Set default value for the keys limit. Default value is used if a specific value is not set for the curveId /// @param limit value to be set as default for the keys limit function setDefaultKeysLimit(uint256 limit) external; /// @notice Get default value for the key removal charge function defaultKeysLimit() external returns (uint256); /// @notice Set default value for the reward share. Default value is used if a specific value is not set for the curveId /// @param share value to be set as default for the reward share function setDefaultRewardShare(uint256 share) external; /// @notice Get default value for the reward share function defaultRewardShare() external returns (uint256); /// @notice Set default value for the performance leeway. Default value is used if a specific value is not set for the curveId /// @param leeway value to be set as default for the performance leeway function setDefaultPerformanceLeeway(uint256 leeway) external; /// @notice Get default value for the performance leeway function defaultPerformanceLeeway() external returns (uint256); /// @notice Set default values for the strikes lifetime and threshold. Default values are used if specific values are not set for the curveId /// @param lifetime The default number of CSM Performance Oracle frames to store strikes values /// @param threshold The default strikes value leading to validator force ejection. function setDefaultStrikesParams( uint256 lifetime, uint256 threshold ) external; /// @notice Get default value for the strikes lifetime (frames count) and threshold (integer) /// @return lifetime The default number of CSM Performance Oracle frames to store strikes values /// @return threshold The default strikes value leading to validator force ejection. function defaultStrikesParams() external returns (uint32, uint32); /// @notice Set default value for the bad performance penalty. Default value is used if a specific value is not set for the curveId /// @param penalty value to be set as default for the bad performance penalty function setDefaultBadPerformancePenalty(uint256 penalty) external; /// @notice Get default value for the bad performance penalty function defaultBadPerformancePenalty() external returns (uint256); /// @notice Set default values for the performance coefficients. Default values are used if specific values are not set for the curveId /// @param attestationsWeight value to be set as default for the attestations effectiveness weight /// @param blocksWeight value to be set as default for block proposals effectiveness weight /// @param syncWeight value to be set as default for sync participation effectiveness weight function setDefaultPerformanceCoefficients( uint256 attestationsWeight, uint256 blocksWeight, uint256 syncWeight ) external; /// @notice Get default value for the performance coefficients function defaultPerformanceCoefficients() external returns (uint32, uint32, uint32); /// @notice set default value for allowed delay before the exit was initiated exit delay in seconds. Default value is used if a specific value is not set for the curveId /// @param delay value to be set as default for the allowed exit delay function setDefaultAllowedExitDelay(uint256 delay) external; /// @notice set default value for exit delay penalty. Default value is used if a specific value is not set for the curveId /// @param penalty value to be set as default for the exit delay penalty function setDefaultExitDelayPenalty(uint256 penalty) external; /// @notice set default value for max withdrawal request fee. Default value is used if a specific value is not set for the curveId /// @param fee value to be set as default for the max withdrawal request fee function setDefaultMaxWithdrawalRequestFee(uint256 fee) external; /// @notice Get default value for the allowed exit delay function defaultAllowedExitDelay() external returns (uint256); /// @notice Set key removal charge for the curveId. /// @param curveId Curve Id to associate key removal charge with /// @param keyRemovalCharge Key removal charge function setKeyRemovalCharge( uint256 curveId, uint256 keyRemovalCharge ) external; /// @notice Unset key removal charge for the curveId /// @param curveId Curve Id to unset custom key removal charge for function unsetKeyRemovalCharge(uint256 curveId) external; /// @notice Get key removal charge by the curveId. A charge is taken from the bond for each removed key from CSM /// @dev `defaultKeyRemovalCharge` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get key removal charge for /// @return keyRemovalCharge Key removal charge function getKeyRemovalCharge( uint256 curveId ) external view returns (uint256 keyRemovalCharge); /// @notice Set EL rewards stealing additional fine for the curveId. /// @param curveId Curve Id to associate EL rewards stealing additional fine limit with /// @param fine EL rewards stealing additional fine function setElRewardsStealingAdditionalFine( uint256 curveId, uint256 fine ) external; /// @notice Unset EL rewards stealing additional fine for the curveId /// @param curveId Curve Id to unset custom EL rewards stealing additional fine for function unsetElRewardsStealingAdditionalFine(uint256 curveId) external; /// @notice Get EL rewards stealing additional fine by the curveId. Additional fine is added to the EL rewards stealing penalty by CSM /// @dev `defaultElRewardsStealingAdditionalFine` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get EL rewards stealing additional fine for /// @return fine EL rewards stealing additional fine function getElRewardsStealingAdditionalFine( uint256 curveId ) external view returns (uint256 fine); /// @notice Set keys limit for the curveId. /// @param curveId Curve Id to associate keys limit with /// @param limit Keys limit function setKeysLimit(uint256 curveId, uint256 limit) external; /// @notice Unset key removal charge for the curveId /// @param curveId Curve Id to unset custom key removal charge for function unsetKeysLimit(uint256 curveId) external; /// @notice Get keys limit by the curveId. A limit indicates the maximal amount of the non-exited keys Node Operator can upload /// @dev `defaultKeysLimit` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get keys limit for /// @return limit Keys limit function getKeysLimit( uint256 curveId ) external view returns (uint256 limit); /// @notice Set reward share parameters for the curveId /// @dev KeyNumberValueInterval = [[1, 10000], [11, 8000], [51, 5000]] stands for /// 100% rewards for the first 10 keys, 80% rewards for the keys 11-50, and 50% rewards for the keys > 50 /// @param curveId Curve Id to associate reward share data with /// @param data Interval values for keys count and reward share percentages in BP (ex. [[1, 10000], [11, 8000], [51, 5000]]) function setRewardShareData( uint256 curveId, KeyNumberValueInterval[] calldata data ) external; /// @notice Unset reward share parameters for the curveId /// @param curveId Curve Id to unset custom reward share parameters for function unsetRewardShareData(uint256 curveId) external; /// @notice Get reward share parameters by the curveId. /// @dev Returns [[1, defaultRewardShare]] if no intervals are set for the given curveId. /// @dev KeyNumberValueInterval = [[1, 10000], [11, 8000], [51, 5000]] stands for /// 100% rewards for the first 10 keys, 80% rewards for the keys 11-50, and 50% rewards for the keys > 50 /// @param curveId Curve Id to get reward share data for /// @param data Interval values for keys count and reward share percentages in BP (ex. [[1, 10000], [11, 8000], [51, 5000]]) function getRewardShareData( uint256 curveId ) external view returns (KeyNumberValueInterval[] memory data); /// @notice Set default value for QueueConfig. Default value is used if a specific value is not set for the curveId. /// @param priority Queue priority. /// @param maxDeposits Maximum number of deposits a Node Operator can get via the priority queue. function setDefaultQueueConfig( uint256 priority, uint256 maxDeposits ) external; /// @notice Sets the provided config to the given curve. /// @param curveId Curve Id to set the config. /// @param priority Priority of the queue /// @param maxDeposits Max deposits in prioritized queue function setQueueConfig( uint256 curveId, uint256 priority, uint256 maxDeposits ) external; /// @notice Set the given curve's config to the default one. /// @param curveId Curve Id to unset custom config. function unsetQueueConfig(uint256 curveId) external; /// @notice Get the queue config for the given curve. /// @param curveId Curve Id to get the queue config for. /// @return priority Queue priority. /// @return maxDeposits Maximum number of deposits a Node Operator can get via the priority queue. function getQueueConfig( uint256 curveId ) external view returns (uint32 priority, uint32 maxDeposits); /// @notice Set performance leeway parameters for the curveId /// @dev Returns [[1, defaultPerformanceLeeway]] if no intervals are set for the given curveId. /// @dev KeyNumberValueInterval = [[1, 500], [101, 450], [501, 400]] stands for /// 5% performance leeway for the first 100 keys, 4.5% performance leeway for the keys 101-500, and 4% performance leeway for the keys > 500 /// @param curveId Curve Id to associate performance leeway data with /// @param data Interval values for keys count and performance leeway percentages in BP (ex. [[1, 500], [101, 450], [501, 400]]) function setPerformanceLeewayData( uint256 curveId, KeyNumberValueInterval[] calldata data ) external; /// @notice Unset performance leeway parameters for the curveId /// @param curveId Curve Id to unset custom performance leeway parameters for function unsetPerformanceLeewayData(uint256 curveId) external; /// @notice Get performance leeway parameters by the curveId /// @dev Returns [[1, defaultPerformanceLeeway]] if no intervals are set for the given curveId. /// @dev KeyNumberValueInterval = [[1, 500], [101, 450], [501, 400]] stands for /// 5% performance leeway for the first 100 keys, 4.5% performance leeway for the keys 101-500, and 4% performance leeway for the keys > 500 /// @param curveId Curve Id to get performance leeway data for /// @param data Interval values for keys count and performance leeway percentages in BP (ex. [[1, 500], [101, 450], [501, 400]]) function getPerformanceLeewayData( uint256 curveId ) external view returns (KeyNumberValueInterval[] memory data); /// @notice Set performance strikes lifetime and threshold for the curveId /// @param curveId Curve Id to associate performance strikes lifetime and threshold with /// @param lifetime Number of CSM Performance Oracle frames to store strikes values /// @param threshold The strikes value leading to validator force ejection function setStrikesParams( uint256 curveId, uint256 lifetime, uint256 threshold ) external; /// @notice Unset custom performance strikes lifetime and threshold for the curveId /// @param curveId Curve Id to unset custom performance strikes lifetime and threshold for function unsetStrikesParams(uint256 curveId) external; /// @notice Get performance strikes lifetime and threshold by the curveId /// @dev `defaultStrikesParams` are returned if the value is not set for the given curveId /// @param curveId Curve Id to get performance strikes lifetime and threshold for /// @return lifetime Number of CSM Performance Oracle frames to store strikes values /// @return threshold The strikes value leading to validator force ejection function getStrikesParams( uint256 curveId ) external view returns (uint256 lifetime, uint256 threshold); /// @notice Set bad performance penalty for the curveId /// @param curveId Curve Id to associate bad performance penalty with /// @param penalty Bad performance penalty function setBadPerformancePenalty( uint256 curveId, uint256 penalty ) external; /// @notice Unset bad performance penalty for the curveId /// @param curveId Curve Id to unset custom bad performance penalty for function unsetBadPerformancePenalty(uint256 curveId) external; /// @notice Get bad performance penalty by the curveId /// @dev `defaultBadPerformancePenalty` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get bad performance penalty for /// @return penalty Bad performance penalty function getBadPerformancePenalty( uint256 curveId ) external view returns (uint256 penalty); /// @notice Set performance coefficients for the curveId /// @param curveId Curve Id to associate performance coefficients with /// @param attestationsWeight Attestations effectiveness weight /// @param blocksWeight Block proposals effectiveness weight /// @param syncWeight Sync participation effectiveness weight function setPerformanceCoefficients( uint256 curveId, uint256 attestationsWeight, uint256 blocksWeight, uint256 syncWeight ) external; /// @notice Unset custom performance coefficients for the curveId /// @param curveId Curve Id to unset custom performance coefficients for function unsetPerformanceCoefficients(uint256 curveId) external; /// @notice Get performance coefficients by the curveId /// @dev `defaultPerformanceCoefficients` are returned if the value is not set for the given curveId. /// @param curveId Curve Id to get performance coefficients for /// @return attestationsWeight Attestations effectiveness weight /// @return blocksWeight Block proposals effectiveness weight /// @return syncWeight Sync participation effectiveness weight function getPerformanceCoefficients( uint256 curveId ) external view returns ( uint256 attestationsWeight, uint256 blocksWeight, uint256 syncWeight ); /// @notice Set allowed exit delay for the curveId in seconds /// @param curveId Curve Id to associate allowed exit delay with /// @param delay allowed exit delay function setAllowedExitDelay(uint256 curveId, uint256 delay) external; /// @notice Unset exit timeframe deadline delay for the curveId /// @param curveId Curve Id to unset allowed exit delay for function unsetAllowedExitDelay(uint256 curveId) external; /// @notice Get allowed exit delay by the curveId in seconds /// @dev `defaultAllowedExitDelay` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get allowed exit delay for function getAllowedExitDelay( uint256 curveId ) external view returns (uint256 delay); /// @notice Set exit delay penalty for the curveId /// @dev cannot be zero /// @param curveId Curve Id to associate exit delay penalty with /// @param penalty exit delay penalty function setExitDelayPenalty(uint256 curveId, uint256 penalty) external; /// @notice Unset exit delay penalty for the curveId /// @param curveId Curve Id to unset exit delay penalty for function unsetExitDelayPenalty(uint256 curveId) external; /// @notice Get exit delay penalty by the curveId /// @dev `defaultExitDelayPenalty` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get exit delay penalty for function getExitDelayPenalty( uint256 curveId ) external view returns (uint256 penalty); /// @notice Set max withdrawal request fee for the curveId /// @param curveId Curve Id to associate max withdrawal request fee with /// @param fee max withdrawal request fee function setMaxWithdrawalRequestFee(uint256 curveId, uint256 fee) external; /// @notice Unset max withdrawal request fee for the curveId /// @param curveId Curve Id to unset max withdrawal request fee for function unsetMaxWithdrawalRequestFee(uint256 curveId) external; /// @notice Get max withdrawal request fee by the curveId /// @dev `defaultMaxWithdrawalRequestFee` is returned if the value is not set for the given curveId. /// @param curveId Curve Id to get max withdrawal request fee for function getMaxWithdrawalRequestFee( uint256 curveId ) external view returns (uint256 fee); /// @notice Returns the initialized version of the contract function getInitializedVersion() external view returns (uint64); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { ICSBondCore } from "./ICSBondCore.sol"; import { ICSBondCurve } from "./ICSBondCurve.sol"; import { ICSBondLock } from "./ICSBondLock.sol"; import { ICSFeeDistributor } from "./ICSFeeDistributor.sol"; import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol"; import { ICSModule } from "./ICSModule.sol"; interface ICSAccounting is ICSBondCore, ICSBondCurve, ICSBondLock, IAssetRecovererLib { struct PermitInput { uint256 value; uint256 deadline; uint8 v; bytes32 r; bytes32 s; } event BondLockCompensated(uint256 indexed nodeOperatorId, uint256 amount); event ChargePenaltyRecipientSet(address chargePenaltyRecipient); error SenderIsNotModule(); error SenderIsNotEligible(); error ZeroModuleAddress(); error ZeroAdminAddress(); error ZeroFeeDistributorAddress(); error ZeroChargePenaltyRecipientAddress(); error NodeOperatorDoesNotExist(); error ElRewardsVaultReceiveFailed(); error InvalidBondCurvesLength(); function PAUSE_ROLE() external view returns (bytes32); function RESUME_ROLE() external view returns (bytes32); function MANAGE_BOND_CURVES_ROLE() external view returns (bytes32); function SET_BOND_CURVE_ROLE() external view returns (bytes32); function RECOVERER_ROLE() external view returns (bytes32); function MODULE() external view returns (ICSModule); function FEE_DISTRIBUTOR() external view returns (ICSFeeDistributor); function feeDistributor() external view returns (ICSFeeDistributor); function chargePenaltyRecipient() external view returns (address); /// @notice Get the initialized version of the contract function getInitializedVersion() external view returns (uint64); /// @notice Resume reward claims and deposits function resume() external; /// @notice Pause reward claims and deposits for `duration` seconds /// @dev Must be called together with `CSModule.pauseFor` /// @dev Passing MAX_UINT_256 as `duration` pauses indefinitely /// @param duration Duration of the pause in seconds function pauseFor(uint256 duration) external; /// @notice Set charge recipient address /// @param _chargePenaltyRecipient Charge recipient address function setChargePenaltyRecipient( address _chargePenaltyRecipient ) external; /// @notice Set bond lock period /// @param period Period in seconds to retain bond lock function setBondLockPeriod(uint256 period) external; /// @notice Add a new bond curve /// @param bondCurve Bond curve definition to add /// @return id Id of the added curve function addBondCurve( BondCurveIntervalInput[] calldata bondCurve ) external returns (uint256 id); /// @notice Update existing bond curve /// @dev If the curve is updated to a curve with higher values for any point, /// Extensive checks should be performed to avoid inconsistency in the keys accounting /// @param curveId Bond curve ID to update /// @param bondCurve Bond curve definition function updateBondCurve( uint256 curveId, BondCurveIntervalInput[] calldata bondCurve ) external; /// @notice Get the required bond in ETH (inc. missed and excess) for the given Node Operator to upload new deposit data /// @param nodeOperatorId ID of the Node Operator /// @param additionalKeys Number of new keys to add /// @return Required bond amount in ETH function getRequiredBondForNextKeys( uint256 nodeOperatorId, uint256 additionalKeys ) external view returns (uint256); /// @notice Get the bond amount in wstETH required for the `keysCount` keys using the default bond curve /// @param keysCount Keys count to calculate the required bond amount /// @param curveId Id of the curve to perform calculations against /// @return wstETH amount required for the `keysCount` function getBondAmountByKeysCountWstETH( uint256 keysCount, uint256 curveId ) external view returns (uint256); /// @notice Get the required bond in wstETH (inc. missed and excess) for the given Node Operator to upload new keys /// @param nodeOperatorId ID of the Node Operator /// @param additionalKeys Number of new keys to add /// @return Required bond in wstETH function getRequiredBondForNextKeysWstETH( uint256 nodeOperatorId, uint256 additionalKeys ) external view returns (uint256); /// @notice Get the number of the unbonded keys /// @param nodeOperatorId ID of the Node Operator /// @return Unbonded keys count function getUnbondedKeysCount( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get the number of the unbonded keys to be ejected using a forcedTargetLimit /// @param nodeOperatorId ID of the Node Operator /// @return Unbonded keys count function getUnbondedKeysCountToEject( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get current and required bond amounts in ETH (stETH) for the given Node Operator /// @dev To calculate excess bond amount subtract `required` from `current` value. /// To calculate missed bond amount subtract `current` from `required` value /// @param nodeOperatorId ID of the Node Operator /// @return current Current bond amount in ETH /// @return required Required bond amount in ETH function getBondSummary( uint256 nodeOperatorId ) external view returns (uint256 current, uint256 required); /// @notice Get current and required bond amounts in stETH shares for the given Node Operator /// @dev To calculate excess bond amount subtract `required` from `current` value. /// To calculate missed bond amount subtract `current` from `required` value /// @param nodeOperatorId ID of the Node Operator /// @return current Current bond amount in stETH shares /// @return required Required bond amount in stETH shares function getBondSummaryShares( uint256 nodeOperatorId ) external view returns (uint256 current, uint256 required); /// @notice Get current claimable bond in stETH shares for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Current claimable bond in stETH shares function getClaimableBondShares( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get current claimable bond in stETH shares for the given Node Operator /// Includes potential rewards distributed by the Fee Distributor /// @param nodeOperatorId ID of the Node Operator /// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator /// @param rewardsProof Merkle proof of the rewards /// @return Current claimable bond in stETH shares function getClaimableRewardsAndBondShares( uint256 nodeOperatorId, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof ) external view returns (uint256); /// @notice Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator /// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count /// @param from Address to unwrap wstETH from /// @param nodeOperatorId ID of the Node Operator /// @param wstETHAmount Amount of wstETH to deposit /// @param permit wstETH permit for the contract function depositWstETH( address from, uint256 nodeOperatorId, uint256 wstETHAmount, PermitInput calldata permit ) external; /// @notice Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator /// @dev Permissionless. Enqueues Node Operator's keys if needed /// @param nodeOperatorId ID of the Node Operator /// @param wstETHAmount Amount of wstETH to deposit /// @param permit wstETH permit for the contract function depositWstETH( uint256 nodeOperatorId, uint256 wstETHAmount, PermitInput calldata permit ) external; /// @notice Deposit user's stETH to the bond for the given Node Operator /// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count /// @param from Address to deposit stETH from. /// @param nodeOperatorId ID of the Node Operator /// @param stETHAmount Amount of stETH to deposit /// @param permit stETH permit for the contract function depositStETH( address from, uint256 nodeOperatorId, uint256 stETHAmount, PermitInput calldata permit ) external; /// @notice Deposit user's stETH to the bond for the given Node Operator /// @dev Permissionless. Enqueues Node Operator's keys if needed /// @param nodeOperatorId ID of the Node Operator /// @param stETHAmount Amount of stETH to deposit /// @param permit stETH permit for the contract function depositStETH( uint256 nodeOperatorId, uint256 stETHAmount, PermitInput calldata permit ) external; /// @notice Stake user's ETH with Lido and deposit stETH to the bond /// @dev Called by CSM exclusively. CSM should check node operator existence and update depositable validators count /// @param from Address to stake ETH and deposit stETH from /// @param nodeOperatorId ID of the Node Operator function depositETH(address from, uint256 nodeOperatorId) external payable; /// @notice Stake user's ETH with Lido and deposit stETH to the bond /// @dev Permissionless. Enqueues Node Operator's keys if needed /// @param nodeOperatorId ID of the Node Operator function depositETH(uint256 nodeOperatorId) external payable; /// @notice Claim full reward (fee + bond) in stETH for the given Node Operator with desirable value. /// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond /// @param nodeOperatorId ID of the Node Operator /// @param stETHAmount Amount of stETH to claim /// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator /// @param rewardsProof Merkle proof of the rewards /// @return shares Amount of stETH shares claimed /// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by /// off-chain tooling, e.g. to make sure a tree has at least 2 leafs. function claimRewardsStETH( uint256 nodeOperatorId, uint256 stETHAmount, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof ) external returns (uint256 shares); /// @notice Claim full reward (fee + bond) in wstETH for the given Node Operator available for this moment. /// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond /// @param nodeOperatorId ID of the Node Operator /// @param wstETHAmount Amount of wstETH to claim /// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator /// @param rewardsProof Merkle proof of the rewards /// @return claimedWstETHAmount Amount of wstETH claimed /// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by /// off-chain tooling, e.g. to make sure a tree has at least 2 leafs. function claimRewardsWstETH( uint256 nodeOperatorId, uint256 wstETHAmount, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof ) external returns (uint256 claimedWstETHAmount); /// @notice Request full reward (fee + bond) in Withdrawal NFT (unstETH) for the given Node Operator available for this moment. /// `rewardsProof` and `cumulativeFeeShares` might be empty in order to claim only excess bond /// @dev Reverts if amount isn't between `MIN_STETH_WITHDRAWAL_AMOUNT` and `MAX_STETH_WITHDRAWAL_AMOUNT` /// @param nodeOperatorId ID of the Node Operator /// @param stETHAmount Amount of ETH to request /// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator /// @param rewardsProof Merkle proof of the rewards /// @return requestId Withdrawal NFT ID /// @dev It's impossible to use single-leaf proof via this method, so this case should be treated carefully by /// off-chain tooling, e.g. to make sure a tree has at least 2 leafs. function claimRewardsUnstETH( uint256 nodeOperatorId, uint256 stETHAmount, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof ) external returns (uint256 requestId); /// @notice Lock bond in ETH for the given Node Operator /// @dev Called by CSM exclusively /// @param nodeOperatorId ID of the Node Operator /// @param amount Amount to lock in ETH (stETH) function lockBondETH(uint256 nodeOperatorId, uint256 amount) external; /// @notice Release locked bond in ETH for the given Node Operator /// @dev Called by CSM exclusively /// @param nodeOperatorId ID of the Node Operator /// @param amount Amount to release in ETH (stETH) function releaseLockedBondETH( uint256 nodeOperatorId, uint256 amount ) external; /// @notice Settle locked bond ETH for the given Node Operator /// @dev Called by CSM exclusively /// @param nodeOperatorId ID of the Node Operator function settleLockedBondETH( uint256 nodeOperatorId ) external returns (bool); /// @notice Compensate locked bond ETH for the given Node Operator /// @dev Called by CSM exclusively /// @param nodeOperatorId ID of the Node Operator function compensateLockedBondETH(uint256 nodeOperatorId) external payable; /// @notice Set the bond curve for the given Node Operator /// @dev Updates depositable validators count in CSM to ensure key pointers consistency /// @param nodeOperatorId ID of the Node Operator /// @param curveId ID of the bond curve to set function setBondCurve(uint256 nodeOperatorId, uint256 curveId) external; /// @notice Penalize bond by burning stETH shares of the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param amount Amount to penalize in ETH (stETH) function penalize(uint256 nodeOperatorId, uint256 amount) external; /// @notice Charge fee from bond by transferring stETH shares of the given Node Operator to the charge recipient /// @param nodeOperatorId ID of the Node Operator /// @param amount Amount to charge in ETH (stETH) function chargeFee(uint256 nodeOperatorId, uint256 amount) external; /// @notice Pull fees from CSFeeDistributor to the Node Operator's bond /// @dev Permissionless method. Can be called before penalty application to ensure that rewards are also penalized /// @param nodeOperatorId ID of the Node Operator /// @param cumulativeFeeShares Cumulative fee stETH shares for the Node Operator /// @param rewardsProof Merkle proof of the rewards function pullFeeRewards( uint256 nodeOperatorId, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof ) external; /// @notice Service method to update allowance to Burner in case it has changed function renewBurnerAllowance() external; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { ICSAccounting } from "./ICSAccounting.sol"; import { ICSParametersRegistry } from "./ICSParametersRegistry.sol"; import { ICSModule } from "./ICSModule.sol"; import { IExitTypes } from "./IExitTypes.sol"; struct MarkedUint248 { uint248 value; bool isValue; } struct ExitPenaltyInfo { MarkedUint248 delayPenalty; MarkedUint248 strikesPenalty; MarkedUint248 withdrawalRequestFee; } interface ICSExitPenalties is IExitTypes { error ZeroModuleAddress(); error ZeroParametersRegistryAddress(); error ZeroStrikesAddress(); error SenderIsNotModule(); error SenderIsNotStrikes(); error ValidatorExitDelayNotApplicable(); event ValidatorExitDelayProcessed( uint256 indexed nodeOperatorId, bytes pubkey, uint256 delayPenalty ); event TriggeredExitFeeRecorded( uint256 indexed nodeOperatorId, uint256 indexed exitType, bytes pubkey, uint256 withdrawalRequestPaidFee, uint256 withdrawalRequestRecordedFee ); event StrikesPenaltyProcessed( uint256 indexed nodeOperatorId, bytes pubkey, uint256 strikesPenalty ); function MODULE() external view returns (ICSModule); function ACCOUNTING() external view returns (ICSAccounting); function PARAMETERS_REGISTRY() external view returns (ICSParametersRegistry); function STRIKES() external view returns (address); /// @notice Handles tracking and penalization logic for a validator that remains active beyond its eligible exit window. /// @dev see IStakingModule.reportValidatorExitDelay for details /// @param nodeOperatorId The ID of the node operator whose validator's status is being delivered. /// @param publicKey The public key of the validator being reported. /// @param eligibleToExitInSec The duration (in seconds) indicating how long the validator has been eligible to exit but has not exited. function processExitDelayReport( uint256 nodeOperatorId, bytes calldata publicKey, uint256 eligibleToExitInSec ) external; /// @notice Process the triggered exit report /// @param nodeOperatorId ID of the Node Operator /// @param publicKey Public key of the validator /// @param withdrawalRequestPaidFee The fee paid for the withdrawal request /// @param exitType The type of the exit (0 - direct exit, 1 - forced exit) function processTriggeredExit( uint256 nodeOperatorId, bytes calldata publicKey, uint256 withdrawalRequestPaidFee, uint256 exitType ) external; /// @notice Process the strikes report /// @param nodeOperatorId ID of the Node Operator /// @param publicKey Public key of the validator function processStrikesReport( uint256 nodeOperatorId, bytes calldata publicKey ) external; /// @notice Determines whether a validator exit status should be updated and will have affect on Node Operator. /// @dev called only by CSM /// @param nodeOperatorId The ID of the node operator. /// @param publicKey Validator's public key. /// @param eligibleToExitInSec The number of seconds the validator was eligible to exit but did not. /// @return bool Returns true if contract should receive updated validator's status. function isValidatorExitDelayPenaltyApplicable( uint256 nodeOperatorId, bytes calldata publicKey, uint256 eligibleToExitInSec ) external view returns (bool); /// @notice get delayed exit penalty info for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param publicKey Public key of the validator /// @return penaltyInfo Delayed exit penalty info function getExitPenaltyInfo( uint256 nodeOperatorId, bytes calldata publicKey ) external view returns (ExitPenaltyInfo memory penaltyInfo); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { IStakingModule } from "./IStakingModule.sol"; import { ICSAccounting } from "./ICSAccounting.sol"; import { IQueueLib } from "../lib/QueueLib.sol"; import { INOAddresses } from "../lib/NOAddresses.sol"; import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol"; import { Batch } from "../lib/QueueLib.sol"; import { ILidoLocator } from "./ILidoLocator.sol"; import { IStETH } from "./IStETH.sol"; import { ICSParametersRegistry } from "./ICSParametersRegistry.sol"; import { ICSExitPenalties } from "./ICSExitPenalties.sol"; struct NodeOperator { // All the counters below are used together e.g. in the _updateDepositableValidatorsCount /* 1 */ uint32 totalAddedKeys; // @dev increased and decreased when removed /* 1 */ uint32 totalWithdrawnKeys; // @dev only increased /* 1 */ uint32 totalDepositedKeys; // @dev only increased /* 1 */ uint32 totalVettedKeys; // @dev both increased and decreased /* 1 */ uint32 stuckValidatorsCount; // @dev both increased and decreased /* 1 */ uint32 depositableValidatorsCount; // @dev any value /* 1 */ uint32 targetLimit; /* 1 */ uint8 targetLimitMode; /* 2 */ uint32 totalExitedKeys; // @dev only increased except for the unsafe updates /* 2 */ uint32 enqueuedCount; // Tracks how many places are occupied by the node operator's keys in the queue. /* 2 */ address managerAddress; /* 3 */ address proposedManagerAddress; /* 4 */ address rewardAddress; /* 5 */ address proposedRewardAddress; /* 5 */ bool extendedManagerPermissions; /* 5 */ bool usedPriorityQueue; } struct NodeOperatorManagementProperties { address managerAddress; address rewardAddress; bool extendedManagerPermissions; } struct ValidatorWithdrawalInfo { uint256 nodeOperatorId; // @dev ID of the Node Operator uint256 keyIndex; // @dev Index of the withdrawn key in the Node Operator's keys storage uint256 amount; // @dev Amount of withdrawn ETH in wei } /// @title Lido's Community Staking Module interface interface ICSModule is IQueueLib, INOAddresses, IAssetRecovererLib, IStakingModule { error CannotAddKeys(); error NodeOperatorDoesNotExist(); error SenderIsNotEligible(); error InvalidVetKeysPointer(); error ExitedKeysHigherThanTotalDeposited(); error ExitedKeysDecrease(); error InvalidInput(); error NotEnoughKeys(); error PriorityQueueAlreadyUsed(); error NotEligibleForPriorityQueue(); error PriorityQueueMaxDepositsUsed(); error NoQueuedKeysToMigrate(); error KeysLimitExceeded(); error SigningKeysInvalidOffset(); error InvalidAmount(); error ZeroLocatorAddress(); error ZeroAccountingAddress(); error ZeroExitPenaltiesAddress(); error ZeroAdminAddress(); error ZeroSenderAddress(); error ZeroParametersRegistryAddress(); event NodeOperatorAdded( uint256 indexed nodeOperatorId, address indexed managerAddress, address indexed rewardAddress, bool extendedManagerPermissions ); event ReferrerSet(uint256 indexed nodeOperatorId, address indexed referrer); event DepositableSigningKeysCountChanged( uint256 indexed nodeOperatorId, uint256 depositableKeysCount ); event VettedSigningKeysCountChanged( uint256 indexed nodeOperatorId, uint256 vettedKeysCount ); event VettedSigningKeysCountDecreased(uint256 indexed nodeOperatorId); event DepositedSigningKeysCountChanged( uint256 indexed nodeOperatorId, uint256 depositedKeysCount ); event ExitedSigningKeysCountChanged( uint256 indexed nodeOperatorId, uint256 exitedKeysCount ); event TotalSigningKeysCountChanged( uint256 indexed nodeOperatorId, uint256 totalKeysCount ); event TargetValidatorsCountChanged( uint256 indexed nodeOperatorId, uint256 targetLimitMode, uint256 targetValidatorsCount ); event WithdrawalSubmitted( uint256 indexed nodeOperatorId, uint256 keyIndex, uint256 amount, bytes pubkey ); event BatchEnqueued( uint256 indexed queuePriority, uint256 indexed nodeOperatorId, uint256 count ); event KeyRemovalChargeApplied(uint256 indexed nodeOperatorId); event ELRewardsStealingPenaltyReported( uint256 indexed nodeOperatorId, bytes32 proposedBlockHash, uint256 stolenAmount ); event ELRewardsStealingPenaltyCancelled( uint256 indexed nodeOperatorId, uint256 amount ); event ELRewardsStealingPenaltyCompensated( uint256 indexed nodeOperatorId, uint256 amount ); event ELRewardsStealingPenaltySettled(uint256 indexed nodeOperatorId); function PAUSE_ROLE() external view returns (bytes32); function RESUME_ROLE() external view returns (bytes32); function STAKING_ROUTER_ROLE() external view returns (bytes32); function REPORT_EL_REWARDS_STEALING_PENALTY_ROLE() external view returns (bytes32); function SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE() external view returns (bytes32); function VERIFIER_ROLE() external view returns (bytes32); function RECOVERER_ROLE() external view returns (bytes32); function CREATE_NODE_OPERATOR_ROLE() external view returns (bytes32); function DEPOSIT_SIZE() external view returns (uint256); function LIDO_LOCATOR() external view returns (ILidoLocator); function STETH() external view returns (IStETH); function PARAMETERS_REGISTRY() external view returns (ICSParametersRegistry); function ACCOUNTING() external view returns (ICSAccounting); function EXIT_PENALTIES() external view returns (ICSExitPenalties); function FEE_DISTRIBUTOR() external view returns (address); function QUEUE_LOWEST_PRIORITY() external view returns (uint256); function QUEUE_LEGACY_PRIORITY() external view returns (uint256); /// @notice Returns the address of the accounting contract function accounting() external view returns (ICSAccounting); /// @notice Pause creation of the Node Operators and keys upload for `duration` seconds. /// Existing NO management and reward claims are still available. /// To pause reward claims use pause method on CSAccounting /// @param duration Duration of the pause in seconds function pauseFor(uint256 duration) external; /// @notice Resume creation of the Node Operators and keys upload function resume() external; /// @notice Returns the initialized version of the contract function getInitializedVersion() external view returns (uint64); /// @notice Permissioned method to add a new Node Operator /// Should be called by `*Gate.sol` contracts. See `PermissionlessGate.sol` and `VettedGate.sol` for examples /// @param from Sender address. Initial sender address to be used as a default manager and reward addresses. /// Gates must pass the correct address in order to specify which address should be the owner of the Node Operator /// @param managementProperties Optional. Management properties to be used for the Node Operator. /// managerAddress: Used as `managerAddress` for the Node Operator. If not passed `from` will be used. /// rewardAddress: Used as `rewardAddress` for the Node Operator. If not passed `from` will be used. /// extendedManagerPermissions: Flag indicating that `managerAddress` will be able to change `rewardAddress`. /// If set to true `resetNodeOperatorManagerAddress` method will be disabled /// @param referrer Optional. Referrer address. Should be passed when Node Operator is created using partners integration function createNodeOperator( address from, NodeOperatorManagementProperties memory managementProperties, address referrer ) external returns (uint256 nodeOperatorId); /// @notice Add new keys to the existing Node Operator using ETH as a bond /// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts /// @param nodeOperatorId ID of the Node Operator /// @param keysCount Signing keys count /// @param publicKeys Public keys to submit /// @param signatures Signatures of `(deposit_message_root, domain)` tuples /// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata function addValidatorKeysETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes memory publicKeys, bytes memory signatures ) external payable; /// @notice Add new keys to the existing Node Operator using stETH as a bond /// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert /// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts /// @param nodeOperatorId ID of the Node Operator /// @param keysCount Signing keys count /// @param publicKeys Public keys to submit /// @param signatures Signatures of `(deposit_message_root, domain)` tuples /// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata /// @param permit Optional. Permit to use stETH as bond function addValidatorKeysStETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes memory publicKeys, bytes memory signatures, ICSAccounting.PermitInput memory permit ) external; /// @notice Add new keys to the existing Node Operator using wstETH as a bond /// @notice Due to the stETH rounding issue make sure to make approval or sign permit with extra 10 wei to avoid revert /// @param from Sender address. Commonly equals to `msg.sender` except for the case of Node Operator creation by `*Gate.sol` contracts /// @param nodeOperatorId ID of the Node Operator /// @param keysCount Signing keys count /// @param publicKeys Public keys to submit /// @param signatures Signatures of `(deposit_message_root, domain)` tuples /// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata /// @param permit Optional. Permit to use wstETH as bond function addValidatorKeysWstETH( address from, uint256 nodeOperatorId, uint256 keysCount, bytes memory publicKeys, bytes memory signatures, ICSAccounting.PermitInput memory permit ) external; /// @notice Report EL rewards stealing for the given Node Operator /// @notice The final locked amount will be equal to the stolen funds plus EL stealing additional fine /// @param nodeOperatorId ID of the Node Operator /// @param blockHash Execution layer block hash of the proposed block with EL rewards stealing /// @param amount Amount of stolen EL rewards in ETH function reportELRewardsStealingPenalty( uint256 nodeOperatorId, bytes32 blockHash, uint256 amount ) external; /// @notice Compensate EL rewards stealing penalty for the given Node Operator to prevent further validator exits /// @dev Can only be called by the Node Operator manager /// @param nodeOperatorId ID of the Node Operator function compensateELRewardsStealingPenalty( uint256 nodeOperatorId ) external payable; /// @notice Cancel previously reported and not settled EL rewards stealing penalty for the given Node Operator /// @notice The funds will be unlocked /// @param nodeOperatorId ID of the Node Operator /// @param amount Amount of penalty to cancel function cancelELRewardsStealingPenalty( uint256 nodeOperatorId, uint256 amount ) external; /// @notice Settle locked bond for the given Node Operators /// @dev SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE role is expected to be assigned to Easy Track /// @param nodeOperatorIds IDs of the Node Operators function settleELRewardsStealingPenalty( uint256[] memory nodeOperatorIds ) external; /// @notice Propose a new manager address for the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param proposedAddress Proposed manager address function proposeNodeOperatorManagerAddressChange( uint256 nodeOperatorId, address proposedAddress ) external; /// @notice Confirm a new manager address for the Node Operator. /// Should be called from the currently proposed address /// @param nodeOperatorId ID of the Node Operator function confirmNodeOperatorManagerAddressChange( uint256 nodeOperatorId ) external; /// @notice Reset the manager address to the reward address. /// Should be called from the reward address /// @param nodeOperatorId ID of the Node Operator function resetNodeOperatorManagerAddress(uint256 nodeOperatorId) external; /// @notice Propose a new reward address for the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param proposedAddress Proposed reward address function proposeNodeOperatorRewardAddressChange( uint256 nodeOperatorId, address proposedAddress ) external; /// @notice Confirm a new reward address for the Node Operator. /// Should be called from the currently proposed address /// @param nodeOperatorId ID of the Node Operator function confirmNodeOperatorRewardAddressChange( uint256 nodeOperatorId ) external; /// @notice Change rewardAddress if extendedManagerPermissions is enabled for the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param newAddress Proposed reward address function changeNodeOperatorRewardAddress( uint256 nodeOperatorId, address newAddress ) external; /// @notice Get the pointers to the head and tail of queue with the given priority. /// @param queuePriority Priority of the queue to get the pointers. /// @return head Pointer to the head of the queue. /// @return tail Pointer to the tail of the queue. function depositQueuePointers( uint256 queuePriority ) external view returns (uint128 head, uint128 tail); /// @notice Get the deposit queue item by an index /// @param queuePriority Priority of the queue to get an item from /// @param index Index of a queue item /// @return Deposit queue item from the priority queue function depositQueueItem( uint256 queuePriority, uint128 index ) external view returns (Batch); /// @notice Clean the deposit queue from batches with no depositable keys /// @dev Use **eth_call** to check how many items will be removed /// @param maxItems How many queue items to review /// @return removed Count of batches to be removed by visiting `maxItems` batches /// @return lastRemovedAtDepth The value to use as `maxItems` to remove `removed` batches if the static call of the method was used function cleanDepositQueue( uint256 maxItems ) external returns (uint256 removed, uint256 lastRemovedAtDepth); /// @notice Update depositable validators data and enqueue all unqueued keys for the given Node Operator /// @notice Unqueued stands for vetted but not enqueued keys /// @param nodeOperatorId ID of the Node Operator function updateDepositableValidatorsCount(uint256 nodeOperatorId) external; /// Performs a one-time migration of allocated seats from the legacy queue to a priority queue /// for an eligible node operator. This is possible, e.g., in the following scenario: A node /// operator with EA curve added their keys before CSM v2 and has no deposits due to a very long /// queue. The EA curve gives the node operator the ability to get some count of deposits through /// the priority queue. So, by calling the migration method, the node operator can obtain seats /// in the priority queue even though they already have seats in the legacy queue. /// @param nodeOperatorId ID of the Node Operator function migrateToPriorityQueue(uint256 nodeOperatorId) external; /// @notice Get Node Operator info /// @param nodeOperatorId ID of the Node Operator /// @return Node Operator info function getNodeOperator( uint256 nodeOperatorId ) external view returns (NodeOperator memory); /// @notice Get Node Operator management properties /// @param nodeOperatorId ID of the Node Operator /// @return Node Operator management properties function getNodeOperatorManagementProperties( uint256 nodeOperatorId ) external view returns (NodeOperatorManagementProperties memory); /// @notice Get Node Operator owner. Owner is manager address if `extendedManagerPermissions` is enabled and reward address otherwise /// @param nodeOperatorId ID of the Node Operator /// @return Node Operator owner function getNodeOperatorOwner( uint256 nodeOperatorId ) external view returns (address); /// @notice Get Node Operator non-withdrawn keys /// @param nodeOperatorId ID of the Node Operator /// @return Non-withdrawn keys count function getNodeOperatorNonWithdrawnKeys( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get Node Operator total deposited keys /// @param nodeOperatorId ID of the Node Operator /// @return Total deposited keys count function getNodeOperatorTotalDepositedKeys( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get Node Operator signing keys /// @param nodeOperatorId ID of the Node Operator /// @param startIndex Index of the first key /// @param keysCount Count of keys to get /// @return Signing keys function getSigningKeys( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external view returns (bytes memory); /// @notice Get Node Operator signing keys with signatures /// @param nodeOperatorId ID of the Node Operator /// @param startIndex Index of the first key /// @param keysCount Count of keys to get /// @return keys Signing keys /// @return signatures Signatures of `(deposit_message_root, domain)` tuples /// https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/beacon-chain.md#signingdata function getSigningKeysWithSignatures( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external view returns (bytes memory keys, bytes memory signatures); /// @notice Report Node Operator's keys as withdrawn and settle withdrawn amount /// @notice Called by `CSVerifier` contract. /// See `CSVerifier.processWithdrawalProof` to use this method permissionless /// @param withdrawalsInfo An array for the validator withdrawals info structs function submitWithdrawals( ValidatorWithdrawalInfo[] calldata withdrawalsInfo ) external; /// @notice Check if the given Node Operator's key is reported as withdrawn /// @param nodeOperatorId ID of the Node Operator /// @param keyIndex index of the key to check /// @return Is validator reported as withdrawn or not function isValidatorWithdrawn( uint256 nodeOperatorId, uint256 keyIndex ) external view returns (bool); /// @notice Remove keys for the Node Operator and confiscate removal charge for each deleted key /// @param nodeOperatorId ID of the Node Operator /// @param startIndex Index of the first key /// @param keysCount Keys count to delete function removeKeys( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) external; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { UnstructuredStorage } from "../UnstructuredStorage.sol"; contract PausableUntil { using UnstructuredStorage for bytes32; /// Contract resume/pause control storage slot bytes32 internal constant RESUME_SINCE_TIMESTAMP_POSITION = keccak256("lido.PausableUntil.resumeSinceTimestamp"); /// Special value for the infinite pause uint256 public constant PAUSE_INFINITELY = type(uint256).max; /// @notice Emitted when paused by the `pauseFor` or `pauseUntil` call event Paused(uint256 duration); /// @notice Emitted when resumed by the `resume` call event Resumed(); error ZeroPauseDuration(); error PausedExpected(); error ResumedExpected(); error PauseUntilMustBeInFuture(); /// @notice Reverts when resumed modifier whenPaused() { _checkPaused(); _; } /// @notice Reverts when paused modifier whenResumed() { _checkResumed(); _; } /// @notice Returns one of: /// - PAUSE_INFINITELY if paused infinitely returns /// - first second when get contract get resumed if paused for specific duration /// - some timestamp in past if not paused function getResumeSinceTimestamp() external view returns (uint256) { return RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } /// @notice Returns whether the contract is paused function isPaused() public view returns (bool) { return block.timestamp < RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); } function _resume() internal { _checkPaused(); RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(block.timestamp); emit Resumed(); } function _pauseFor(uint256 duration) internal { _checkResumed(); if (duration == 0) { revert ZeroPauseDuration(); } uint256 resumeSince; if (duration == PAUSE_INFINITELY) { resumeSince = PAUSE_INFINITELY; } else { resumeSince = block.timestamp + duration; } _setPausedState(resumeSince); } function _pauseUntil(uint256 pauseUntilInclusive) internal { _checkResumed(); if (pauseUntilInclusive < block.timestamp) { revert PauseUntilMustBeInFuture(); } uint256 resumeSince; if (pauseUntilInclusive != PAUSE_INFINITELY) { resumeSince = pauseUntilInclusive + 1; } else { resumeSince = PAUSE_INFINITELY; } _setPausedState(resumeSince); } function _setPausedState(uint256 resumeSince) internal { RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(resumeSince); if (resumeSince == PAUSE_INFINITELY) { emit Paused(PAUSE_INFINITELY); } else { emit Paused(resumeSince - block.timestamp); } } function _checkPaused() internal view { if (!isPaused()) { revert PausedExpected(); } } function _checkResumed() internal view { if (isPaused()) { revert ResumedExpected(); } } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { NodeOperator } from "../interfaces/ICSModule.sol"; import { TransientUintUintMap } from "./TransientUintUintMapLib.sol"; // Batch is an uint256 as it's the internal data type used by solidity. // Batch is a packed value, consisting of the following fields: // - uint64 nodeOperatorId // - uint64 keysCount -- count of keys enqueued by the batch // - uint128 next -- index of the next batch in the queue type Batch is uint256; /// @notice Batch of the operator with index 0, with no keys in it and the next Batch' index 0 is meaningless. function isNil(Batch self) pure returns (bool) { return Batch.unwrap(self) == 0; } /// @dev Syntactic sugar for the type. function unwrap(Batch self) pure returns (uint256) { return Batch.unwrap(self); } function noId(Batch self) pure returns (uint64 n) { assembly { n := shr(192, self) } } function keys(Batch self) pure returns (uint64 n) { assembly { n := shl(64, self) n := shr(192, n) } } function next(Batch self) pure returns (uint128 n) { assembly { n := shl(128, self) n := shr(128, n) } } /// @dev keys count cast is unsafe function setKeys(Batch self, uint256 keysCount) pure returns (Batch) { assembly { self := or( and( self, 0xffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff ), shl(128, and(keysCount, 0xffffffffffffffff)) ) // self.keys = keysCount } return self; } /// @dev can be unsafe if the From batch is previous to the self function setNext(Batch self, uint128 nextIndex) pure returns (Batch) { assembly { self := or( and( self, 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 ), nextIndex ) // self.next = next } return self; } /// @dev Instantiate a new Batch to be added to the queue. The `next` field will be determined upon the enqueue. /// @dev Parameters are uint256 to make usage easier. function createBatch( uint256 nodeOperatorId, uint256 keysCount ) pure returns (Batch item) { // NOTE: No need to safe cast due to internal logic. nodeOperatorId = uint64(nodeOperatorId); keysCount = uint64(keysCount); assembly { item := shl(128, keysCount) // `keysCount` in [64:127] item := or(item, shl(192, nodeOperatorId)) // `nodeOperatorId` in [0:63] } } using { noId, keys, setKeys, setNext, next, isNil, unwrap } for Batch global; using QueueLib for QueueLib.Queue; interface IQueueLib { error QueueIsEmpty(); error QueueLookupNoLimit(); } /// @author madlabman library QueueLib { struct Queue { // Pointer to the item to be dequeued. uint128 head; // Tracks the total number of batches ever enqueued. uint128 tail; // Mapping saves a little in costs and allows easily fallback to a zeroed batch on out-of-bounds access. mapping(uint128 => Batch) queue; } ////// /// External methods ////// function clean( Queue storage self, mapping(uint256 => NodeOperator) storage nodeOperators, uint256 maxItems, TransientUintUintMap queueLookup ) external returns ( uint256 removed, uint256 lastRemovedAtDepth, uint256 visited, bool reachedOutOfQueue ) { removed = 0; lastRemovedAtDepth = 0; visited = 0; reachedOutOfQueue = false; if (maxItems == 0) { revert IQueueLib.QueueLookupNoLimit(); } Batch prevItem; uint128 indexOfPrev; uint128 head = self.head; uint128 curr = head; while (visited < maxItems) { Batch item = self.queue[curr]; if (item.isNil()) { reachedOutOfQueue = true; break; } visited++; NodeOperator storage no = nodeOperators[item.noId()]; if (queueLookup.get(item.noId()) >= no.depositableValidatorsCount) { // NOTE: Since we reached that point there's no way for a Node Operator to have a depositable batch // later in the queue, and hence we don't update _queueLookup for the Node Operator. if (curr == head) { self.dequeue(); head = self.head; } else { // There's no `prev` item while we call `dequeue`, and removing an item will keep the `prev` intact // other than changing its `next` field. prevItem = prevItem.setNext(item.next()); self.queue[indexOfPrev] = prevItem; } // We assume that the invariant `enqueuedCount` >= `keys` is kept. // NOTE: No need to safe cast due to internal logic. no.enqueuedCount -= uint32(item.keys()); unchecked { lastRemovedAtDepth = visited; ++removed; } } else { queueLookup.add(item.noId(), item.keys()); indexOfPrev = curr; prevItem = item; } curr = item.next(); } } ///// /// Internal methods ///// function enqueue( Queue storage self, uint256 nodeOperatorId, uint256 keysCount ) internal returns (Batch item) { uint128 tail = self.tail; item = createBatch(nodeOperatorId, keysCount); assembly { item := or( and( item, 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 ), add(tail, 1) ) // item.next = self.tail + 1; } self.queue[tail] = item; unchecked { ++self.tail; } } function dequeue(Queue storage self) internal returns (Batch item) { item = peek(self); if (item.isNil()) { revert IQueueLib.QueueIsEmpty(); } self.head = item.next(); } function peek(Queue storage self) internal view returns (Batch) { return self.queue[self.head]; } function at( Queue storage self, uint128 index ) internal view returns (Batch) { return self.queue[index]; } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; /// @author skhomuti library ValidatorCountsReport { error InvalidReportData(); function safeCountOperators( bytes calldata ids, bytes calldata counts ) internal pure returns (uint256) { if ( counts.length / 16 != ids.length / 8 || ids.length % 8 != 0 || counts.length % 16 != 0 ) { revert InvalidReportData(); } return ids.length / 8; } function next( bytes calldata ids, bytes calldata counts, uint256 offset ) internal pure returns (uint256 nodeOperatorId, uint256 keysCount) { // prettier-ignore assembly ("memory-safe") { nodeOperatorId := shr(192, calldataload(add(ids.offset, mul(offset, 8)))) keysCount := shr(128, calldataload(add(counts.offset, mul(offset, 16)))) } } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { NodeOperator, ICSModule } from "../interfaces/ICSModule.sol"; /// Library for changing and reset node operator's manager and reward addresses /// @dev the only use of this to be a library is to save CSModule contract size via delegatecalls interface INOAddresses { event NodeOperatorManagerAddressChangeProposed( uint256 indexed nodeOperatorId, address indexed oldProposedAddress, address indexed newProposedAddress ); event NodeOperatorRewardAddressChangeProposed( uint256 indexed nodeOperatorId, address indexed oldProposedAddress, address indexed newProposedAddress ); // args order as in https://github.com/OpenZeppelin/openzeppelin-contracts/blob/11dc5e3809ebe07d5405fe524385cbe4f890a08b/contracts/access/Ownable.sol#L33 event NodeOperatorManagerAddressChanged( uint256 indexed nodeOperatorId, address indexed oldAddress, address indexed newAddress ); // args order as in https://github.com/OpenZeppelin/openzeppelin-contracts/blob/11dc5e3809ebe07d5405fe524385cbe4f890a08b/contracts/access/Ownable.sol#L33 event NodeOperatorRewardAddressChanged( uint256 indexed nodeOperatorId, address indexed oldAddress, address indexed newAddress ); error AlreadyProposed(); error SameAddress(); error SenderIsNotManagerAddress(); error SenderIsNotRewardAddress(); error SenderIsNotProposedAddress(); error MethodCallIsNotAllowed(); error ZeroRewardAddress(); } library NOAddresses { /// @notice Propose a new manager address for the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param proposedAddress Proposed manager address function proposeNodeOperatorManagerAddressChange( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId, address proposedAddress ) external { NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.managerAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (no.managerAddress != msg.sender) { revert INOAddresses.SenderIsNotManagerAddress(); } if (no.managerAddress == proposedAddress) { revert INOAddresses.SameAddress(); } if (no.proposedManagerAddress == proposedAddress) { revert INOAddresses.AlreadyProposed(); } address oldProposedAddress = no.proposedManagerAddress; no.proposedManagerAddress = proposedAddress; emit INOAddresses.NodeOperatorManagerAddressChangeProposed( nodeOperatorId, oldProposedAddress, proposedAddress ); } /// @notice Confirm a new manager address for the Node Operator. /// Should be called from the currently proposed address /// @param nodeOperatorId ID of the Node Operator function confirmNodeOperatorManagerAddressChange( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId ) external { NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.managerAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (no.proposedManagerAddress != msg.sender) { revert INOAddresses.SenderIsNotProposedAddress(); } address oldAddress = no.managerAddress; no.managerAddress = msg.sender; delete no.proposedManagerAddress; emit INOAddresses.NodeOperatorManagerAddressChanged( nodeOperatorId, oldAddress, msg.sender ); } /// @notice Propose a new reward address for the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param proposedAddress Proposed reward address function proposeNodeOperatorRewardAddressChange( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId, address proposedAddress ) external { NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.rewardAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (no.rewardAddress != msg.sender) { revert INOAddresses.SenderIsNotRewardAddress(); } if (no.rewardAddress == proposedAddress) { revert INOAddresses.SameAddress(); } if (no.proposedRewardAddress == proposedAddress) { revert INOAddresses.AlreadyProposed(); } address oldProposedAddress = no.proposedRewardAddress; no.proposedRewardAddress = proposedAddress; emit INOAddresses.NodeOperatorRewardAddressChangeProposed( nodeOperatorId, oldProposedAddress, proposedAddress ); } /// @notice Confirm a new reward address for the Node Operator. /// Should be called from the currently proposed address /// @param nodeOperatorId ID of the Node Operator function confirmNodeOperatorRewardAddressChange( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId ) external { NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.rewardAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (no.proposedRewardAddress != msg.sender) { revert INOAddresses.SenderIsNotProposedAddress(); } address oldAddress = no.rewardAddress; no.rewardAddress = msg.sender; delete no.proposedRewardAddress; emit INOAddresses.NodeOperatorRewardAddressChanged( nodeOperatorId, oldAddress, msg.sender ); } /// @notice Reset the manager address to the reward address. /// Should be called from the reward address /// @param nodeOperatorId ID of the Node Operator function resetNodeOperatorManagerAddress( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId ) external { NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.rewardAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (no.extendedManagerPermissions) { revert INOAddresses.MethodCallIsNotAllowed(); } if (no.rewardAddress != msg.sender) { revert INOAddresses.SenderIsNotRewardAddress(); } if (no.managerAddress == no.rewardAddress) { revert INOAddresses.SameAddress(); } address previousManagerAddress = no.managerAddress; no.managerAddress = no.rewardAddress; // @dev Gas golfing if (no.proposedManagerAddress != address(0)) { delete no.proposedManagerAddress; } emit INOAddresses.NodeOperatorManagerAddressChanged( nodeOperatorId, previousManagerAddress, no.rewardAddress ); } /// @notice Change rewardAddress if extendedManagerPermissions is enabled for the Node Operator. /// Should be called from the current manager address /// @param nodeOperatorId ID of the Node Operator /// @param newAddress New reward address function changeNodeOperatorRewardAddress( mapping(uint256 => NodeOperator) storage nodeOperators, uint256 nodeOperatorId, address newAddress ) external { if (newAddress == address(0)) { revert INOAddresses.ZeroRewardAddress(); } NodeOperator storage no = nodeOperators[nodeOperatorId]; if (no.managerAddress == address(0)) { revert ICSModule.NodeOperatorDoesNotExist(); } if (!no.extendedManagerPermissions) { revert INOAddresses.MethodCallIsNotAllowed(); } if (no.managerAddress != msg.sender) { revert INOAddresses.SenderIsNotManagerAddress(); } address oldAddress = no.rewardAddress; no.rewardAddress = newAddress; // @dev Gas golfing if (no.proposedRewardAddress != address(0)) { delete no.proposedRewardAddress; } emit INOAddresses.NodeOperatorRewardAddressChanged( nodeOperatorId, oldAddress, newAddress ); } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; type TransientUintUintMap is uint256; using TransientUintUintMapLib for TransientUintUintMap global; library TransientUintUintMapLib { function create() internal returns (TransientUintUintMap self) { // keccak256(abi.encode(uint256(keccak256("TransientUintUintMap")) - 1)) & ~bytes32(uint256(0xff)) uint256 anchor = 0x6e38e7eaa4307e6ee6c66720337876ca65012869fbef035f57219354c1728400; // `anchor` slot in the transient storage tracks the "address" of the last created object. // The next address is being computed as keccak256(`anchor` . `prev`). assembly ("memory-safe") { let prev := tload(anchor) mstore(0x00, anchor) mstore(0x20, prev) self := keccak256(0x00, 0x40) tstore(anchor, self) } } function add( TransientUintUintMap self, uint256 key, uint256 value ) internal { uint256 slot = _slot(self, key); assembly ("memory-safe") { let v := tload(slot) // NOTE: Here's no overflow check. v := add(v, value) tstore(slot, v) } } function set( TransientUintUintMap self, uint256 key, uint256 value ) internal { uint256 slot = _slot(self, key); assembly ("memory-safe") { tstore(slot, value) } } function get( TransientUintUintMap self, uint256 key ) internal view returns (uint256 v) { uint256 slot = _slot(self, key); assembly ("memory-safe") { v := tload(slot) } } function load( bytes32 tslot ) internal pure returns (TransientUintUintMap self) { assembly ("memory-safe") { self := tslot } } function _slot( TransientUintUintMap self, uint256 key ) internal pure returns (uint256 slot) { // Compute an address in the transient storage in the same manner it works for storage mappings. // `slot` = keccak256(`self` . `key`) assembly ("memory-safe") { mstore(0x00, self) mstore(0x20, key) slot := keccak256(0x00, 0x40) } } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.8.24; import { IStakingModule } from "../interfaces/IStakingModule.sol"; /// @title Library for manage operator keys in storage /// @author KRogLA library SigningKeys { using SigningKeys for bytes32; bytes32 internal constant SIGNING_KEYS_POSITION = keccak256("lido.CommunityStakingModule.signingKeysPosition"); uint64 internal constant PUBKEY_LENGTH = 48; uint64 internal constant SIGNATURE_LENGTH = 96; error InvalidKeysCount(); error InvalidLength(); error EmptyKey(); /// @dev store operator keys to storage /// @param nodeOperatorId operator id /// @param startIndex start index /// @param keysCount keys count to load /// @param pubkeys keys buffer to read from /// @param signatures signatures buffer to read from /// @return new total keys count function saveKeysSigs( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount, bytes calldata pubkeys, bytes calldata signatures ) internal returns (uint256) { if (keysCount == 0 || startIndex + keysCount > type(uint32).max) { revert InvalidKeysCount(); } unchecked { if ( pubkeys.length != keysCount * PUBKEY_LENGTH || signatures.length != keysCount * SIGNATURE_LENGTH ) { revert InvalidLength(); } } uint256 curOffset; bool isEmpty; bytes memory tmpKey = new bytes(48); for (uint256 i; i < keysCount; ) { curOffset = SIGNING_KEYS_POSITION.getKeyOffset( nodeOperatorId, startIndex ); assembly { let _ofs := add(pubkeys.offset, mul(i, 48)) // PUBKEY_LENGTH = 48 let _part1 := calldataload(_ofs) // bytes 0..31 let _part2 := calldataload(add(_ofs, 0x10)) // bytes 16..47 isEmpty := iszero(or(_part1, _part2)) mstore(add(tmpKey, 0x30), _part2) // store 2nd part first mstore(add(tmpKey, 0x20), _part1) // store 1st part with overwrite bytes 16-31 } if (isEmpty) { revert EmptyKey(); } assembly { // store key sstore(curOffset, mload(add(tmpKey, 0x20))) // store bytes 0..31 sstore(add(curOffset, 1), shl(128, mload(add(tmpKey, 0x30)))) // store bytes 32..47 // store signature let _ofs := add(signatures.offset, mul(i, 96)) // SIGNATURE_LENGTH = 96 sstore(add(curOffset, 2), calldataload(_ofs)) sstore(add(curOffset, 3), calldataload(add(_ofs, 0x20))) sstore(add(curOffset, 4), calldataload(add(_ofs, 0x40))) i := add(i, 1) startIndex := add(startIndex, 1) } emit IStakingModule.SigningKeyAdded(nodeOperatorId, tmpKey); } return startIndex; } /// @dev remove operator keys from storage /// @param nodeOperatorId operator id /// @param startIndex start index /// @param keysCount keys count to load /// @param totalKeysCount current total keys count for operator /// @return new total keys count function removeKeysSigs( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount, uint256 totalKeysCount ) internal returns (uint256) { if ( keysCount == 0 || startIndex + keysCount > totalKeysCount || totalKeysCount > type(uint32).max ) { revert InvalidKeysCount(); } uint256 curOffset; uint256 lastOffset; uint256 j; bytes memory tmpKey = new bytes(48); // removing from the last index unchecked { for (uint256 i = startIndex + keysCount; i > startIndex; ) { curOffset = SIGNING_KEYS_POSITION.getKeyOffset( nodeOperatorId, i - 1 ); assembly { // read key mstore( add(tmpKey, 0x30), shr(128, sload(add(curOffset, 1))) ) // bytes 16..47 mstore(add(tmpKey, 0x20), sload(curOffset)) // bytes 0..31 } if (i < totalKeysCount) { lastOffset = SIGNING_KEYS_POSITION.getKeyOffset( nodeOperatorId, totalKeysCount - 1 ); // move last key to deleted key index for (j = 0; j < 5; ) { // load 160 bytes (5 slots) containing key and signature assembly { sstore(add(curOffset, j), sload(add(lastOffset, j))) j := add(j, 1) } } curOffset = lastOffset; } // clear storage for (j = 0; j < 5; ) { assembly { sstore(add(curOffset, j), 0) j := add(j, 1) } } assembly { totalKeysCount := sub(totalKeysCount, 1) i := sub(i, 1) } emit IStakingModule.SigningKeyRemoved(nodeOperatorId, tmpKey); } } return totalKeysCount; } /// @dev Load operator's keys and signatures from the storage to the given in-memory arrays. /// @dev The function doesn't check for `pubkeys` and `signatures` out of boundaries access. /// @param nodeOperatorId operator id /// @param startIndex start index /// @param keysCount keys count to load /// @param pubkeys preallocated keys buffer to read in /// @param signatures preallocated signatures buffer to read in /// @param bufOffset start offset in `pubkeys`/`signatures` buffer to place values (in number of keys) function loadKeysSigs( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount, bytes memory pubkeys, bytes memory signatures, uint256 bufOffset ) internal view { uint256 curOffset; for (uint256 i; i < keysCount; ) { curOffset = SIGNING_KEYS_POSITION.getKeyOffset( nodeOperatorId, startIndex + i ); assembly { // read key let _ofs := add(add(pubkeys, 0x20), mul(add(bufOffset, i), 48)) // PUBKEY_LENGTH = 48 mstore(add(_ofs, 0x10), shr(128, sload(add(curOffset, 1)))) // bytes 16..47 mstore(_ofs, sload(curOffset)) // bytes 0..31 // store signature _ofs := add(add(signatures, 0x20), mul(add(bufOffset, i), 96)) // SIGNATURE_LENGTH = 96 mstore(_ofs, sload(add(curOffset, 2))) mstore(add(_ofs, 0x20), sload(add(curOffset, 3))) mstore(add(_ofs, 0x40), sload(add(curOffset, 4))) i := add(i, 1) } } } function loadKeys( uint256 nodeOperatorId, uint256 startIndex, uint256 keysCount ) internal view returns (bytes memory pubkeys) { uint256 curOffset; pubkeys = new bytes(keysCount * PUBKEY_LENGTH); for (uint256 i; i < keysCount; ) { curOffset = SIGNING_KEYS_POSITION.getKeyOffset( nodeOperatorId, startIndex + i ); assembly { // read key let offset := add(add(pubkeys, 0x20), mul(i, 48)) // PUBKEY_LENGTH = 48 mstore(add(offset, 0x10), shr(128, sload(add(curOffset, 1)))) // bytes 16..47 mstore(offset, sload(curOffset)) // bytes 0..31 i := add(i, 1) } } } function initKeysSigsBuf( uint256 count ) internal pure returns (bytes memory, bytes memory) { return ( new bytes(count * PUBKEY_LENGTH), new bytes(count * SIGNATURE_LENGTH) ); } function getKeyOffset( bytes32 position, uint256 nodeOperatorId, uint256 keyIndex ) internal pure returns (uint256) { return uint256( keccak256(abi.encodePacked(position, nodeOperatorId, keyIndex)) ); } }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlEnumerable.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../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 (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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:
*
* ```solidity
* 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}:
*
* ```solidity
* 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. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @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 virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @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 virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[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.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual 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.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual 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 `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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.
*
* ```solidity
* 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.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 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 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[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) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// 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;
/// @solidity memory-safe-assembly
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 in 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;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { ILido } from "../interfaces/ILido.sol"; interface IAssetRecovererLib { event EtherRecovered(address indexed recipient, uint256 amount); event ERC20Recovered( address indexed token, address indexed recipient, uint256 amount ); event StETHSharesRecovered(address indexed recipient, uint256 shares); event ERC721Recovered( address indexed token, uint256 tokenId, address indexed recipient ); event ERC1155Recovered( address indexed token, uint256 tokenId, address indexed recipient, uint256 amount ); error FailedToSendEther(); error NotAllowedToRecover(); } /* * @title AssetRecovererLib * @dev Library providing mechanisms for recovering various asset types (ETH, ERC20, ERC721, ERC1155). * This library is designed to be used by a contract that implements the AssetRecoverer interface. */ library AssetRecovererLib { using SafeERC20 for IERC20; /** * @dev Allows the sender to recover Ether held by the contract. * Emits an EtherRecovered event upon success. */ function recoverEther() external { uint256 amount = address(this).balance; (bool success, ) = msg.sender.call{ value: amount }(""); if (!success) { revert IAssetRecovererLib.FailedToSendEther(); } emit IAssetRecovererLib.EtherRecovered(msg.sender, amount); } /** * @dev Allows the sender to recover ERC20 tokens held by the contract. * @param token The address of the ERC20 token to recover. * @param amount The amount of the ERC20 token to recover. * Emits an ERC20Recovered event upon success. */ function recoverERC20(address token, uint256 amount) external { IERC20(token).safeTransfer(msg.sender, amount); emit IAssetRecovererLib.ERC20Recovered(token, msg.sender, amount); } /** * @dev Allows the sender to recover stETH shares held by the contract. * The use of a separate method for stETH is to avoid rounding problems when converting shares to stETH. * @param lido The address of the Lido contract. * @param shares The amount of stETH shares to recover. * Emits an StETHSharesRecovered event upon success. */ function recoverStETHShares(address lido, uint256 shares) external { ILido(lido).transferShares(msg.sender, shares); emit IAssetRecovererLib.StETHSharesRecovered(msg.sender, shares); } /** * @dev Allows the sender to recover ERC721 tokens held by the contract. * @param token The address of the ERC721 token to recover. * @param tokenId The token ID of the ERC721 token to recover. * Emits an ERC721Recovered event upon success. */ function recoverERC721(address token, uint256 tokenId) external { IERC721(token).safeTransferFrom(address(this), msg.sender, tokenId); emit IAssetRecovererLib.ERC721Recovered(token, tokenId, msg.sender); } /** * @dev Allows the sender to recover ERC1155 tokens held by the contract. * @param token The address of the ERC1155 token to recover. * @param tokenId The token ID of the ERC1155 token to recover. * Emits an ERC1155Recovered event upon success. */ function recoverERC1155(address token, uint256 tokenId) external { uint256 amount = IERC1155(token).balanceOf(address(this), tokenId); IERC1155(token).safeTransferFrom({ from: address(this), to: msg.sender, id: tokenId, value: amount, data: "" }); emit IAssetRecovererLib.ERC1155Recovered( token, tokenId, msg.sender, amount ); } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { ILidoLocator } from "./ILidoLocator.sol"; import { ILido } from "./ILido.sol"; import { IWithdrawalQueue } from "./IWithdrawalQueue.sol"; import { IWstETH } from "./IWstETH.sol"; interface ICSBondCore { event BondDepositedETH( uint256 indexed nodeOperatorId, address from, uint256 amount ); event BondDepositedStETH( uint256 indexed nodeOperatorId, address from, uint256 amount ); event BondDepositedWstETH( uint256 indexed nodeOperatorId, address from, uint256 amount ); event BondClaimedUnstETH( uint256 indexed nodeOperatorId, address to, uint256 amount, uint256 requestId ); event BondClaimedStETH( uint256 indexed nodeOperatorId, address to, uint256 amount ); event BondClaimedWstETH( uint256 indexed nodeOperatorId, address to, uint256 amount ); event BondBurned( uint256 indexed nodeOperatorId, uint256 amountToBurn, uint256 burnedAmount ); event BondCharged( uint256 indexed nodeOperatorId, uint256 toChargeAmount, uint256 chargedAmount ); error ZeroLocatorAddress(); error NothingToClaim(); function LIDO_LOCATOR() external view returns (ILidoLocator); function LIDO() external view returns (ILido); function WITHDRAWAL_QUEUE() external view returns (IWithdrawalQueue); function WSTETH() external view returns (IWstETH); /// @notice Get total bond shares (stETH) stored on the contract /// @return Total bond shares (stETH) function totalBondShares() external view returns (uint256); /// @notice Get bond shares (stETH) for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Bond in stETH shares function getBondShares( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get bond amount in ETH (stETH) for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Bond amount in ETH (stETH) function getBond(uint256 nodeOperatorId) external view returns (uint256); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface ICSBondCurve { /// @dev Bond curve structure. /// /// It contains: /// - intervals |> intervals-based representation of the bond curve /// /// The interval is defined by: /// - minKeysCount |> minimum keys count (inclusive) of the interval /// - minBond |> minimum bond amount (inclusive) of the interval /// - trend |> trend of the bond amount in the interval /// /// For example, how the curve intervals look like: /// Interval 0: minKeysCount = 1, minBond = 2 ETH, trend = 2 ETH /// Interval 1: minKeysCount = 2, minBond = 3.9 ETH, trend = 1.9 ETH /// Interval 2: minKeysCount = 3, minBond = 5.7 ETH, trend = 1.8 ETH /// /// Bond Amount (ETH) /// ^ /// | /// 6 - /// | ------------------ 5.7 ETH --> . /// 5.5 - ..^ /// | . | /// 5 - . | /// | . | /// 4.5 - . | /// | . | /// 4 - .. | /// | ------- 3.9 ETH --> .. | /// 3.5 - .^ | /// | .. | | /// 3 - .. | | /// | . | | /// 2.5 - . | | /// | .. | | /// 2 - -------->.. | | /// | ^ | | /// |----------|----------|----------|----------|----> Keys Count /// | 1 2 3 i /// struct BondCurve { BondCurveInterval[] intervals; } struct BondCurveInterval { uint256 minKeysCount; uint256 minBond; uint256 trend; } struct BondCurveIntervalInput { uint256 minKeysCount; uint256 trend; } event BondCurveAdded( uint256 indexed curveId, BondCurveIntervalInput[] bondCurveIntervals ); event BondCurveUpdated( uint256 indexed curveId, BondCurveIntervalInput[] bondCurveIntervals ); event BondCurveSet(uint256 indexed nodeOperatorId, uint256 curveId); error InvalidBondCurveLength(); error InvalidBondCurveValues(); error InvalidBondCurveId(); error InvalidInitializationCurveId(); function MIN_CURVE_LENGTH() external view returns (uint256); function MAX_CURVE_LENGTH() external view returns (uint256); function DEFAULT_BOND_CURVE_ID() external view returns (uint256); /// @notice Get the number of available curves /// @return Number of available curves function getCurvesCount() external view returns (uint256); /// @notice Return bond curve for the given curve id /// @param curveId Curve id to get bond curve for /// @return Bond curve /// @dev Reverts if `curveId` is invalid function getCurveInfo( uint256 curveId ) external view returns (BondCurve memory); /// @notice Get bond curve for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Bond curve function getBondCurve( uint256 nodeOperatorId ) external view returns (BondCurve memory); /// @notice Get bond curve ID for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Bond curve ID function getBondCurveId( uint256 nodeOperatorId ) external view returns (uint256); /// @notice Get required bond in ETH for the given number of keys for default bond curve /// @dev To calculate the amount for the new keys 2 calls are required: /// getBondAmountByKeysCount(newTotal) - getBondAmountByKeysCount(currentTotal) /// @param keys Number of keys to get required bond for /// @param curveId Id of the curve to perform calculations against /// @return Amount for particular keys count function getBondAmountByKeysCount( uint256 keys, uint256 curveId ) external view returns (uint256); /// @notice Get keys count for the given bond amount with default bond curve /// @param amount Bond amount in ETH (stETH)to get keys count for /// @param curveId Id of the curve to perform calculations against /// @return Keys count function getKeysCountByBondAmount( uint256 amount, uint256 curveId ) external view returns (uint256); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface ICSBondLock { /// @dev Bond lock structure. /// It contains: /// - amount |> amount of locked bond /// - until |> timestamp until locked bond is retained struct BondLock { uint128 amount; uint128 until; } event BondLockChanged( uint256 indexed nodeOperatorId, uint256 newAmount, uint256 until ); event BondLockRemoved(uint256 indexed nodeOperatorId); event BondLockPeriodChanged(uint256 period); error InvalidBondLockPeriod(); error InvalidBondLockAmount(); function MIN_BOND_LOCK_PERIOD() external view returns (uint256); function MAX_BOND_LOCK_PERIOD() external view returns (uint256); /// @notice Get default bond lock period /// @return period Default bond lock period function getBondLockPeriod() external view returns (uint256 period); /// @notice Get information about the locked bond for the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Locked bond info function getLockedBondInfo( uint256 nodeOperatorId ) external view returns (BondLock memory); /// @notice Get amount of the locked bond in ETH (stETH) by the given Node Operator /// @param nodeOperatorId ID of the Node Operator /// @return Amount of the actual locked bond function getActualLockedBond( uint256 nodeOperatorId ) external view returns (uint256); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 import { IAssetRecovererLib } from "../lib/AssetRecovererLib.sol"; import { IStETH } from "./IStETH.sol"; pragma solidity 0.8.24; interface ICSFeeDistributor is IAssetRecovererLib { struct DistributionData { /// @dev Reference slot for which the report was calculated. If the slot /// contains a block, the state being reported should include all state /// changes resulting from that block. The epoch containing the slot /// should be finalized prior to calculating the report. uint256 refSlot; /// @notice Merkle Tree root. bytes32 treeRoot; /// @notice CID of the published Merkle tree. string treeCid; /// @notice CID of the file with log of the frame reported. string logCid; /// @notice Total amount of fees distributed in the report. uint256 distributed; /// @notice Amount of the rebate shares in the report uint256 rebate; } /// @dev Emitted when fees are distributed event OperatorFeeDistributed( uint256 indexed nodeOperatorId, uint256 shares ); /// @dev Emitted when distribution data is updated event DistributionDataUpdated( uint256 totalClaimableShares, bytes32 treeRoot, string treeCid ); /// @dev Emitted when distribution log is updated event DistributionLogUpdated(string logCid); /// @dev It logs how many shares were distributed in the latest report event ModuleFeeDistributed(uint256 shares); /// @dev Emitted when rebate is transferred event RebateTransferred(uint256 shares); /// @dev Emitted when rebate recipient is set event RebateRecipientSet(address recipient); error ZeroAccountingAddress(); error ZeroStEthAddress(); error ZeroAdminAddress(); error ZeroOracleAddress(); error ZeroRebateRecipientAddress(); error SenderIsNotAccounting(); error SenderIsNotOracle(); error InvalidReportData(); error InvalidTreeRoot(); error InvalidTreeCid(); error InvalidLogCID(); error InvalidShares(); error InvalidProof(); error FeeSharesDecrease(); error NotEnoughShares(); function RECOVERER_ROLE() external view returns (bytes32); function STETH() external view returns (IStETH); function ACCOUNTING() external view returns (address); function ORACLE() external view returns (address); function treeRoot() external view returns (bytes32); function treeCid() external view returns (string calldata); function logCid() external view returns (string calldata); function distributedShares(uint256) external view returns (uint256); function totalClaimableShares() external view returns (uint256); function distributionDataHistoryCount() external view returns (uint256); function rebateRecipient() external view returns (address); /// @notice Get the initialized version of the contract function getInitializedVersion() external view returns (uint64); /// @notice Set address to send rebate to /// @param _rebateRecipient Address to send rebate to function setRebateRecipient(address _rebateRecipient) external; /// @notice Get the Amount of stETH shares that can be distributed in favor of the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param cumulativeFeeShares Total Amount of stETH shares earned as fees /// @param proof Merkle proof of the leaf /// @return sharesToDistribute Amount of stETH shares that can be distributed function getFeesToDistribute( uint256 nodeOperatorId, uint256 cumulativeFeeShares, bytes32[] calldata proof ) external view returns (uint256); /// @notice Distribute fees to the Accounting in favor of the Node Operator /// @param nodeOperatorId ID of the Node Operator /// @param cumulativeFeeShares Total Amount of stETH shares earned as fees /// @param proof Merkle proof of the leaf /// @return sharesToDistribute Amount of stETH shares distributed function distributeFees( uint256 nodeOperatorId, uint256 cumulativeFeeShares, bytes32[] calldata proof ) external returns (uint256); /// @notice Receive the data of the Merkle tree from the Oracle contract and process it /// @param _treeRoot Root of the Merkle tree /// @param _treeCid an IPFS CID of the tree /// @param _logCid an IPFS CID of the log /// @param distributed an amount of the distributed shares /// @param rebate an amount of the rebate shares /// @param refSlot refSlot of the report function processOracleReport( bytes32 _treeRoot, string calldata _treeCid, string calldata _logCid, uint256 distributed, uint256 rebate, uint256 refSlot ) external; /// @notice Get the Amount of stETH shares that are pending to be distributed /// @return pendingShares Amount shares that are pending to distribute function pendingSharesToDistribute() external view returns (uint256); /// @notice Get the historical record of distribution data /// @param index Historical entry index /// @return Historical distribution data function getHistoricalDistributionData( uint256 index ) external view returns (DistributionData memory); /// @notice Get a hash of a leaf /// @param nodeOperatorId ID of the Node Operator /// @param shares Amount of stETH shares /// @return Hash of the leaf /// @dev Double hash the leaf to prevent second preimage attacks function hashLeaf( uint256 nodeOperatorId, uint256 shares ) external pure returns (bytes32); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface IExitTypes { function VOLUNTARY_EXIT_TYPE_ID() external view returns (uint8); function STRIKES_EXIT_TYPE_ID() external view returns (uint8); }
/*
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.24;
/**
* @notice Aragon Unstructured Storage library
*/
library UnstructuredStorage {
function setStorageAddress(bytes32 position, address data) internal {
assembly {
sstore(position, data)
}
}
function setStorageUint256(bytes32 position, uint256 data) internal {
assembly {
sstore(position, data)
}
}
function getStorageAddress(
bytes32 position
) internal view returns (address data) {
assembly {
data := sload(position)
}
}
function getStorageUint256(
bytes32 position
) internal view returns (uint256 data) {
assembly {
data := sload(position)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @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.
*/
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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.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);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the value of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155Received} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `value` amount.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* WARNING: This function can potentially allow a reentrancy attack when transferring tokens
* to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
* Ensure to follow the checks-effects-interactions pattern and consider employing
* reentrancy guards when interacting with untrusted contracts.
*
* Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
*
* Requirements:
*
* - `ids` and `values` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; import { IStETH } from "./IStETH.sol"; /** * @title Interface defining Lido contract */ interface ILido is IStETH { function STAKING_CONTROL_ROLE() external view returns (bytes32); function submit(address _referral) external payable returns (uint256); function deposit( uint256 _maxDepositsCount, uint256 _stakingModuleId, bytes calldata _depositCalldata ) external; function removeStakingLimit() external; function kernel() external returns (address); function sharesOf(address _account) external view returns (uint256); }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; 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 ORACLE_ROLE() external view returns (bytes32); function getRoleMember( bytes32 role, uint256 index ) external view returns (address); function WSTETH() external view returns (address); /// @notice minimal amount of stETH that is possible to withdraw function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256); /// @notice maximum amount of stETH that is possible to withdraw by a single request /// Prevents accumulating too much funds per single request fulfillment in the future. /// @dev To withdraw larger amounts, it's recommended to split it to several requests function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256); function requestWithdrawals( uint256[] calldata _amounts, address _owner ) external returns (uint256[] memory requestIds); function getWithdrawalStatus( uint256[] calldata _requestIds ) external view returns (WithdrawalRequestStatus[] memory statuses); function getWithdrawalRequests( address _owner ) external view returns (uint256[] memory requestsIds); function isBunkerModeActive() external view returns (bool); function onOracleReport( bool _isBunkerModeNow, uint256 _bunkerStartTimestamp, uint256 _currentReportTimestamp ) external; }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.24; interface IWstETH { function balanceOf(address account) external view returns (uint256); function approve(address _spender, uint256 _amount) external returns (bool); function wrap(uint256 _stETHAmount) external returns (uint256); function unwrap(uint256 _wstETHAmount) external returns (uint256); function transferFrom( address sender, address recipient, uint256 amount ) external; function transfer(address recipient, uint256 amount) external; function getStETHByWstETH( uint256 _wstETHAmount ) external view returns (uint256); function getWstETHByStETH( uint256 _stETHAmount ) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function allowance( address _owner, address _spender ) external view returns (uint256); }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @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 (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}{
"remappings": [
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"forge-std/=node_modules/forge-std/src/",
"ds-test/=node_modules/ds-test/src/"
],
"optimizer": {
"enabled": true,
"runs": 160
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {
"src/lib/AssetRecovererLib.sol": {
"AssetRecovererLib": "0xa74528edc289b1a597Faf83fCfF7eFf871Cc01D9"
},
"src/lib/NOAddresses.sol": {
"NOAddresses": "0x7aE73890a2FE240362161BA9E83Fc996D7Fe1496"
},
"src/lib/QueueLib.sol": {
"QueueLib": "0x6eFF460627b6798C2907409EA2Fdfb287Eaa2e55"
}
}
}Contract ABI
API[{"inputs":[{"internalType":"bytes32","name":"moduleType","type":"bytes32"},{"internalType":"address","name":"lidoLocator","type":"address"},{"internalType":"address","name":"parametersRegistry","type":"address"},{"internalType":"address","name":"_accounting","type":"address"},{"internalType":"address","name":"exitPenalties","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AlreadyProposed","type":"error"},{"inputs":[],"name":"CannotAddKeys","type":"error"},{"inputs":[],"name":"EmptyKey","type":"error"},{"inputs":[],"name":"ExitedKeysDecrease","type":"error"},{"inputs":[],"name":"ExitedKeysHigherThanTotalDeposited","type":"error"},{"inputs":[],"name":"FailedToSendEther","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[],"name":"InvalidKeysCount","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"InvalidReportData","type":"error"},{"inputs":[],"name":"InvalidVetKeysPointer","type":"error"},{"inputs":[],"name":"KeysLimitExceeded","type":"error"},{"inputs":[],"name":"MethodCallIsNotAllowed","type":"error"},{"inputs":[],"name":"NoQueuedKeysToMigrate","type":"error"},{"inputs":[],"name":"NodeOperatorDoesNotExist","type":"error"},{"inputs":[],"name":"NotAllowedToRecover","type":"error"},{"inputs":[],"name":"NotEligibleForPriorityQueue","type":"error"},{"inputs":[],"name":"NotEnoughKeys","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"PauseUntilMustBeInFuture","type":"error"},{"inputs":[],"name":"PausedExpected","type":"error"},{"inputs":[],"name":"PriorityQueueAlreadyUsed","type":"error"},{"inputs":[],"name":"PriorityQueueMaxDepositsUsed","type":"error"},{"inputs":[],"name":"QueueIsEmpty","type":"error"},{"inputs":[],"name":"QueueLookupNoLimit","type":"error"},{"inputs":[],"name":"ResumedExpected","type":"error"},{"inputs":[],"name":"SameAddress","type":"error"},{"inputs":[],"name":"SenderIsNotEligible","type":"error"},{"inputs":[],"name":"SenderIsNotManagerAddress","type":"error"},{"inputs":[],"name":"SenderIsNotProposedAddress","type":"error"},{"inputs":[],"name":"SenderIsNotRewardAddress","type":"error"},{"inputs":[],"name":"SigningKeysInvalidOffset","type":"error"},{"inputs":[],"name":"ZeroAccountingAddress","type":"error"},{"inputs":[],"name":"ZeroAdminAddress","type":"error"},{"inputs":[],"name":"ZeroExitPenaltiesAddress","type":"error"},{"inputs":[],"name":"ZeroLocatorAddress","type":"error"},{"inputs":[],"name":"ZeroParametersRegistryAddress","type":"error"},{"inputs":[],"name":"ZeroPauseDuration","type":"error"},{"inputs":[],"name":"ZeroRewardAddress","type":"error"},{"inputs":[],"name":"ZeroSenderAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"queuePriority","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"BatchEnqueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositableKeysCount","type":"uint256"}],"name":"DepositableSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositedKeysCount","type":"uint256"}],"name":"DepositedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ELRewardsStealingPenaltyCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ELRewardsStealingPenaltyCompensated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"proposedBlockHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"stolenAmount","type":"uint256"}],"name":"ELRewardsStealingPenaltyReported","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"ELRewardsStealingPenaltySettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC1155Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"ERC721Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EtherRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exitedKeysCount","type":"uint256"}],"name":"ExitedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"KeyRemovalChargeApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"managerAddress","type":"address"},{"indexed":true,"internalType":"address","name":"rewardAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"extendedManagerPermissions","type":"bool"}],"name":"NodeOperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldProposedAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedAddress","type":"address"}],"name":"NodeOperatorManagerAddressChangeProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"NodeOperatorManagerAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldProposedAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedAddress","type":"address"}],"name":"NodeOperatorRewardAddressChangeProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"NodeOperatorRewardAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"NonceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"}],"name":"ReferrerSet","type":"event"},{"anonymous":false,"inputs":[],"name":"Resumed","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":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"SigningKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"SigningKeyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"StETHSharesRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"targetLimitMode","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"targetValidatorsCount","type":"uint256"}],"name":"TargetValidatorsCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalKeysCount","type":"uint256"}],"name":"TotalSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vettedKeysCount","type":"uint256"}],"name":"VettedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"VettedSigningKeysCountDecreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"keyIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"}],"name":"WithdrawalSubmitted","type":"event"},{"inputs":[],"name":"ACCOUNTING","outputs":[{"internalType":"contract ICSAccounting","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CREATE_NODE_OPERATOR_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":"DEPOSIT_SIZE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXIT_PENALTIES","outputs":[{"internalType":"contract ICSExitPenalties","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DISTRIBUTOR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIDO_LOCATOR","outputs":[{"internalType":"contract ILidoLocator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARAMETERS_REGISTRY","outputs":[{"internalType":"contract ICSParametersRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_INFINITELY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"QUEUE_LEGACY_PRIORITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"QUEUE_LOWEST_PRIORITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECOVERER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPORT_EL_REWARDS_STEALING_PENALTY_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESUME_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SETTLE_EL_REWARDS_STEALING_PENALTY_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKING_ROUTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STETH","outputs":[{"internalType":"contract IStETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERIFIER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accounting","outputs":[{"internalType":"contract ICSAccounting","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"},{"internalType":"bytes","name":"publicKeys","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"addValidatorKeysETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"},{"internalType":"bytes","name":"publicKeys","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct ICSAccounting.PermitInput","name":"permit","type":"tuple"}],"name":"addValidatorKeysStETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"},{"internalType":"bytes","name":"publicKeys","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"components":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct ICSAccounting.PermitInput","name":"permit","type":"tuple"}],"name":"addValidatorKeysWstETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"cancelELRewardsStealingPenalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"newAddress","type":"address"}],"name":"changeNodeOperatorRewardAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxItems","type":"uint256"}],"name":"cleanDepositQueue","outputs":[{"internalType":"uint256","name":"removed","type":"uint256"},{"internalType":"uint256","name":"lastRemovedAtDepth","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"compensateELRewardsStealingPenalty","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"confirmNodeOperatorManagerAddressChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"confirmNodeOperatorRewardAddressChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"components":[{"internalType":"address","name":"managerAddress","type":"address"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"extendedManagerPermissions","type":"bool"}],"internalType":"struct NodeOperatorManagementProperties","name":"managementProperties","type":"tuple"},{"internalType":"address","name":"referrer","type":"address"}],"name":"createNodeOperator","outputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"nodeOperatorIds","type":"bytes"},{"internalType":"bytes","name":"vettedSigningKeysCounts","type":"bytes"}],"name":"decreaseVettedSigningKeysCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"queuePriority","type":"uint256"},{"internalType":"uint128","name":"index","type":"uint128"}],"name":"depositQueueItem","outputs":[{"internalType":"Batch","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"queuePriority","type":"uint256"}],"name":"depositQueuePointers","outputs":[{"internalType":"uint128","name":"head","type":"uint128"},{"internalType":"uint128","name":"tail","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"exitDeadlineThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizeUpgradeV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getActiveNodeOperatorsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitializedVersion","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperator","outputs":[{"components":[{"internalType":"uint32","name":"totalAddedKeys","type":"uint32"},{"internalType":"uint32","name":"totalWithdrawnKeys","type":"uint32"},{"internalType":"uint32","name":"totalDepositedKeys","type":"uint32"},{"internalType":"uint32","name":"totalVettedKeys","type":"uint32"},{"internalType":"uint32","name":"stuckValidatorsCount","type":"uint32"},{"internalType":"uint32","name":"depositableValidatorsCount","type":"uint32"},{"internalType":"uint32","name":"targetLimit","type":"uint32"},{"internalType":"uint8","name":"targetLimitMode","type":"uint8"},{"internalType":"uint32","name":"totalExitedKeys","type":"uint32"},{"internalType":"uint32","name":"enqueuedCount","type":"uint32"},{"internalType":"address","name":"managerAddress","type":"address"},{"internalType":"address","name":"proposedManagerAddress","type":"address"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"address","name":"proposedRewardAddress","type":"address"},{"internalType":"bool","name":"extendedManagerPermissions","type":"bool"},{"internalType":"bool","name":"usedPriorityQueue","type":"bool"}],"internalType":"struct NodeOperator","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getNodeOperatorIds","outputs":[{"internalType":"uint256[]","name":"nodeOperatorIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorIsActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorManagementProperties","outputs":[{"components":[{"internalType":"address","name":"managerAddress","type":"address"},{"internalType":"address","name":"rewardAddress","type":"address"},{"internalType":"bool","name":"extendedManagerPermissions","type":"bool"}],"internalType":"struct NodeOperatorManagementProperties","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorNonWithdrawnKeys","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorSummary","outputs":[{"internalType":"uint256","name":"targetLimitMode","type":"uint256"},{"internalType":"uint256","name":"targetValidatorsCount","type":"uint256"},{"internalType":"uint256","name":"stuckValidatorsCount","type":"uint256"},{"internalType":"uint256","name":"refundedValidatorsCount","type":"uint256"},{"internalType":"uint256","name":"stuckPenaltyEndTimestamp","type":"uint256"},{"internalType":"uint256","name":"totalExitedValidators","type":"uint256"},{"internalType":"uint256","name":"totalDepositedValidators","type":"uint256"},{"internalType":"uint256","name":"depositableValidatorsCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorTotalDepositedKeys","outputs":[{"internalType":"uint256","name":"totalDepositedKeys","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNodeOperatorsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getResumeSinceTimestamp","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":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"}],"name":"getSigningKeys","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"}],"name":"getSigningKeysWithSignatures","outputs":[{"internalType":"bytes","name":"keys","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakingModuleSummary","outputs":[{"internalType":"uint256","name":"totalExitedValidators","type":"uint256"},{"internalType":"uint256","name":"totalDepositedValidators","type":"uint256"},{"internalType":"uint256","name":"depositableValidatorsCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getType","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"address","name":"admin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint256","name":"eligibleToExitInSec","type":"uint256"}],"name":"isValidatorExitDelayPenaltyApplicable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"keyIndex","type":"uint256"}],"name":"isValidatorWithdrawn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"migrateToPriorityQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"depositsCount","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"obtainDepositData","outputs":[{"internalType":"bytes","name":"publicKeys","type":"bytes"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"onExitedAndStuckValidatorsCountsUpdated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"onRewardsMinted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint256","name":"withdrawalRequestPaidFee","type":"uint256"},{"internalType":"uint256","name":"exitType","type":"uint256"}],"name":"onValidatorExitTriggered","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"onWithdrawalCredentialsChanged","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"pauseFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"proposedAddress","type":"address"}],"name":"proposeNodeOperatorManagerAddressChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"address","name":"proposedAddress","type":"address"}],"name":"proposeNodeOperatorRewardAddressChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"recoverERC1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"recoverERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoverEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"keysCount","type":"uint256"}],"name":"removeKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reportELRewardsStealingPenalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint256","name":"eligibleToExitInSec","type":"uint256"}],"name":"reportValidatorExitDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"resetNodeOperatorManagerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"nodeOperatorIds","type":"uint256[]"}],"name":"settleELRewardsStealingPenalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"keyIndex","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct ValidatorWithdrawalInfo[]","name":"withdrawalsInfo","type":"tuple[]"}],"name":"submitWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"exitedValidatorsKeysCount","type":"uint256"}],"name":"unsafeUpdateValidatorsCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"}],"name":"updateDepositableValidatorsCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"nodeOperatorIds","type":"bytes"},{"internalType":"bytes","name":"exitedValidatorsCounts","type":"bytes"}],"name":"updateExitedValidatorsCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nodeOperatorId","type":"uint256"},{"internalType":"uint256","name":"targetLimitMode","type":"uint256"},{"internalType":"uint256","name":"targetLimit","type":"uint256"}],"name":"updateTargetValidatorsLimits","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101a060405234801562000011575f80fd5b50604051620064e1380380620064e1833981016040819052620000349162000390565b6001600160a01b0384166200005c57604051630f05a38b60e41b815260040160405180910390fd5b6001600160a01b038316620000845760405163215400ff60e01b815260040160405180910390fd5b6001600160a01b038216620000ac576040516368ea2bc160e01b815260040160405180910390fd5b6001600160a01b038116620000d35760405162f9f8c960e81b815260040160405180910390fd5b60808590526001600160a01b03841660a0819052604080516323509a2d60e01b815290516323509a2d916004808201926020929091908290030181865afa15801562000121573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000147919062000400565b6001600160a01b0390811660c052831660e0819052604080516335852b8360e21b8152905163d614ae0c916004808201926020929091908290030181865afa15801562000196573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001bc919062000425565b610160818152505060e0516001600160a01b031663a6b89b816040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000203573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000229919062000425565b610180526001600160a01b038083166101008190529082166101205260408051630d43e8ad60e01b81529051630d43e8ad916004808201926020929091908290030181865afa1580156200027f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002a5919062000400565b6001600160a01b031661014052620002bc620002c7565b50505050506200043d565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620003185760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620003785780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6001600160a01b038116811462000378575f80fd5b5f805f805f60a08688031215620003a5575f80fd5b855194506020860151620003b9816200037b565b6040870151909450620003cc816200037b565b6060870151909350620003df816200037b565b6080870151909250620003f2816200037b565b809150509295509295909350565b5f6020828403121562000411575f80fd5b81516200041e816200037b565b9392505050565b5f6020828403121562000436575f80fd5b5051919050565b60805160a05160c05160e0516101005161012051610140516101605161018051615fb26200052f5f395f818161107f015261400401525f818161124d01528181611ffc015281816128e20152818161364001528181614c670152614d2201525f8181610ba7015261268301525f818161136201528181611d1d01528181611e6a015281816121a40152612c0b01525f8181610c3701526129e801525f8181610624015281816115800152818161190c0152818161240a015281816128520152818161482b0152614b9401525f81816112b3015261265601525f81816112800152613a3501525f6105360152615fb25ff3fe6080604052600436106104bb575f3560e01c80638573e3511161026d578063a6ab5b9c1161014a578063ca15c873116100be578063e00bfe5011610083578063e00bfe50146112a2578063e7705db6146112d5578063e864299e14611308578063f3f449c714611313578063f696ccb314611332578063fa367c9e14611351575f80fd5b8063ca15c873146111ea578063d087d28814611209578063d547741f1461121d578063d614ae0c1461123c578063dbba4b481461126f575f80fd5b8063b187bd261161010f578063b187bd26146110f3578063b3076c3c14611107578063b3c6501514611161578063b643189b1461118d578063bee41b58146111ac578063c4d66de8146111cb575f80fd5b8063a6ab5b9c1461104f578063a6b89b811461106e578063a70c70e414610d50578063acf1c948146110a1578063b055e15c146110d4575f80fd5b806394120368116101e15780639c963aef116101a65780639c963aef14610f9f578063a0c8c47e14610fbe578063a1913f4b14610ff6578063a217fddf14611009578063a302ee381461101c578063a4516c9814611030575f80fd5b80639412036814610ee95780639417366f14610f085780639624e83e14610f275780639abddf0914610f3b5780639b00c14614610f80575f80fd5b80638d7e4017116102325780638d7e401714610e185780638eab3cd014610e375780638ec6902814610e565780639010d07c14610e9757806390c09bdb14610eb657806391d1485414610eca575f80fd5b80638573e35114610d7457806388984a9714610da75780638980f11f14610dbb5780638b3ac71d14610dda5780638cabe95914610df9575f80fd5b806352d8bfc21161039b5780636a6304cc1161030f578063743f5105116102d4578063743f510514610ca057806375a401da14610cd357806380231f1514610cf2578063819d4cc614610d1257806383b57a4e14610d315780638469cbd314610d50575f80fd5b80636a6304cc14610be85780636bb1bfdf14610c075780636dc3f2bd14610c265780636efe37a214610c59578063735dfa2814610c6c575f80fd5b806359e25c121161036057806359e25c12146109665780635c654ad9146109925780635e2fb908146109b157806365c14dc7146109e25780636910dcce14610b96578063693cc60014610bc9575f80fd5b806352d8bfc2146108ab57806353433643146108bf57806357f9c341146108f45780635810f62214610913578063589ff76c14610952575f80fd5b806336bf3325116104325780633f04f0c8116103f75780633f04f0c814610729578063400448011461075c578063499b8e9a1461077b5780634febc81b1461083357806350388cb61461085f5780635204281c1461088c575f80fd5b806336bf33251461067d57806337b12b5f1461069957806337ebdf6f146106b8578063388dd1d1146106d7578063389ed267146106f6575f80fd5b8063248a9ca311610483578063248a9ca31461058357806328d6d36b146105a25780632de03aa1146105c15780632f2ff15d146105f45780632fc887411461061357806336568abe1461065e575f80fd5b806301ffc9a7146104bf578063046f7da2146104f357806308a679ad1461050957806315dae03e146105285780631b40b23114610564575b5f80fd5b3480156104ca575f80fd5b506104de6104d9366004615341565b611384565b60405190151581526020015b60405180910390f35b3480156104fe575f80fd5b506105076113ae565b005b348015610514575f80fd5b50610507610523366004615368565b6113e3565b348015610533575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020016104ea565b34801561056f575f80fd5b5061050761057e3660046153a5565b6114e9565b34801561058e575f80fd5b5061055661059d3660046153d3565b611554565b3480156105ad575f80fd5b506105566105bc3660046153d3565b611574565b3480156105cc575f80fd5b506105567f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b3480156105ff575f80fd5b5061050761060e3660046153a5565b61167e565b34801561061e575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016104ea565b348015610669575f80fd5b506105076106783660046153a5565b6116a0565b348015610688575f80fd5b506105566801bc16d674ec80000081565b3480156106a4575f80fd5b506105076106b33660046153ea565b6116d8565b3480156106c3575f80fd5b506105566106d2366004615458565b6117ed565b3480156106e2575f80fd5b506105076106f1366004615368565b611821565b348015610701575f80fd5b506105567f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b348015610734575f80fd5b506105567fe85fdec10fe0f93d0792364051df7c3d73e37c17b3a954bffe593960e3cd301281565b348015610767575f80fd5b50610507610776366004615486565b611a2f565b348015610786575f80fd5b506108016107953660046153d3565b60408051606080820183525f80835260208084018290529284018190529384526006825292829020825193840183526001810154600160401b90046001600160a01b0390811685526003820154169184019190915260040154600160a01b900460ff1615159082015290565b6040805182516001600160a01b03908116825260208085015190911690820152918101511515908201526060016104ea565b34801561083e575f80fd5b5061085261084d366004615486565b611b0d565b6040516104ea91906154a6565b34801561086a575f80fd5b5061087e610879366004615368565b611bf5565b6040516104ea92919061552c565b348015610897575f80fd5b506105076108a63660046153d3565b611c27565b3480156108b6575f80fd5b50610507611c8a565b3480156108ca575f80fd5b506104de6108d9366004615486565b60809190911b175f9081526007602052604090205460ff1690565b3480156108ff575f80fd5b5061050761090e366004615594565b611ce6565b34801561091e575f80fd5b5061093261092d3660046153d3565b611d8d565b604080516001600160801b039384168152929091166020830152016104ea565b34801561095d575f80fd5b50610556611db7565b348015610971575f80fd5b50610985610980366004615368565b611dd2565b6040516104ea91906155e9565b34801561099d575f80fd5b506105076109ac3660046155fb565b611df2565b3480156109bc575f80fd5b506104de6109cb3660046153d3565b600954600160c01b90046001600160401b03161190565b3480156109ed575f80fd5b50610b896109fc3660046153d3565b60408051610200810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e0810191909152505f90815260066020908152604091829020825161020081018452815463ffffffff8082168352600160201b808304821695840195909552600160401b808304821696840196909652600160601b820481166060840152600160801b820481166080840152600160a01b808304821660a0850152600160c01b8304821660c085015260ff600160e01b909304831660e085015260018501548083166101008601529586049091166101208401526001600160a01b0395909404851661014083015260028301548516610160830152600383015485166101808301526004909201549384166101a0820152918304811615156101c0830152600160a81b90920490911615156101e082015290565b6040516104ea9190615625565b348015610ba1575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b348015610bd4575f80fd5b50610507610be336600461575f565b611e33565b348015610bf3575f80fd5b50610507610c023660046153d3565b611ea7565b348015610c12575f80fd5b50610507610c213660046153d3565b611ee6565b348015610c31575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b610507610c673660046153d3565b611f25565b348015610c77575f80fd5b50610c8b610c863660046153d3565b611fd8565b604080519283526020830191909152016104ea565b348015610cab575f80fd5b506105567fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf043847881565b348015610cde575f80fd5b50610507610ced3660046153a5565b612106565b348015610cfd575f80fd5b506105565f80516020615f0683398151915281565b348015610d1d575f80fd5b50610507610d2c3660046155fb565b612142565b348015610d3c575f80fd5b506104de610d4b366004615594565b612183565b348015610d5b575f80fd5b50600954600160c01b90046001600160401b0316610556565b348015610d7f575f80fd5b506105567f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de930281565b348015610db2575f80fd5b50610507612228565b348015610dc6575f80fd5b50610507610dd53660046155fb565b6122ed565b348015610de5575f80fd5b50610507610df4366004615368565b61232e565b348015610e04575f80fd5b50610507610e133660046153a5565b6125ec565b348015610e23575f80fd5b50610507610e323660046153d3565b612628565b348015610e42575f80fd5b50610507610e513660046153d3565b6126ed565b348015610e61575f80fd5b50610556610e703660046153d3565b5f9081526006602052604090205463ffffffff600160201b82048116918116919091031690565b348015610ea2575f80fd5b50610646610eb1366004615486565b6126f8565b348015610ec1575f80fd5b5061050761271d565b348015610ed5575f80fd5b506104de610ee43660046153a5565b61273c565b348015610ef4575f80fd5b50610507610f03366004615486565b612772565b348015610f13575f80fd5b50610507610f223660046153d3565b61279d565b348015610f32575f80fd5b506106466129e6565b348015610f46575f80fd5b50600954604080516001600160401b03600160401b8404811682528084166020830152600160801b909304909216908201526060016104ea565b348015610f8b575f80fd5b50610507610f9a3660046157b7565b612a0a565b348015610faa575f80fd5b50610507610fb936600461581d565b612a6c565b348015610fc9575f80fd5b50610556610fd83660046153d3565b5f90815260066020526040902054600160401b900463ffffffff1690565b610507611004366004615879565b612e2a565b348015611014575f80fd5b506105565f81565b348015611027575f80fd5b506105565f1981565b34801561103b575f80fd5b5061055661104a366004615907565b612f4d565b34801561105a575f80fd5b50610507611069366004615958565b61318d565b348015611079575f80fd5b506105567f000000000000000000000000000000000000000000000000000000000000000081565b3480156110ac575f80fd5b506105567fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc81565b3480156110df575f80fd5b506106466110ee3660046153d3565b6132a0565b3480156110fe575f80fd5b506104de6132ed565b348015611112575f80fd5b506111266111213660046153d3565b61330a565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e0820152610100016104ea565b34801561116c575f80fd5b506111756134a2565b6040516001600160401b0390911681526020016104ea565b348015611198575f80fd5b506105076111a73660046157b7565b6134c1565b3480156111b7575f80fd5b5061087e6111c6366004615a01565b61360b565b3480156111d6575f80fd5b506105076111e5366004615a48565b613973565b3480156111f5575f80fd5b506105566112043660046153d3565b613b0f565b348015611214575f80fd5b50600554610556565b348015611228575f80fd5b506105076112373660046153a5565b613b33565b348015611247575f80fd5b506105567f000000000000000000000000000000000000000000000000000000000000000081565b34801561127a575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b3480156112ad575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b3480156112e0575f80fd5b506105567f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea0981565b348015610507575f80fd5b34801561131e575f80fd5b5061050761132d3660046153d3565b613b51565b34801561133d575f80fd5b5061050761134c366004615958565b613b88565b34801561135c575f80fd5b506106467f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b03198216635a05180f60e01b14806113a857506113a882613c54565b92915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c76113d881613c88565b6113e0613c92565b50565b5f80516020615f068339815191526113fa81613c88565b600283111561141c5760405163b4fa3fb360e01b815260040160405180910390fd5b63ffffffff8211156114415760405163b4fa3fb360e01b815260040160405180910390fd5b61144a84613cd4565b5f84815260066020526040812090849003611463575f92505b805464ffffffffff60c01b1916600160e01b60ff86160263ffffffff60c01b191617600160c01b63ffffffff851602178155604080518581526020810185905286917ff92eb109ce5b449e9b121c352c6aeb4319538a90738cb95d84f08e41274e92d2910160405180910390a26114da855f613d0a565b6114e2613f46565b5050505050565b60405162d74f0b60e71b8152737ae73890a2fe240362161ba9e83fc996d7fe149690636ba78580906115249060069086908690600401615a63565b5f6040518083038186803b15801561153a575f80fd5b505af415801561154c573d5f803e3d5ffd5b505050505050565b5f9081525f80516020615f26833981519152602052604090206001015490565b5f61157e82613cd4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639a7b05086115b56129e6565b6001600160a01b0316630569b947856040518263ffffffff1660e01b81526004016115e291815260200190565b602060405180830381865afa1580156115fd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116219190615a82565b6040518263ffffffff1660e01b815260040161163f91815260200190565b602060405180830381865afa15801561165a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a89190615a82565b61168782611554565b61169081613c88565b61169a8383613f86565b50505050565b6001600160a01b03811633146116c95760405163334bd91960e11b815260040160405180910390fd5b6116d38282613fc8565b505050565b7fe85fdec10fe0f93d0792364051df7c3d73e37c17b3a954bffe593960e3cd301261170281613c88565b5f5b8281101561169a575f84848381811061171f5761171f615a99565b90506020020135905061173181613cd4565b5f61173a6129e6565b6001600160a01b0316634bb22a72836040518263ffffffff1660e01b815260040161176791815260200190565b6020604051808303815f875af1158015611783573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a79190615aba565b905080156117e35760405182907ef4fe19c0404d2fbb58da6f646c0a3ee5a6994a034213bbd22b072ed1ca5c27905f90a26117e3826001613d0a565b5050600101611704565b5f61181a826117fb85614001565b906001600160801b03165f908152600191909101602052604090205490565b9392505050565b7f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de930261184b81613c88565b61185484613cd4565b815f036118745760405163162908e360e11b815260040160405180910390fd5b5f61187d6129e6565b6001600160a01b0316630569b947866040518263ffffffff1660e01b81526004016118aa91815260200190565b602060405180830381865afa1580156118c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118e99190615a82565b6040516307a994c760e01b8152600481018290529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906307a994c790602401602060405180830381865afa158015611951573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119759190615a82565b905061197f6129e6565b6001600160a01b031663dcab7f83876119988488615ae9565b6040516001600160e01b031960e085901b168152600481019290925260248201526044015f604051808303815f87803b1580156119d3575f80fd5b505af11580156119e5573d5f803e3d5ffd5b505060408051888152602081018890528993507feec4d6dbe34149c6728a9638eca869d0e5a7fcd85c7a96178f7e9780b4b7fe4b92500160405180910390a261154c866001613d0a565b7f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de9302611a5981613c88565b611a6283613cd4565b611a6a6129e6565b60405163d963ae5560e01b815260048101859052602481018490526001600160a01b03919091169063d963ae55906044015f604051808303815f87803b158015611ab2575f80fd5b505af1158015611ac4573d5f803e3d5ffd5b50505050827f1e7ebd3c5f4de9502000b6f7e6e7cf5d4ecb27d6fe1778e43fb9d1d0ca87d0e783604051611afa91815260200190565b60405180910390a26116d3836001613d0a565b600954606090600160c01b90046001600160401b03168084101580611b30575082155b15611b4a575050604080515f8152602081019091526113a8565b5f611b558583615b10565b8410611b6a57611b658583615b10565b611b6c565b835b9050806001600160401b03811115611b8657611b86615afc565b604051908082528060200260200182016040528015611baf578160200160208202803683370190505b5092505f5b8351811015611bec57611bc78187615ae9565b848281518110611bd957611bd9615a99565b6020908102919091010152600101611bb4565b50505092915050565b606080611c03858585614045565b611c0c83614082565b9092509050611c1f85858585855f614128565b935093915050565b604051631f46d51760e01b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe149690631f46d517906044015b5f6040518083038186803b158015611c78575f80fd5b505af41580156114e2573d5f803e3d5ffd5b611c926141a3565b73a74528edc289b1a597faf83fcff7eff871cc01d96352d8bfc26040518163ffffffff1660e01b81526004015f6040518083038186803b158015611cd4575f80fd5b505af415801561169a573d5f803e3d5ffd5b5f80516020615f06833981519152611cfd81613c88565b611d0686613cd4565b6040516344dab94960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906344dab94990611d58908990889088908890600401615b4b565b5f604051808303815f87803b158015611d6f575f80fd5b505af1158015611d81573d5f803e3d5ffd5b50505050505050505050565b5f805f611d9984614001565b546001600160801b0380821696600160801b90920416945092505050565b5f611dcd5f80516020615f468339815191525490565b905090565b6060611ddf848484614045565b611dea8484846141cc565b949350505050565b611dfa6141a3565b604051635c654ad960e01b815273a74528edc289b1a597faf83fcff7eff871cc01d990635c654ad9906115249085908590600401615b75565b5f80516020615f06833981519152611e4a81613c88565b611e5386613cd4565b604051632122c77b60e21b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063848b1dec90611d589089908990899089908990600401615b8e565b60405163612b8c3b60e11b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe14969063c257187690604401611c62565b60405163c990450f60e01b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe14969063c990450f90604401611c62565b611f2f8133614267565b611f376129e6565b6001600160a01b03166315b5c47734836040518363ffffffff1660e01b8152600401611f6591815260200190565b5f604051808303818588803b158015611f7c575f80fd5b505af1158015611f8e573d5f803e3d5ffd5b5050505050807fb1858b4c2ab6242521725a8f7350a6cb22ad4ecae009c9b63ef114baffb054be34604051611fc591815260200190565b60405180910390a26113e0816001613d0a565b5f80828103611feb57505f928392509050565b5f611ff46142d8565b90505f80805b7f000000000000000000000000000000000000000000000000000000000000000081116120fd5761202a81614001565b6040516304ada34360e41b8152600481018290526006602482015260448101899052606481018690529093506001909101905f90819081908190736eff460627b6798c2907409ea2fdfb287eaa2e5590634ada343090608401608060405180830381865af415801561209e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c29190615bbb565b93509350935093505f8411156120dc579883019885830198505b806120ea57505050506120fd565b8186019550818b039a5050505050611ffa565b50505050915091565b6040516317a9a2c160e11b8152737ae73890a2fe240362161ba9e83fc996d7fe149690632f534582906115249060069086908690600401615a63565b61214a6141a3565b6040516340cea66360e11b815273a74528edc289b1a597faf83fcff7eff871cc01d99063819d4cc6906115249085908590600401615b75565b5f61218d86613cd4565b60405163d404037960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d4040379906121df908990889088908890600401615b4b565b602060405180830381865afa1580156121fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061221e9190615aba565b9695505050505050565b5f80516020615f66833981519152805460029190600160401b900460ff168061225e575080546001600160401b03808416911610155b1561227c5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b038316908117600160401b1782555f8080556004819055600355815460ff60401b191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b6122f56141a3565b604051638980f11f60e01b815273a74528edc289b1a597faf83fcff7eff871cc01d990638980f11f906115249085908590600401615b75565b6123388333614267565b5f8381526006602052604090208054600160401b900463ffffffff1683101561237457604051635caf530f60e11b815260040160405180910390fd5b80545f9061238d9086908690869063ffffffff1661430e565b90505f6123986129e6565b6001600160a01b0316630569b947876040518263ffffffff1660e01b81526004016123c591815260200190565b602060405180830381865afa1580156123e0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124049190615a82565b90505f847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f42d7db5846040518263ffffffff1660e01b815260040161245691815260200190565b602060405180830381865afa158015612471573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124959190615a82565b61249f9190615bf9565b90508015612538576124af6129e6565b604051632207e80f60e21b815260048101899052602481018390526001600160a01b03919091169063881fa03c906044015f604051808303815f87803b1580156124f7575f80fd5b505af1158015612509573d5f803e3d5ffd5b50506040518992507f1cbb8dafbedbdf4f813a8ed1f50d871def63e1104f8729b677af57905eda90f691505f90a25b835463ffffffff191663ffffffff841617845560405183815287907fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09060200160405180910390a2835463ffffffff60601b1916600160601b63ffffffff85160217845560405183815287907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a26125db875f613d0a565b6125e3613f46565b50505050505050565b604051632a5a705b60e01b8152737ae73890a2fe240362161ba9e83fc996d7fe149690632a5a705b906115249060069086908690600401615a63565b5f80516020615f0683398151915261263f81613c88565b604051638fcb4e5b60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638fcb4e5b906126ad907f0000000000000000000000000000000000000000000000000000000000000000908690600401615b75565b6020604051808303815f875af11580156126c9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116d39190615a82565b6113e0816001613d0a565b5f8281525f80516020615ee6833981519152602081905260408220611dea9084614472565b5f80516020615f0683398151915261273481613c88565b6113e0613f46565b5f9182525f80516020615f26833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f80516020615f0683398151915261278981613c88565b6127958383600161447d565b6116d3613f46565b5f8181526006602052604090206004810154600160a81b900460ff16156127d757604051634d5bd9a760e01b815260040160405180910390fd5b5f6127e06129e6565b6001600160a01b0316630569b947846040518263ffffffff1660e01b815260040161280d91815260200190565b602060405180830381865afa158015612828573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061284c9190615a82565b90505f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663decfec56846040518263ffffffff1660e01b815260040161289e91815260200190565b6040805180830381865afa1580156128b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128dc9190615c23565b915091507f00000000000000000000000000000000000000000000000000000000000000008263ffffffff16036129265760405163bba5f23f60e01b815260040160405180910390fd5b6001840154600160201b900463ffffffff165f8190036129595760405163ebe0edcd60e01b815260040160405180910390fd5b845463ffffffff600160401b9091048116908316811061298c576040516327da251f60e21b815260040160405180910390fd5b5f6129ac63ffffffff84166129a18487615c54565b63ffffffff1661459b565b90506129bf888663ffffffff16836145b0565b60048701805460ff60a81b1916600160a81b1790556129dc613f46565b5050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b5f80516020615f06833981519152612a2181613c88565b5f612a2e86868686614664565b90505f5b81811015612a63576008810287013560c01c6010820286013560801c612a5982825f61447d565b5050600101612a32565b5061154c613f46565b7f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea09612a9681613c88565b5f805b83811015612e1b575f858583818110612ab457612ab4615a99565b905060600201803603810190612aca9190615ca5565b9050612ad8815f0151613cd4565b80515f9081526006602090815260409091208054918301519091600160401b900463ffffffff1611612b1d57604051635caf530f60e11b815260040160405180910390fd5b815160208301515f9160801b175f8181526007602052604090205490915060ff1615612b4b57505050612e13565b5f8181526007602090815260408220805460ff19166001908117909155845463ffffffff600160201b808304821684019091160267ffffffff0000000019909116178555855191860151612b9f92916141cc565b9050835f01517f9bc54857932b6f10bb750fdad91f736b82dd4de202ed5c2f9f076773bb5b3fb78560200151866040015184604051612be093929190615cdf565b60405180910390a2835160405163e83ba79d60e01b8152600197505f91829182916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163e83ba79d91612c40918890600401615cfd565b60c060405180830381865afa158015612c5b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c7f9190615d8e565b80516020015190915015612ca3578051516001600160f81b03169290920191600191505b80602001516020015115612cca576020810151516001600160f81b03169290920191600191505b818015612ce457506040810151516001600160f81b031615155b15612d6057612cf16129e6565b8751604083810151519051632207e80f60e21b815260048101929092526001600160f81b031660248201526001600160a01b03919091169063881fa03c906044015f604051808303815f87803b158015612d49575f80fd5b505af1158015612d5b573d5f803e3d5ffd5b505050505b86604001516801bc16d674ec8000001115612d8a5786604001516801bc16d674ec80000003830192505b8215612dff57612d986129e6565b875160405163e5220e3f60e01b81526001600160a01b03929092169163e5220e3f91612dd1918790600401918252602082015260400190565b5f604051808303815f87803b158015612de8575f80fd5b505af1158015612dfa573d5f803e3d5ffd5b505050505b8651612e0b905f613d0a565b505050505050505b600101612a99565b50801561169a5761169a613f46565b612e326146d7565b612e3c86886146fd565b612e446129e6565b6040516358a46db560e11b815260048101889052602481018790526001600160a01b03919091169063b148db6a90604401602060405180830381865afa158015612e90573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb49190615a82565b341015612ed45760405163162908e360e11b815260040160405180910390fd5b3415612f3f57612ee26129e6565b6001600160a01b0316632e5990543489896040518463ffffffff1660e01b8152600401612f10929190615b75565b5f604051808303818588803b158015612f27575f80fd5b505af1158015612f39573d5f803e3d5ffd5b50505050505b6125e3868686868686614771565b5f7fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf0438478612f7881613c88565b612f806146d7565b6001600160a01b038516612faa5760405160016232750f60e21b0319815260040160405180910390fd5b600954600160c01b90046001600160401b03169150612fc8826149b0565b5f828152600660209081526040822091908190612fe790880188615a48565b6001600160a01b031614613007576130026020870187615a48565b613009565b865b90505f8061301d6040890160208a01615a48565b6001600160a01b0316146130405761303b6040880160208901615a48565b613042565b875b600184018054600160401b600160e01b031916600160401b6001600160a01b0386811691909102919091179091556003850180546001600160a01b031916918316919091179055905061309b6060880160408901615ddd565b156130b65760048301805460ff60a01b1916600160a01b1790555b6009805460016001600160401b03600160c01b80840482169290920116026001600160c01b039091161790556001600160a01b03808216908316867ff17baf73d46b0a80157c3ea3dda1bf081a702732d53ff1720f85e55d9f0997c061312260608c0160408d01615ddd565b604051901515815260200160405180910390a46001600160a01b0386161561317a576040516001600160a01b0387169086907f67334334c388385e5f244703f8a8b28b7f4ffe52909130aca69bc62a8e27f09a905f90a35b613182613f46565b505050509392505050565b6131956146d7565b61319f87896146fd565b5f6131a86129e6565b604051632884698160e01b8152600481018a9052602481018990526001600160a01b039190911690632884698190604401602060405180830381865afa1580156131f4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132189190615a82565b90508015613287576132286129e6565b6001600160a01b031663f7966efe8a8a84866040518563ffffffff1660e01b81526004016132599493929190615df8565b5f604051808303815f87803b158015613270575f80fd5b505af1158015613282573d5f803e3d5ffd5b505050505b613295888888888888614771565b505050505050505050565b5f8181526006602052604081206004810154600160a01b900460ff166132d35760038101546001600160a01b031661181a565b60010154600160401b90046001600160a01b031692915050565b5f6133035f80516020615f468339815191525490565b4210905090565b5f805f805f805f8061331b89613cd4565b5f898152600660205260408120906133316129e6565b6001600160a01b0316639c5161028c6040518263ffffffff1660e01b815260040161335e91815260200190565b602060405180830381865afa158015613379573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061339d9190615a82565b82549091505f906133bd9063ffffffff600160401b820481169116615c54565b63ffffffff16905080821180156133df57508254600160e01b900460ff166002145b1561341a57825460029b506134139063ffffffff600160c01b8204811691600160201b81048216908216031684900361459b565b9950613466565b8082111561344857825460029b5063ffffffff600160201b8204811691811691909103168290039950613466565b8254600160e01b810460ff169b50600160c01b900463ffffffff1699505b505060018101549054989a9799505f98899889985063ffffffff9283169750600160401b820483169650600160a01b9091049091169350915050565b5f611dcd5f80516020615f66833981519152546001600160401b031690565b5f80516020615f068339815191526134d881613c88565b5f6134e586868686614664565b90505f5b81811015612a63576008810287013560c01c6010820286013560801c61350e82613cd4565b5f8281526006602052604090208054600160601b900463ffffffff168210613549576040516388e1a28160e01b815260040160405180910390fd5b8054600160401b900463ffffffff16821015613578576040516388e1a28160e01b815260040160405180910390fd5b805463ffffffff60601b1916600160601b63ffffffff84160217815560405182815283907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a260405183907fe5725d045d5c47bd1483feba445e395dc8647486963e6d54aad9ed03ff7d6ce6905f90a26135fd835f613d0a565b5050508060010190506134e9565b6060805f80516020615f0683398151915261362581613c88565b61362e86614082565b9093509150851561396a57855f80805b7f000000000000000000000000000000000000000000000000000000000000000081118061366a575083155b6138e45761367781614001565b91506001015f6136a08380546001600160801b03165f9081526001909101602052604090205490565b90505b80156138de575f6136b48260c01c90565b6001600160401b039081165f8181526006602052604081208054929450608086901c90931692916136fc906136f690600160a01b900463ffffffff168561459b565b8a61459b565b90508089118061370b57508281145b1561374b5760018201805463ffffffff600160201b80830482168790039091160267ffffffff0000000019909116179055613745876149dc565b506137aa565b60018201805463ffffffff600160201b808304821685900382160267ffffffff00000000199092169190911790915561378a90869083860390614a3b16565b87546001600160801b03165f908152600189016020526040902081905594505b805f036137ba57505050506138bd565b81546137d8908590600160401b900463ffffffff16838f8f8d614128565b815463ffffffff600160401b8083048216840191821690810263ffffffff60401b19909316929092178455604051918252988201989085907f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b789060200160405180910390a2825463ffffffff600160a01b808304821685900391821690810263ffffffff60a01b199093169290921785556040519182529086907ff9109091b368cedad2edff45414eef892edd6b4fe80084bd590aa8f8def8ed339060200160405180910390a2828b039a508a5f036138b6575050505050506138de565b5050505050505b5081546001600160801b03165f9081526001830160205260409020546136a3565b5061363e565b89831461390457604051630bc9ea5560e21b815260040160405180910390fd5b600980546001600160401b03600160801b80830482168e9003821602808216828416178e0190911667ffffffffffffffff1990911677ffffffffffffffff0000000000000000ffffffffffffffff1990921691909117179055613965613f46565b505050505b50935093915050565b5f80516020615f66833981519152805460029190600160401b900460ff16806139a9575080546001600160401b03808416911610155b156139c75760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b1781556001600160a01b038316613a0f57604051633ef39b8160e01b815260040160405180910390fd5b613a17614a60565b613a215f84613f86565b50613ab85f80516020615f068339815191527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613ab39190615e5f565b613f86565b50613ac35f19614a68565b805460ff60401b191681556040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050565b5f8181525f80516020615ee683398151915260208190526040822061181a90614ab7565b613b3c82611554565b613b4581613c88565b61169a8383613fc8565b565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d613b7b81613c88565b613b8482614a68565b5050565b613b906146d7565b613b9a87896146fd565b5f613ba36129e6565b6040516358a46db560e11b8152600481018a9052602481018990526001600160a01b03919091169063b148db6a90604401602060405180830381865afa158015613bef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c139190615a82565b9050801561328757613c236129e6565b6001600160a01b0316634c7ed3d28a8a84866040518563ffffffff1660e01b81526004016132599493929190615df8565b5f6001600160e01b03198216637965db0b60e01b14806113a857506301ffc9a760e01b6001600160e01b03198316146113a8565b6113e08133614ac0565b613c9a614af4565b425f80516020615f46833981519152556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f9905f90a1565b600954600160c01b90046001600160401b0316811015613cf15750565b604051633ed893db60e21b815260040160405180910390fd5b5f8281526006602052604081208054909163ffffffff600160401b8304811692613d3d918491600160601b900416615c54565b63ffffffff1690505f613d4e6129e6565b6001600160a01b03166301a5e9e3876040518263ffffffff1660e01b8152600401613d7b91815260200190565b602060405180830381865afa158015613d96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613dba9190615a82565b84549091505f90613dd290859063ffffffff16615c54565b63ffffffff169050808210613de9575f9250613e1e565b8454613e049063ffffffff600160601b820481169116615c54565b63ffffffff16821115613e1e57613e1b8282615b10565b92505b508354600160e01b900460ff1615801590613e3857505f82115b15613e8857835463ffffffff600160201b820481168503811691613e8491600160c01b909104168210613e6b575f613e7e565b8554600160c01b900463ffffffff168290035b8461459b565b9250505b8354600160a01b900463ffffffff16821461154c5783546009805467ffffffffffffffff60801b198116600160a01b9384900463ffffffff908116600160801b938490046001600160401b039081169190910388011690920217909155855463ffffffff60a01b191690841690910217845560405182815286907ff9109091b368cedad2edff45414eef892edd6b4fe80084bd590aa8f8def8ed339060200160405180910390a28415613f3d57613f3d613f46565b61154c86614b19565b60058054600101908190556040519081527f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9060200160405180910390a1565b5f5f80516020615ee683398151915281613fa08585614d47565b90508015611dea575f858152602083905260409020613fbf9085614def565b50949350505050565b5f5f80516020615ee683398151915281613fe28585614e03565b90508015611dea575f858152602083905260409020613fbf9085614e7c565b5f7f0000000000000000000000000000000000000000000000000000000000000000820361403157506001919050565b505f8181526020819052604090205b919050565b5f8381526006602052604090205463ffffffff166140638284615ae9565b11156116d357604051635caf530f60e11b815260040160405180910390fd5b606080614090603084615bf9565b6001600160401b038111156140a7576140a7615afc565b6040519080825280601f01601f1916602001820160405280156140d1576020820181803683370190505b506140dd606085615bf9565b6001600160401b038111156140f4576140f4615afc565b6040519080825280601f01601f19166020018201604052801561411e576020820181803683370190505b5091509150915091565b5f805b858110156129dc5761415688614141838a615ae9565b5f80516020615f868339815191529190614e90565b60018082015460801c85840160308181028a01908101929092528354602092830152600284015460609182028901928301526003840154604083015260048401549101529092500161412b565b613b4f7fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc613c88565b60605f6141da603084615bf9565b6001600160401b038111156141f1576141f1615afc565b6040519080825280601f01601f19166020018201604052801561421b576020820181803683370190505b5091505f5b8381101561425e57614236866141418388615ae9565b9150603081026020840101600183015460801c60108201528254815250600181019050614220565b50509392505050565b5f82815260066020526040902060010154600160401b90046001600160a01b0316806142a657604051633ed893db60e21b815260040160405180910390fd5b816001600160a01b0316816001600160a01b0316146116d35760405163743a3f7960e11b815260040160405180910390fd5b7f6e38e7eaa4307e6ee6c66720337876ca65012869fbef035f57219354c17284005f818152815c602052604090209081815d5090565b5f8215806143245750816143228486615ae9565b115b80614332575063ffffffff82115b156143505760405163575697ff60e01b815260040160405180910390fd5b604080516030808252606082019092525f91829182918291906020820181803683370190505090508787015b888111156144645761439f5f80516020615f868339815191528b5f198401614e90565b9450600185015460801c603083015284546020830152868110156143fe576143d85f80516020615f868339815191528b5f198a01614e90565b93505f92505b60058310156143fa5782840154838601556001830192506143de565b8394505b5f92505b600583101561441b575f83860155600183019250614402565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b98360405161445791906155e9565b60405180910390a261437c565b509498975050505050505050565b5f61181a8383614ec7565b61448683613cd4565b5f838152600660205260409020600181015463ffffffff168084036144ac575050505050565b8154600160401b900463ffffffff168411156144db5760405163cc11217f60e01b815260040160405180910390fd5b821580156144ee57508063ffffffff1684105b1561450c576040516371a4bd1560e01b815260040160405180910390fd5b6009805467ffffffffffffffff60401b19811663ffffffff848116600160401b938490046001600160401b03908116919091038901169092021790915560018301805463ffffffff191691861691909117905560405184815285907f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c2201669060200160405180910390a25050505050565b5f8183106145a9578161181a565b5090919050565b5f8381526006602052604090206001810180548391906004906145e1908490600160201b900463ffffffff16615e7a565b92506101000a81548163ffffffff021916908363ffffffff1602179055505f61460984614001565b905061461f818663ffffffff80871690614eed16565b5060405163ffffffff84168152859085907fdc891a44aee443f7f65d1abc5710a05ef241c0c5d7a62f12671522f3c14852bc9060200160405180910390a35050505050565b5f614670600885615eab565b61467b601084615eab565b141580614691575061468e600885615ebe565b15155b806146a557506146a2601083615ebe565b15155b156146c35760405163319c9a2160e21b815260040160405180910390fd5b6146ce600885615eab565b95945050505050565b6146df6132ed565b15613b4f57604051630286f07360e31b815260040160405180910390fd5b336001600160a01b0382160361471757613b848233614267565b6147407fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf0438478613c88565b3361474a83614f5d565b6001600160a01b031614613b84576040516310b922ef60e21b815260040160405180910390fd5b61477a86614f93565b5f8681526006602052604081208054909163ffffffff9091169061479c6129e6565b6001600160a01b0316630569b9478a6040518263ffffffff1660e01b81526004016147c991815260200190565b602060405180830381865afa1580156147e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906148089190615a82565b60405163014dddeb60e51b8152600481018290529091505f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906329bbbd6090602401602060405180830381865afa158015614870573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906148949190615a82565b8454909150600160201b900463ffffffff16898401038110156148ca57604051630911e76760e41b815260040160405180910390fd5b5f6148da8b858c8c8c8c8c614fbf565b8554909150600160601b900463ffffffff16840361495557845463ffffffff600160601b80830482168d0191821690810263ffffffff60601b19909316929092178755604051918252908c907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a2505b845463ffffffff191663ffffffff82161785556040518181528b907fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09060200160405180910390a2506149a88a5f613d0a565b611d81613f46565b7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a500613b84818333615140565b80546001600160801b03165f90815260018201602052604090205480614a15576040516363c3654960e01b815260040160405180910390fd5b81546fffffffffffffffffffffffffffffffff19166001600160801b0382161790915590565b60801b67ffffffffffffffff60801b1667ffffffffffffffff60801b19919091161790565b613b4f615156565b614a706146d7565b805f03614a905760405163ad58bfc760e01b815260040160405180910390fd5b5f5f198203614aa157505f19614aae565b614aab8242615ae9565b90505b613b848161518c565b5f6113a8825490565b614aca828261273c565b613b8457808260405163e2517d3f60e01b8152600401614aeb929190615b75565b60405180910390fd5b614afc6132ed565b613b4f5760405163b047186b60e01b815260040160405180910390fd5b5f614b226129e6565b6001600160a01b0316630569b947836040518263ffffffff1660e01b8152600401614b4f91815260200190565b602060405180830381865afa158015614b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614b8e9190615a82565b90505f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663decfec56846040518263ffffffff1660e01b8152600401614be091815260200190565b6040805180830381865afa158015614bfa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614c1e9190615c23565b5f868152600660205260409020805460018201549395509193509163ffffffff600160a01b909204821691600160201b90910416808211614c625750505050505050565b8082037f000000000000000000000000000000000000000000000000000000000000000063ffffffff87161015614d1057835463ffffffff600160401b90910481168301908181169087161115614d0e578086035f614cca63ffffffff80861690841661459b565b9050614cdd8b8a63ffffffff16836145b0565b60048701549381900393600160a81b900460ff16614d0b5760048701805460ff60a81b1916600160a81b1790555b50505b505b63ffffffff8116156129dc576129dc887f0000000000000000000000000000000000000000000000000000000000000000836145b0565b5f5f80516020615f26833981519152614d60848461273c565b614ddf575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055614d953390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506113a8565b5f9150506113a8565b5092915050565b5f61181a836001600160a01b03841661521b565b5f5f80516020615f26833981519152614e1c848461273c565b15614ddf575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506113a8565b5f61181a836001600160a01b038416615267565b6040805160208082019590955280820193909352606080840192909252805180840390920182526080909201909152805191012090565b5f825f018281548110614edc57614edc615a99565b905f5260205f200154905092915050565b8254600160801b908190046001600160801b039081165f8181526001808801602052604090912060809590951b67ffffffffffffffff60801b1660c09690961b6001600160c01b031916959095179085011792839055845480821690839004821690940116029190911790915590565b5f7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a50061181a81845f918252602052604090205c90565b7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a500613b8481835f615140565b5f851580614fd9575063ffffffff614fd78789615ae9565b115b15614ff75760405163575697ff60e01b815260040160405180910390fd5b603086028414158061500c5750606086028214155b1561502a5760405163251f56a160e21b815260040160405180910390fd5b604080516030808252606082019092525f91829182916020820181803683370190505090505f5b89811015615130576150715f80516020615f868339815191528d8d614e90565b60308281028b01601081013591850182905235602085018190529195501715925082156150b157604051630f35a7eb60e21b815260040160405180910390fd5b60208201518455603082015160801b60018501556060810287018035600286015560208101356003860155604081013560048601555060018101905060018b019a508b7fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a898360405161512391906155e9565b60405180910390a2615051565b50989a9950505050505050505050565b5f83815260208390526040902081815d50505050565b5f80516020615f6683398151915254600160401b900460ff16613b4f57604051631afcd79f60e31b815260040160405180910390fd5b6151a25f80516020615f46833981519152829055565b5f1981036151e2576040515f1981527f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e906020015b60405180910390a150565b7f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e61520d4283615b10565b6040519081526020016151d7565b5f81815260018301602052604081205461526057508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556113a8565b505f6113a8565b5f8181526001830160205260408120548015614ddf575f615289600183615b10565b85549091505f9061529c90600190615b10565b90508082146152fb575f865f0182815481106152ba576152ba615a99565b905f5260205f200154905080875f0184815481106152da576152da615a99565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061530c5761530c615ed1565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506113a8565b5f60208284031215615351575f80fd5b81356001600160e01b03198116811461181a575f80fd5b5f805f6060848603121561537a575f80fd5b505081359360208301359350604090920135919050565b6001600160a01b03811681146113e0575f80fd5b5f80604083850312156153b6575f80fd5b8235915060208301356153c881615391565b809150509250929050565b5f602082840312156153e3575f80fd5b5035919050565b5f80602083850312156153fb575f80fd5b82356001600160401b0380821115615411575f80fd5b818501915085601f830112615424575f80fd5b813581811115615432575f80fd5b8660208260051b8501011115615446575f80fd5b60209290920196919550909350505050565b5f8060408385031215615469575f80fd5b8235915060208301356001600160801b03811681146153c8575f80fd5b5f8060408385031215615497575f80fd5b50508035926020909101359150565b602080825282518282018190525f9190848201906040850190845b818110156154dd578351835292840192918401916001016154c1565b50909695505050505050565b5f81518084525f5b8181101561550d576020818501810151868301820152016154f1565b505f602082860101526020601f19601f83011685010191505092915050565b604081525f61553e60408301856154e9565b82810360208401526146ce81856154e9565b5f8083601f840112615560575f80fd5b5081356001600160401b03811115615576575f80fd5b60208301915083602082850101111561558d575f80fd5b9250929050565b5f805f805f608086880312156155a8575f80fd5b853594506020860135935060408601356001600160401b038111156155cb575f80fd5b6155d788828901615550565b96999598509660600135949350505050565b602081525f61181a60208301846154e9565b5f806040838503121561560c575f80fd5b823561561781615391565b946020939093013593505050565b815163ffffffff1681526102008101602083015161564b602084018263ffffffff169052565b506040830151615663604084018263ffffffff169052565b50606083015161567b606084018263ffffffff169052565b506080830151615693608084018263ffffffff169052565b5060a08301516156ab60a084018263ffffffff169052565b5060c08301516156c360c084018263ffffffff169052565b5060e08301516156d860e084018260ff169052565b506101008381015163ffffffff908116918401919091526101208085015190911690830152610140808401516001600160a01b039081169184019190915261016080850151821690840152610180808501518216908401526101a080850151909116908301526101c0808401511515908301526101e0928301511515929091019190915290565b5f805f805f60808688031215615773575f80fd5b8535945060208601356001600160401b0381111561578f575f80fd5b61579b88828901615550565b9699909850959660408101359660609091013595509350505050565b5f805f80604085870312156157ca575f80fd5b84356001600160401b03808211156157e0575f80fd5b6157ec88838901615550565b90965094506020870135915080821115615804575f80fd5b5061581187828801615550565b95989497509550505050565b5f806020838503121561582e575f80fd5b82356001600160401b0380821115615844575f80fd5b818501915085601f830112615857575f80fd5b813581811115615865575f80fd5b866020606083028501011115615446575f80fd5b5f805f805f805f60a0888a03121561588f575f80fd5b873561589a81615391565b9650602088013595506040880135945060608801356001600160401b03808211156158c3575f80fd5b6158cf8b838c01615550565b909650945060808a01359150808211156158e7575f80fd5b506158f48a828b01615550565b989b979a50959850939692959293505050565b5f805f83850360a081121561591a575f80fd5b843561592581615391565b93506060601f1982011215615938575f80fd5b50602084019150608084013561594d81615391565b809150509250925092565b5f805f805f805f80888a03610140811215615971575f80fd5b893561597c81615391565b985060208a0135975060408a0135965060608a01356001600160401b03808211156159a5575f80fd5b6159b18d838e01615550565b909850965060808c01359150808211156159c9575f80fd5b506159d68c828d01615550565b90955093505060a0609f19820112156159ed575f80fd5b5060a0890190509295985092959890939650565b5f805f60408486031215615a13575f80fd5b8335925060208401356001600160401b03811115615a2f575f80fd5b615a3b86828701615550565b9497909650939450505050565b5f60208284031215615a58575f80fd5b813561181a81615391565b92835260208301919091526001600160a01b0316604082015260600190565b5f60208284031215615a92575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b80151581146113e0575f80fd5b5f60208284031215615aca575f80fd5b815161181a81615aad565b634e487b7160e01b5f52601160045260245ffd5b808201808211156113a8576113a8615ad5565b634e487b7160e01b5f52604160045260245ffd5b818103818111156113a8576113a8615ad5565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152606060208201525f615b64606083018587615b23565b905082604083015295945050505050565b6001600160a01b03929092168252602082015260400190565b858152608060208201525f615ba7608083018688615b23565b604083019490945250606001529392505050565b5f805f8060808587031215615bce575f80fd5b8451935060208501519250604085015191506060850151615bee81615aad565b939692955090935050565b80820281158282048414176113a8576113a8615ad5565b805163ffffffff81168114614040575f80fd5b5f8060408385031215615c34575f80fd5b615c3d83615c10565b9150615c4b60208401615c10565b90509250929050565b63ffffffff828116828216039080821115614de857614de8615ad5565b604051606081016001600160401b0381118282101715615c9f57634e487b7160e01b5f52604160045260245ffd5b60405290565b5f60608284031215615cb5575f80fd5b615cbd615c71565b8235815260208301356020820152604083013560408201528091505092915050565b838152826020820152606060408201525f6146ce60608301846154e9565b828152604060208201525f611dea60408301846154e9565b5f60408284031215615d25575f80fd5b604051604081018181106001600160401b0382111715615d5357634e487b7160e01b5f52604160045260245ffd5b604052825190915081906001600160f81b0381168114615d71575f80fd5b81526020830151615d8181615aad565b6020919091015292915050565b5f60c08284031215615d9e575f80fd5b615da6615c71565b615db08484615d15565b8152615dbf8460408501615d15565b6020820152615dd18460808501615d15565b60408201529392505050565b5f60208284031215615ded575f80fd5b813561181a81615aad565b6001600160a01b0385168152602080820185905260408083018590528335606084015290830135608083015261010082019083013560ff8116808214615e3c575f80fd5b60a084015250606083013560c083015260809092013560e0909101529392505050565b5f60208284031215615e6f575f80fd5b815161181a81615391565b63ffffffff818116838216019080821115614de857614de8615ad5565b634e487b7160e01b5f52601260045260245ffd5b5f82615eb957615eb9615e97565b500490565b5f82615ecc57615ecc615e97565b500690565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c602dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800e8b012900cb200ee5dfc3b895a32791b67d12891b09f117814f167a237783a02f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00059e9c54cf92ba46cc39c6b4acd51d5116e9d49fabee6193530ea918b54be94aa164736f6c6343000818000a636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000e2ef9536daaaebff5b1c130957ab3e80056b06d8000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad000000000000000000000000a54b90ba34c5f326bc1485054080994e38fb4c60000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800
Deployed Bytecode
0x6080604052600436106104bb575f3560e01c80638573e3511161026d578063a6ab5b9c1161014a578063ca15c873116100be578063e00bfe5011610083578063e00bfe50146112a2578063e7705db6146112d5578063e864299e14611308578063f3f449c714611313578063f696ccb314611332578063fa367c9e14611351575f80fd5b8063ca15c873146111ea578063d087d28814611209578063d547741f1461121d578063d614ae0c1461123c578063dbba4b481461126f575f80fd5b8063b187bd261161010f578063b187bd26146110f3578063b3076c3c14611107578063b3c6501514611161578063b643189b1461118d578063bee41b58146111ac578063c4d66de8146111cb575f80fd5b8063a6ab5b9c1461104f578063a6b89b811461106e578063a70c70e414610d50578063acf1c948146110a1578063b055e15c146110d4575f80fd5b806394120368116101e15780639c963aef116101a65780639c963aef14610f9f578063a0c8c47e14610fbe578063a1913f4b14610ff6578063a217fddf14611009578063a302ee381461101c578063a4516c9814611030575f80fd5b80639412036814610ee95780639417366f14610f085780639624e83e14610f275780639abddf0914610f3b5780639b00c14614610f80575f80fd5b80638d7e4017116102325780638d7e401714610e185780638eab3cd014610e375780638ec6902814610e565780639010d07c14610e9757806390c09bdb14610eb657806391d1485414610eca575f80fd5b80638573e35114610d7457806388984a9714610da75780638980f11f14610dbb5780638b3ac71d14610dda5780638cabe95914610df9575f80fd5b806352d8bfc21161039b5780636a6304cc1161030f578063743f5105116102d4578063743f510514610ca057806375a401da14610cd357806380231f1514610cf2578063819d4cc614610d1257806383b57a4e14610d315780638469cbd314610d50575f80fd5b80636a6304cc14610be85780636bb1bfdf14610c075780636dc3f2bd14610c265780636efe37a214610c59578063735dfa2814610c6c575f80fd5b806359e25c121161036057806359e25c12146109665780635c654ad9146109925780635e2fb908146109b157806365c14dc7146109e25780636910dcce14610b96578063693cc60014610bc9575f80fd5b806352d8bfc2146108ab57806353433643146108bf57806357f9c341146108f45780635810f62214610913578063589ff76c14610952575f80fd5b806336bf3325116104325780633f04f0c8116103f75780633f04f0c814610729578063400448011461075c578063499b8e9a1461077b5780634febc81b1461083357806350388cb61461085f5780635204281c1461088c575f80fd5b806336bf33251461067d57806337b12b5f1461069957806337ebdf6f146106b8578063388dd1d1146106d7578063389ed267146106f6575f80fd5b8063248a9ca311610483578063248a9ca31461058357806328d6d36b146105a25780632de03aa1146105c15780632f2ff15d146105f45780632fc887411461061357806336568abe1461065e575f80fd5b806301ffc9a7146104bf578063046f7da2146104f357806308a679ad1461050957806315dae03e146105285780631b40b23114610564575b5f80fd5b3480156104ca575f80fd5b506104de6104d9366004615341565b611384565b60405190151581526020015b60405180910390f35b3480156104fe575f80fd5b506105076113ae565b005b348015610514575f80fd5b50610507610523366004615368565b6113e3565b348015610533575f80fd5b507f636f6d6d756e6974792d6f6e636861696e2d76310000000000000000000000005b6040519081526020016104ea565b34801561056f575f80fd5b5061050761057e3660046153a5565b6114e9565b34801561058e575f80fd5b5061055661059d3660046153d3565b611554565b3480156105ad575f80fd5b506105566105bc3660046153d3565b611574565b3480156105cc575f80fd5b506105567f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b3480156105ff575f80fd5b5061050761060e3660046153a5565b61167e565b34801561061e575f80fd5b506106467f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad81565b6040516001600160a01b0390911681526020016104ea565b348015610669575f80fd5b506105076106783660046153a5565b6116a0565b348015610688575f80fd5b506105566801bc16d674ec80000081565b3480156106a4575f80fd5b506105076106b33660046153ea565b6116d8565b3480156106c3575f80fd5b506105566106d2366004615458565b6117ed565b3480156106e2575f80fd5b506105076106f1366004615368565b611821565b348015610701575f80fd5b506105567f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b348015610734575f80fd5b506105567fe85fdec10fe0f93d0792364051df7c3d73e37c17b3a954bffe593960e3cd301281565b348015610767575f80fd5b50610507610776366004615486565b611a2f565b348015610786575f80fd5b506108016107953660046153d3565b60408051606080820183525f80835260208084018290529284018190529384526006825292829020825193840183526001810154600160401b90046001600160a01b0390811685526003820154169184019190915260040154600160a01b900460ff1615159082015290565b6040805182516001600160a01b03908116825260208085015190911690820152918101511515908201526060016104ea565b34801561083e575f80fd5b5061085261084d366004615486565b611b0d565b6040516104ea91906154a6565b34801561086a575f80fd5b5061087e610879366004615368565b611bf5565b6040516104ea92919061552c565b348015610897575f80fd5b506105076108a63660046153d3565b611c27565b3480156108b6575f80fd5b50610507611c8a565b3480156108ca575f80fd5b506104de6108d9366004615486565b60809190911b175f9081526007602052604090205460ff1690565b3480156108ff575f80fd5b5061050761090e366004615594565b611ce6565b34801561091e575f80fd5b5061093261092d3660046153d3565b611d8d565b604080516001600160801b039384168152929091166020830152016104ea565b34801561095d575f80fd5b50610556611db7565b348015610971575f80fd5b50610985610980366004615368565b611dd2565b6040516104ea91906155e9565b34801561099d575f80fd5b506105076109ac3660046155fb565b611df2565b3480156109bc575f80fd5b506104de6109cb3660046153d3565b600954600160c01b90046001600160401b03161190565b3480156109ed575f80fd5b50610b896109fc3660046153d3565b60408051610200810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e0810191909152505f90815260066020908152604091829020825161020081018452815463ffffffff8082168352600160201b808304821695840195909552600160401b808304821696840196909652600160601b820481166060840152600160801b820481166080840152600160a01b808304821660a0850152600160c01b8304821660c085015260ff600160e01b909304831660e085015260018501548083166101008601529586049091166101208401526001600160a01b0395909404851661014083015260028301548516610160830152600383015485166101808301526004909201549384166101a0820152918304811615156101c0830152600160a81b90920490911615156101e082015290565b6040516104ea9190615625565b348015610ba1575f80fd5b506106467f000000000000000000000000acd9820b0a2229a82dc1a0770307ce5522ff358281565b348015610bd4575f80fd5b50610507610be336600461575f565b611e33565b348015610bf3575f80fd5b50610507610c023660046153d3565b611ea7565b348015610c12575f80fd5b50610507610c213660046153d3565b611ee6565b348015610c31575f80fd5b506106467f000000000000000000000000a54b90ba34c5f326bc1485054080994e38fb4c6081565b610507610c673660046153d3565b611f25565b348015610c77575f80fd5b50610c8b610c863660046153d3565b611fd8565b604080519283526020830191909152016104ea565b348015610cab575f80fd5b506105567fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf043847881565b348015610cde575f80fd5b50610507610ced3660046153a5565b612106565b348015610cfd575f80fd5b506105565f80516020615f0683398151915281565b348015610d1d575f80fd5b50610507610d2c3660046155fb565b612142565b348015610d3c575f80fd5b506104de610d4b366004615594565b612183565b348015610d5b575f80fd5b50600954600160c01b90046001600160401b0316610556565b348015610d7f575f80fd5b506105567f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de930281565b348015610db2575f80fd5b50610507612228565b348015610dc6575f80fd5b50610507610dd53660046155fb565b6122ed565b348015610de5575f80fd5b50610507610df4366004615368565b61232e565b348015610e04575f80fd5b50610507610e133660046153a5565b6125ec565b348015610e23575f80fd5b50610507610e323660046153d3565b612628565b348015610e42575f80fd5b50610507610e513660046153d3565b6126ed565b348015610e61575f80fd5b50610556610e703660046153d3565b5f9081526006602052604090205463ffffffff600160201b82048116918116919091031690565b348015610ea2575f80fd5b50610646610eb1366004615486565b6126f8565b348015610ec1575f80fd5b5061050761271d565b348015610ed5575f80fd5b506104de610ee43660046153a5565b61273c565b348015610ef4575f80fd5b50610507610f03366004615486565b612772565b348015610f13575f80fd5b50610507610f223660046153d3565b61279d565b348015610f32575f80fd5b506106466129e6565b348015610f46575f80fd5b50600954604080516001600160401b03600160401b8404811682528084166020830152600160801b909304909216908201526060016104ea565b348015610f8b575f80fd5b50610507610f9a3660046157b7565b612a0a565b348015610faa575f80fd5b50610507610fb936600461581d565b612a6c565b348015610fc9575f80fd5b50610556610fd83660046153d3565b5f90815260066020526040902054600160401b900463ffffffff1690565b610507611004366004615879565b612e2a565b348015611014575f80fd5b506105565f81565b348015611027575f80fd5b506105565f1981565b34801561103b575f80fd5b5061055661104a366004615907565b612f4d565b34801561105a575f80fd5b50610507611069366004615958565b61318d565b348015611079575f80fd5b506105567f000000000000000000000000000000000000000000000000000000000000000481565b3480156110ac575f80fd5b506105567fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc81565b3480156110df575f80fd5b506106466110ee3660046153d3565b6132a0565b3480156110fe575f80fd5b506104de6132ed565b348015611112575f80fd5b506111266111213660046153d3565b61330a565b604080519889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e0820152610100016104ea565b34801561116c575f80fd5b506111756134a2565b6040516001600160401b0390911681526020016104ea565b348015611198575f80fd5b506105076111a73660046157b7565b6134c1565b3480156111b7575f80fd5b5061087e6111c6366004615a01565b61360b565b3480156111d6575f80fd5b506105076111e5366004615a48565b613973565b3480156111f5575f80fd5b506105566112043660046153d3565b613b0f565b348015611214575f80fd5b50600554610556565b348015611228575f80fd5b506105076112373660046153a5565b613b33565b348015611247575f80fd5b506105567f000000000000000000000000000000000000000000000000000000000000000581565b34801561127a575f80fd5b506106467f000000000000000000000000e2ef9536daaaebff5b1c130957ab3e80056b06d881565b3480156112ad575f80fd5b506106467f0000000000000000000000003508a952176b3c15387c97be809eaffb1982176a81565b3480156112e0575f80fd5b506105567f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea0981565b348015610507575f80fd5b34801561131e575f80fd5b5061050761132d3660046153d3565b613b51565b34801561133d575f80fd5b5061050761134c366004615958565b613b88565b34801561135c575f80fd5b506106467f000000000000000000000000d259b31083be841e5c85b2d481cfc17c1427680081565b5f6001600160e01b03198216635a05180f60e01b14806113a857506113a882613c54565b92915050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c76113d881613c88565b6113e0613c92565b50565b5f80516020615f068339815191526113fa81613c88565b600283111561141c5760405163b4fa3fb360e01b815260040160405180910390fd5b63ffffffff8211156114415760405163b4fa3fb360e01b815260040160405180910390fd5b61144a84613cd4565b5f84815260066020526040812090849003611463575f92505b805464ffffffffff60c01b1916600160e01b60ff86160263ffffffff60c01b191617600160c01b63ffffffff851602178155604080518581526020810185905286917ff92eb109ce5b449e9b121c352c6aeb4319538a90738cb95d84f08e41274e92d2910160405180910390a26114da855f613d0a565b6114e2613f46565b5050505050565b60405162d74f0b60e71b8152737ae73890a2fe240362161ba9e83fc996d7fe149690636ba78580906115249060069086908690600401615a63565b5f6040518083038186803b15801561153a575f80fd5b505af415801561154c573d5f803e3d5ffd5b505050505050565b5f9081525f80516020615f26833981519152602052604090206001015490565b5f61157e82613cd4565b7f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad6001600160a01b0316639a7b05086115b56129e6565b6001600160a01b0316630569b947856040518263ffffffff1660e01b81526004016115e291815260200190565b602060405180830381865afa1580156115fd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116219190615a82565b6040518263ffffffff1660e01b815260040161163f91815260200190565b602060405180830381865afa15801561165a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113a89190615a82565b61168782611554565b61169081613c88565b61169a8383613f86565b50505050565b6001600160a01b03811633146116c95760405163334bd91960e11b815260040160405180910390fd5b6116d38282613fc8565b505050565b7fe85fdec10fe0f93d0792364051df7c3d73e37c17b3a954bffe593960e3cd301261170281613c88565b5f5b8281101561169a575f84848381811061171f5761171f615a99565b90506020020135905061173181613cd4565b5f61173a6129e6565b6001600160a01b0316634bb22a72836040518263ffffffff1660e01b815260040161176791815260200190565b6020604051808303815f875af1158015611783573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117a79190615aba565b905080156117e35760405182907ef4fe19c0404d2fbb58da6f646c0a3ee5a6994a034213bbd22b072ed1ca5c27905f90a26117e3826001613d0a565b5050600101611704565b5f61181a826117fb85614001565b906001600160801b03165f908152600191909101602052604090205490565b9392505050565b7f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de930261184b81613c88565b61185484613cd4565b815f036118745760405163162908e360e11b815260040160405180910390fd5b5f61187d6129e6565b6001600160a01b0316630569b947866040518263ffffffff1660e01b81526004016118aa91815260200190565b602060405180830381865afa1580156118c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118e99190615a82565b6040516307a994c760e01b8152600481018290529091505f906001600160a01b037f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad16906307a994c790602401602060405180830381865afa158015611951573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119759190615a82565b905061197f6129e6565b6001600160a01b031663dcab7f83876119988488615ae9565b6040516001600160e01b031960e085901b168152600481019290925260248201526044015f604051808303815f87803b1580156119d3575f80fd5b505af11580156119e5573d5f803e3d5ffd5b505060408051888152602081018890528993507feec4d6dbe34149c6728a9638eca869d0e5a7fcd85c7a96178f7e9780b4b7fe4b92500160405180910390a261154c866001613d0a565b7f59911a6aa08a72fe3824aec4500dc42335c6d0702b6d5c5c72ceb265a0de9302611a5981613c88565b611a6283613cd4565b611a6a6129e6565b60405163d963ae5560e01b815260048101859052602481018490526001600160a01b03919091169063d963ae55906044015f604051808303815f87803b158015611ab2575f80fd5b505af1158015611ac4573d5f803e3d5ffd5b50505050827f1e7ebd3c5f4de9502000b6f7e6e7cf5d4ecb27d6fe1778e43fb9d1d0ca87d0e783604051611afa91815260200190565b60405180910390a26116d3836001613d0a565b600954606090600160c01b90046001600160401b03168084101580611b30575082155b15611b4a575050604080515f8152602081019091526113a8565b5f611b558583615b10565b8410611b6a57611b658583615b10565b611b6c565b835b9050806001600160401b03811115611b8657611b86615afc565b604051908082528060200260200182016040528015611baf578160200160208202803683370190505b5092505f5b8351811015611bec57611bc78187615ae9565b848281518110611bd957611bd9615a99565b6020908102919091010152600101611bb4565b50505092915050565b606080611c03858585614045565b611c0c83614082565b9092509050611c1f85858585855f614128565b935093915050565b604051631f46d51760e01b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe149690631f46d517906044015b5f6040518083038186803b158015611c78575f80fd5b505af41580156114e2573d5f803e3d5ffd5b611c926141a3565b73a74528edc289b1a597faf83fcff7eff871cc01d96352d8bfc26040518163ffffffff1660e01b81526004015f6040518083038186803b158015611cd4575f80fd5b505af415801561169a573d5f803e3d5ffd5b5f80516020615f06833981519152611cfd81613c88565b611d0686613cd4565b6040516344dab94960e01b81526001600160a01b037f000000000000000000000000d259b31083be841e5c85b2d481cfc17c1427680016906344dab94990611d58908990889088908890600401615b4b565b5f604051808303815f87803b158015611d6f575f80fd5b505af1158015611d81573d5f803e3d5ffd5b50505050505050505050565b5f805f611d9984614001565b546001600160801b0380821696600160801b90920416945092505050565b5f611dcd5f80516020615f468339815191525490565b905090565b6060611ddf848484614045565b611dea8484846141cc565b949350505050565b611dfa6141a3565b604051635c654ad960e01b815273a74528edc289b1a597faf83fcff7eff871cc01d990635c654ad9906115249085908590600401615b75565b5f80516020615f06833981519152611e4a81613c88565b611e5386613cd4565b604051632122c77b60e21b81526001600160a01b037f000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800169063848b1dec90611d589089908990899089908990600401615b8e565b60405163612b8c3b60e11b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe14969063c257187690604401611c62565b60405163c990450f60e01b81526006600482015260248101829052737ae73890a2fe240362161ba9e83fc996d7fe14969063c990450f90604401611c62565b611f2f8133614267565b611f376129e6565b6001600160a01b03166315b5c47734836040518363ffffffff1660e01b8152600401611f6591815260200190565b5f604051808303818588803b158015611f7c575f80fd5b505af1158015611f8e573d5f803e3d5ffd5b5050505050807fb1858b4c2ab6242521725a8f7350a6cb22ad4ecae009c9b63ef114baffb054be34604051611fc591815260200190565b60405180910390a26113e0816001613d0a565b5f80828103611feb57505f928392509050565b5f611ff46142d8565b90505f80805b7f000000000000000000000000000000000000000000000000000000000000000581116120fd5761202a81614001565b6040516304ada34360e41b8152600481018290526006602482015260448101899052606481018690529093506001909101905f90819081908190736eff460627b6798c2907409ea2fdfb287eaa2e5590634ada343090608401608060405180830381865af415801561209e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120c29190615bbb565b93509350935093505f8411156120dc579883019885830198505b806120ea57505050506120fd565b8186019550818b039a5050505050611ffa565b50505050915091565b6040516317a9a2c160e11b8152737ae73890a2fe240362161ba9e83fc996d7fe149690632f534582906115249060069086908690600401615a63565b61214a6141a3565b6040516340cea66360e11b815273a74528edc289b1a597faf83fcff7eff871cc01d99063819d4cc6906115249085908590600401615b75565b5f61218d86613cd4565b60405163d404037960e01b81526001600160a01b037f000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800169063d4040379906121df908990889088908890600401615b4b565b602060405180830381865afa1580156121fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061221e9190615aba565b9695505050505050565b5f80516020615f66833981519152805460029190600160401b900460ff168061225e575080546001600160401b03808416911610155b1561227c5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b038316908117600160401b1782555f8080556004819055600355815460ff60401b191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050565b6122f56141a3565b604051638980f11f60e01b815273a74528edc289b1a597faf83fcff7eff871cc01d990638980f11f906115249085908590600401615b75565b6123388333614267565b5f8381526006602052604090208054600160401b900463ffffffff1683101561237457604051635caf530f60e11b815260040160405180910390fd5b80545f9061238d9086908690869063ffffffff1661430e565b90505f6123986129e6565b6001600160a01b0316630569b947876040518263ffffffff1660e01b81526004016123c591815260200190565b602060405180830381865afa1580156123e0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124049190615a82565b90505f847f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad6001600160a01b031663f42d7db5846040518263ffffffff1660e01b815260040161245691815260200190565b602060405180830381865afa158015612471573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124959190615a82565b61249f9190615bf9565b90508015612538576124af6129e6565b604051632207e80f60e21b815260048101899052602481018390526001600160a01b03919091169063881fa03c906044015f604051808303815f87803b1580156124f7575f80fd5b505af1158015612509573d5f803e3d5ffd5b50506040518992507f1cbb8dafbedbdf4f813a8ed1f50d871def63e1104f8729b677af57905eda90f691505f90a25b835463ffffffff191663ffffffff841617845560405183815287907fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09060200160405180910390a2835463ffffffff60601b1916600160601b63ffffffff85160217845560405183815287907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a26125db875f613d0a565b6125e3613f46565b50505050505050565b604051632a5a705b60e01b8152737ae73890a2fe240362161ba9e83fc996d7fe149690632a5a705b906115249060069086908690600401615a63565b5f80516020615f0683398151915261263f81613c88565b604051638fcb4e5b60e01b81526001600160a01b037f0000000000000000000000003508a952176b3c15387c97be809eaffb1982176a1690638fcb4e5b906126ad907f000000000000000000000000acd9820b0a2229a82dc1a0770307ce5522ff3582908690600401615b75565b6020604051808303815f875af11580156126c9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116d39190615a82565b6113e0816001613d0a565b5f8281525f80516020615ee6833981519152602081905260408220611dea9084614472565b5f80516020615f0683398151915261273481613c88565b6113e0613f46565b5f9182525f80516020615f26833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f80516020615f0683398151915261278981613c88565b6127958383600161447d565b6116d3613f46565b5f8181526006602052604090206004810154600160a81b900460ff16156127d757604051634d5bd9a760e01b815260040160405180910390fd5b5f6127e06129e6565b6001600160a01b0316630569b947846040518263ffffffff1660e01b815260040161280d91815260200190565b602060405180830381865afa158015612828573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061284c9190615a82565b90505f807f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad6001600160a01b031663decfec56846040518263ffffffff1660e01b815260040161289e91815260200190565b6040805180830381865afa1580156128b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128dc9190615c23565b915091507f00000000000000000000000000000000000000000000000000000000000000058263ffffffff16036129265760405163bba5f23f60e01b815260040160405180910390fd5b6001840154600160201b900463ffffffff165f8190036129595760405163ebe0edcd60e01b815260040160405180910390fd5b845463ffffffff600160401b9091048116908316811061298c576040516327da251f60e21b815260040160405180910390fd5b5f6129ac63ffffffff84166129a18487615c54565b63ffffffff1661459b565b90506129bf888663ffffffff16836145b0565b60048701805460ff60a81b1916600160a81b1790556129dc613f46565b5050505050505050565b7f000000000000000000000000a54b90ba34c5f326bc1485054080994e38fb4c6090565b5f80516020615f06833981519152612a2181613c88565b5f612a2e86868686614664565b90505f5b81811015612a63576008810287013560c01c6010820286013560801c612a5982825f61447d565b5050600101612a32565b5061154c613f46565b7f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea09612a9681613c88565b5f805b83811015612e1b575f858583818110612ab457612ab4615a99565b905060600201803603810190612aca9190615ca5565b9050612ad8815f0151613cd4565b80515f9081526006602090815260409091208054918301519091600160401b900463ffffffff1611612b1d57604051635caf530f60e11b815260040160405180910390fd5b815160208301515f9160801b175f8181526007602052604090205490915060ff1615612b4b57505050612e13565b5f8181526007602090815260408220805460ff19166001908117909155845463ffffffff600160201b808304821684019091160267ffffffff0000000019909116178555855191860151612b9f92916141cc565b9050835f01517f9bc54857932b6f10bb750fdad91f736b82dd4de202ed5c2f9f076773bb5b3fb78560200151866040015184604051612be093929190615cdf565b60405180910390a2835160405163e83ba79d60e01b8152600197505f91829182916001600160a01b037f000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800169163e83ba79d91612c40918890600401615cfd565b60c060405180830381865afa158015612c5b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c7f9190615d8e565b80516020015190915015612ca3578051516001600160f81b03169290920191600191505b80602001516020015115612cca576020810151516001600160f81b03169290920191600191505b818015612ce457506040810151516001600160f81b031615155b15612d6057612cf16129e6565b8751604083810151519051632207e80f60e21b815260048101929092526001600160f81b031660248201526001600160a01b03919091169063881fa03c906044015f604051808303815f87803b158015612d49575f80fd5b505af1158015612d5b573d5f803e3d5ffd5b505050505b86604001516801bc16d674ec8000001115612d8a5786604001516801bc16d674ec80000003830192505b8215612dff57612d986129e6565b875160405163e5220e3f60e01b81526001600160a01b03929092169163e5220e3f91612dd1918790600401918252602082015260400190565b5f604051808303815f87803b158015612de8575f80fd5b505af1158015612dfa573d5f803e3d5ffd5b505050505b8651612e0b905f613d0a565b505050505050505b600101612a99565b50801561169a5761169a613f46565b612e326146d7565b612e3c86886146fd565b612e446129e6565b6040516358a46db560e11b815260048101889052602481018790526001600160a01b03919091169063b148db6a90604401602060405180830381865afa158015612e90573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612eb49190615a82565b341015612ed45760405163162908e360e11b815260040160405180910390fd5b3415612f3f57612ee26129e6565b6001600160a01b0316632e5990543489896040518463ffffffff1660e01b8152600401612f10929190615b75565b5f604051808303818588803b158015612f27575f80fd5b505af1158015612f39573d5f803e3d5ffd5b50505050505b6125e3868686868686614771565b5f7fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf0438478612f7881613c88565b612f806146d7565b6001600160a01b038516612faa5760405160016232750f60e21b0319815260040160405180910390fd5b600954600160c01b90046001600160401b03169150612fc8826149b0565b5f828152600660209081526040822091908190612fe790880188615a48565b6001600160a01b031614613007576130026020870187615a48565b613009565b865b90505f8061301d6040890160208a01615a48565b6001600160a01b0316146130405761303b6040880160208901615a48565b613042565b875b600184018054600160401b600160e01b031916600160401b6001600160a01b0386811691909102919091179091556003850180546001600160a01b031916918316919091179055905061309b6060880160408901615ddd565b156130b65760048301805460ff60a01b1916600160a01b1790555b6009805460016001600160401b03600160c01b80840482169290920116026001600160c01b039091161790556001600160a01b03808216908316867ff17baf73d46b0a80157c3ea3dda1bf081a702732d53ff1720f85e55d9f0997c061312260608c0160408d01615ddd565b604051901515815260200160405180910390a46001600160a01b0386161561317a576040516001600160a01b0387169086907f67334334c388385e5f244703f8a8b28b7f4ffe52909130aca69bc62a8e27f09a905f90a35b613182613f46565b505050509392505050565b6131956146d7565b61319f87896146fd565b5f6131a86129e6565b604051632884698160e01b8152600481018a9052602481018990526001600160a01b039190911690632884698190604401602060405180830381865afa1580156131f4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132189190615a82565b90508015613287576132286129e6565b6001600160a01b031663f7966efe8a8a84866040518563ffffffff1660e01b81526004016132599493929190615df8565b5f604051808303815f87803b158015613270575f80fd5b505af1158015613282573d5f803e3d5ffd5b505050505b613295888888888888614771565b505050505050505050565b5f8181526006602052604081206004810154600160a01b900460ff166132d35760038101546001600160a01b031661181a565b60010154600160401b90046001600160a01b031692915050565b5f6133035f80516020615f468339815191525490565b4210905090565b5f805f805f805f8061331b89613cd4565b5f898152600660205260408120906133316129e6565b6001600160a01b0316639c5161028c6040518263ffffffff1660e01b815260040161335e91815260200190565b602060405180830381865afa158015613379573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061339d9190615a82565b82549091505f906133bd9063ffffffff600160401b820481169116615c54565b63ffffffff16905080821180156133df57508254600160e01b900460ff166002145b1561341a57825460029b506134139063ffffffff600160c01b8204811691600160201b81048216908216031684900361459b565b9950613466565b8082111561344857825460029b5063ffffffff600160201b8204811691811691909103168290039950613466565b8254600160e01b810460ff169b50600160c01b900463ffffffff1699505b505060018101549054989a9799505f98899889985063ffffffff9283169750600160401b820483169650600160a01b9091049091169350915050565b5f611dcd5f80516020615f66833981519152546001600160401b031690565b5f80516020615f068339815191526134d881613c88565b5f6134e586868686614664565b90505f5b81811015612a63576008810287013560c01c6010820286013560801c61350e82613cd4565b5f8281526006602052604090208054600160601b900463ffffffff168210613549576040516388e1a28160e01b815260040160405180910390fd5b8054600160401b900463ffffffff16821015613578576040516388e1a28160e01b815260040160405180910390fd5b805463ffffffff60601b1916600160601b63ffffffff84160217815560405182815283907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a260405183907fe5725d045d5c47bd1483feba445e395dc8647486963e6d54aad9ed03ff7d6ce6905f90a26135fd835f613d0a565b5050508060010190506134e9565b6060805f80516020615f0683398151915261362581613c88565b61362e86614082565b9093509150851561396a57855f80805b7f000000000000000000000000000000000000000000000000000000000000000581118061366a575083155b6138e45761367781614001565b91506001015f6136a08380546001600160801b03165f9081526001909101602052604090205490565b90505b80156138de575f6136b48260c01c90565b6001600160401b039081165f8181526006602052604081208054929450608086901c90931692916136fc906136f690600160a01b900463ffffffff168561459b565b8a61459b565b90508089118061370b57508281145b1561374b5760018201805463ffffffff600160201b80830482168790039091160267ffffffff0000000019909116179055613745876149dc565b506137aa565b60018201805463ffffffff600160201b808304821685900382160267ffffffff00000000199092169190911790915561378a90869083860390614a3b16565b87546001600160801b03165f908152600189016020526040902081905594505b805f036137ba57505050506138bd565b81546137d8908590600160401b900463ffffffff16838f8f8d614128565b815463ffffffff600160401b8083048216840191821690810263ffffffff60401b19909316929092178455604051918252988201989085907f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b789060200160405180910390a2825463ffffffff600160a01b808304821685900391821690810263ffffffff60a01b199093169290921785556040519182529086907ff9109091b368cedad2edff45414eef892edd6b4fe80084bd590aa8f8def8ed339060200160405180910390a2828b039a508a5f036138b6575050505050506138de565b5050505050505b5081546001600160801b03165f9081526001830160205260409020546136a3565b5061363e565b89831461390457604051630bc9ea5560e21b815260040160405180910390fd5b600980546001600160401b03600160801b80830482168e9003821602808216828416178e0190911667ffffffffffffffff1990911677ffffffffffffffff0000000000000000ffffffffffffffff1990921691909117179055613965613f46565b505050505b50935093915050565b5f80516020615f66833981519152805460029190600160401b900460ff16806139a9575080546001600160401b03808416911610155b156139c75760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b1781556001600160a01b038316613a0f57604051633ef39b8160e01b815260040160405180910390fd5b613a17614a60565b613a215f84613f86565b50613ab85f80516020615f068339815191527f000000000000000000000000e2ef9536daaaebff5b1c130957ab3e80056b06d86001600160a01b031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613ab39190615e5f565b613f86565b50613ac35f19614a68565b805460ff60401b191681556040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050565b5f8181525f80516020615ee683398151915260208190526040822061181a90614ab7565b613b3c82611554565b613b4581613c88565b61169a8383613fc8565b565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d613b7b81613c88565b613b8482614a68565b5050565b613b906146d7565b613b9a87896146fd565b5f613ba36129e6565b6040516358a46db560e11b8152600481018a9052602481018990526001600160a01b03919091169063b148db6a90604401602060405180830381865afa158015613bef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c139190615a82565b9050801561328757613c236129e6565b6001600160a01b0316634c7ed3d28a8a84866040518563ffffffff1660e01b81526004016132599493929190615df8565b5f6001600160e01b03198216637965db0b60e01b14806113a857506301ffc9a760e01b6001600160e01b03198316146113a8565b6113e08133614ac0565b613c9a614af4565b425f80516020615f46833981519152556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f9905f90a1565b600954600160c01b90046001600160401b0316811015613cf15750565b604051633ed893db60e21b815260040160405180910390fd5b5f8281526006602052604081208054909163ffffffff600160401b8304811692613d3d918491600160601b900416615c54565b63ffffffff1690505f613d4e6129e6565b6001600160a01b03166301a5e9e3876040518263ffffffff1660e01b8152600401613d7b91815260200190565b602060405180830381865afa158015613d96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613dba9190615a82565b84549091505f90613dd290859063ffffffff16615c54565b63ffffffff169050808210613de9575f9250613e1e565b8454613e049063ffffffff600160601b820481169116615c54565b63ffffffff16821115613e1e57613e1b8282615b10565b92505b508354600160e01b900460ff1615801590613e3857505f82115b15613e8857835463ffffffff600160201b820481168503811691613e8491600160c01b909104168210613e6b575f613e7e565b8554600160c01b900463ffffffff168290035b8461459b565b9250505b8354600160a01b900463ffffffff16821461154c5783546009805467ffffffffffffffff60801b198116600160a01b9384900463ffffffff908116600160801b938490046001600160401b039081169190910388011690920217909155855463ffffffff60a01b191690841690910217845560405182815286907ff9109091b368cedad2edff45414eef892edd6b4fe80084bd590aa8f8def8ed339060200160405180910390a28415613f3d57613f3d613f46565b61154c86614b19565b60058054600101908190556040519081527f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9060200160405180910390a1565b5f5f80516020615ee683398151915281613fa08585614d47565b90508015611dea575f858152602083905260409020613fbf9085614def565b50949350505050565b5f5f80516020615ee683398151915281613fe28585614e03565b90508015611dea575f858152602083905260409020613fbf9085614e7c565b5f7f0000000000000000000000000000000000000000000000000000000000000004820361403157506001919050565b505f8181526020819052604090205b919050565b5f8381526006602052604090205463ffffffff166140638284615ae9565b11156116d357604051635caf530f60e11b815260040160405180910390fd5b606080614090603084615bf9565b6001600160401b038111156140a7576140a7615afc565b6040519080825280601f01601f1916602001820160405280156140d1576020820181803683370190505b506140dd606085615bf9565b6001600160401b038111156140f4576140f4615afc565b6040519080825280601f01601f19166020018201604052801561411e576020820181803683370190505b5091509150915091565b5f805b858110156129dc5761415688614141838a615ae9565b5f80516020615f868339815191529190614e90565b60018082015460801c85840160308181028a01908101929092528354602092830152600284015460609182028901928301526003840154604083015260048401549101529092500161412b565b613b4f7fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc613c88565b60605f6141da603084615bf9565b6001600160401b038111156141f1576141f1615afc565b6040519080825280601f01601f19166020018201604052801561421b576020820181803683370190505b5091505f5b8381101561425e57614236866141418388615ae9565b9150603081026020840101600183015460801c60108201528254815250600181019050614220565b50509392505050565b5f82815260066020526040902060010154600160401b90046001600160a01b0316806142a657604051633ed893db60e21b815260040160405180910390fd5b816001600160a01b0316816001600160a01b0316146116d35760405163743a3f7960e11b815260040160405180910390fd5b7f6e38e7eaa4307e6ee6c66720337876ca65012869fbef035f57219354c17284005f818152815c602052604090209081815d5090565b5f8215806143245750816143228486615ae9565b115b80614332575063ffffffff82115b156143505760405163575697ff60e01b815260040160405180910390fd5b604080516030808252606082019092525f91829182918291906020820181803683370190505090508787015b888111156144645761439f5f80516020615f868339815191528b5f198401614e90565b9450600185015460801c603083015284546020830152868110156143fe576143d85f80516020615f868339815191528b5f198a01614e90565b93505f92505b60058310156143fa5782840154838601556001830192506143de565b8394505b5f92505b600583101561441b575f83860155600183019250614402565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b98360405161445791906155e9565b60405180910390a261437c565b509498975050505050505050565b5f61181a8383614ec7565b61448683613cd4565b5f838152600660205260409020600181015463ffffffff168084036144ac575050505050565b8154600160401b900463ffffffff168411156144db5760405163cc11217f60e01b815260040160405180910390fd5b821580156144ee57508063ffffffff1684105b1561450c576040516371a4bd1560e01b815260040160405180910390fd5b6009805467ffffffffffffffff60401b19811663ffffffff848116600160401b938490046001600160401b03908116919091038901169092021790915560018301805463ffffffff191691861691909117905560405184815285907f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c2201669060200160405180910390a25050505050565b5f8183106145a9578161181a565b5090919050565b5f8381526006602052604090206001810180548391906004906145e1908490600160201b900463ffffffff16615e7a565b92506101000a81548163ffffffff021916908363ffffffff1602179055505f61460984614001565b905061461f818663ffffffff80871690614eed16565b5060405163ffffffff84168152859085907fdc891a44aee443f7f65d1abc5710a05ef241c0c5d7a62f12671522f3c14852bc9060200160405180910390a35050505050565b5f614670600885615eab565b61467b601084615eab565b141580614691575061468e600885615ebe565b15155b806146a557506146a2601083615ebe565b15155b156146c35760405163319c9a2160e21b815260040160405180910390fd5b6146ce600885615eab565b95945050505050565b6146df6132ed565b15613b4f57604051630286f07360e31b815260040160405180910390fd5b336001600160a01b0382160361471757613b848233614267565b6147407fc72a21b38830f4d6418a239e17db78b945cc7cfee674bac97fd596eaf0438478613c88565b3361474a83614f5d565b6001600160a01b031614613b84576040516310b922ef60e21b815260040160405180910390fd5b61477a86614f93565b5f8681526006602052604081208054909163ffffffff9091169061479c6129e6565b6001600160a01b0316630569b9478a6040518263ffffffff1660e01b81526004016147c991815260200190565b602060405180830381865afa1580156147e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906148089190615a82565b60405163014dddeb60e51b8152600481018290529091505f906001600160a01b037f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad16906329bbbd6090602401602060405180830381865afa158015614870573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906148949190615a82565b8454909150600160201b900463ffffffff16898401038110156148ca57604051630911e76760e41b815260040160405180910390fd5b5f6148da8b858c8c8c8c8c614fbf565b8554909150600160601b900463ffffffff16840361495557845463ffffffff600160601b80830482168d0191821690810263ffffffff60601b19909316929092178755604051918252908c907f947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfd9060200160405180910390a2505b845463ffffffff191663ffffffff82161785556040518181528b907fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09060200160405180910390a2506149a88a5f613d0a565b611d81613f46565b7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a500613b84818333615140565b80546001600160801b03165f90815260018201602052604090205480614a15576040516363c3654960e01b815260040160405180910390fd5b81546fffffffffffffffffffffffffffffffff19166001600160801b0382161790915590565b60801b67ffffffffffffffff60801b1667ffffffffffffffff60801b19919091161790565b613b4f615156565b614a706146d7565b805f03614a905760405163ad58bfc760e01b815260040160405180910390fd5b5f5f198203614aa157505f19614aae565b614aab8242615ae9565b90505b613b848161518c565b5f6113a8825490565b614aca828261273c565b613b8457808260405163e2517d3f60e01b8152600401614aeb929190615b75565b60405180910390fd5b614afc6132ed565b613b4f5760405163b047186b60e01b815260040160405180910390fd5b5f614b226129e6565b6001600160a01b0316630569b947836040518263ffffffff1660e01b8152600401614b4f91815260200190565b602060405180830381865afa158015614b6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614b8e9190615a82565b90505f807f000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad6001600160a01b031663decfec56846040518263ffffffff1660e01b8152600401614be091815260200190565b6040805180830381865afa158015614bfa573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190614c1e9190615c23565b5f868152600660205260409020805460018201549395509193509163ffffffff600160a01b909204821691600160201b90910416808211614c625750505050505050565b8082037f000000000000000000000000000000000000000000000000000000000000000563ffffffff87161015614d1057835463ffffffff600160401b90910481168301908181169087161115614d0e578086035f614cca63ffffffff80861690841661459b565b9050614cdd8b8a63ffffffff16836145b0565b60048701549381900393600160a81b900460ff16614d0b5760048701805460ff60a81b1916600160a81b1790555b50505b505b63ffffffff8116156129dc576129dc887f0000000000000000000000000000000000000000000000000000000000000005836145b0565b5f5f80516020615f26833981519152614d60848461273c565b614ddf575f848152602082815260408083206001600160a01b03871684529091529020805460ff19166001179055614d953390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a460019150506113a8565b5f9150506113a8565b5092915050565b5f61181a836001600160a01b03841661521b565b5f5f80516020615f26833981519152614e1c848461273c565b15614ddf575f848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a460019150506113a8565b5f61181a836001600160a01b038416615267565b6040805160208082019590955280820193909352606080840192909252805180840390920182526080909201909152805191012090565b5f825f018281548110614edc57614edc615a99565b905f5260205f200154905092915050565b8254600160801b908190046001600160801b039081165f8181526001808801602052604090912060809590951b67ffffffffffffffff60801b1660c09690961b6001600160c01b031916959095179085011792839055845480821690839004821690940116029190911790915590565b5f7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a50061181a81845f918252602052604090205c90565b7f1b07bc0838fdc4254cbabb5dd0c94d936f872c6758547168d513d8ad1dc3a500613b8481835f615140565b5f851580614fd9575063ffffffff614fd78789615ae9565b115b15614ff75760405163575697ff60e01b815260040160405180910390fd5b603086028414158061500c5750606086028214155b1561502a5760405163251f56a160e21b815260040160405180910390fd5b604080516030808252606082019092525f91829182916020820181803683370190505090505f5b89811015615130576150715f80516020615f868339815191528d8d614e90565b60308281028b01601081013591850182905235602085018190529195501715925082156150b157604051630f35a7eb60e21b815260040160405180910390fd5b60208201518455603082015160801b60018501556060810287018035600286015560208101356003860155604081013560048601555060018101905060018b019a508b7fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a898360405161512391906155e9565b60405180910390a2615051565b50989a9950505050505050505050565b5f83815260208390526040902081815d50505050565b5f80516020615f6683398151915254600160401b900460ff16613b4f57604051631afcd79f60e31b815260040160405180910390fd5b6151a25f80516020615f46833981519152829055565b5f1981036151e2576040515f1981527f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e906020015b60405180910390a150565b7f32fb7c9891bc4f963c7de9f1186d2a7755c7d6e9f4604dabe1d8bb3027c2f49e61520d4283615b10565b6040519081526020016151d7565b5f81815260018301602052604081205461526057508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556113a8565b505f6113a8565b5f8181526001830160205260408120548015614ddf575f615289600183615b10565b85549091505f9061529c90600190615b10565b90508082146152fb575f865f0182815481106152ba576152ba615a99565b905f5260205f200154905080875f0184815481106152da576152da615a99565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061530c5761530c615ed1565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506113a8565b5f60208284031215615351575f80fd5b81356001600160e01b03198116811461181a575f80fd5b5f805f6060848603121561537a575f80fd5b505081359360208301359350604090920135919050565b6001600160a01b03811681146113e0575f80fd5b5f80604083850312156153b6575f80fd5b8235915060208301356153c881615391565b809150509250929050565b5f602082840312156153e3575f80fd5b5035919050565b5f80602083850312156153fb575f80fd5b82356001600160401b0380821115615411575f80fd5b818501915085601f830112615424575f80fd5b813581811115615432575f80fd5b8660208260051b8501011115615446575f80fd5b60209290920196919550909350505050565b5f8060408385031215615469575f80fd5b8235915060208301356001600160801b03811681146153c8575f80fd5b5f8060408385031215615497575f80fd5b50508035926020909101359150565b602080825282518282018190525f9190848201906040850190845b818110156154dd578351835292840192918401916001016154c1565b50909695505050505050565b5f81518084525f5b8181101561550d576020818501810151868301820152016154f1565b505f602082860101526020601f19601f83011685010191505092915050565b604081525f61553e60408301856154e9565b82810360208401526146ce81856154e9565b5f8083601f840112615560575f80fd5b5081356001600160401b03811115615576575f80fd5b60208301915083602082850101111561558d575f80fd5b9250929050565b5f805f805f608086880312156155a8575f80fd5b853594506020860135935060408601356001600160401b038111156155cb575f80fd5b6155d788828901615550565b96999598509660600135949350505050565b602081525f61181a60208301846154e9565b5f806040838503121561560c575f80fd5b823561561781615391565b946020939093013593505050565b815163ffffffff1681526102008101602083015161564b602084018263ffffffff169052565b506040830151615663604084018263ffffffff169052565b50606083015161567b606084018263ffffffff169052565b506080830151615693608084018263ffffffff169052565b5060a08301516156ab60a084018263ffffffff169052565b5060c08301516156c360c084018263ffffffff169052565b5060e08301516156d860e084018260ff169052565b506101008381015163ffffffff908116918401919091526101208085015190911690830152610140808401516001600160a01b039081169184019190915261016080850151821690840152610180808501518216908401526101a080850151909116908301526101c0808401511515908301526101e0928301511515929091019190915290565b5f805f805f60808688031215615773575f80fd5b8535945060208601356001600160401b0381111561578f575f80fd5b61579b88828901615550565b9699909850959660408101359660609091013595509350505050565b5f805f80604085870312156157ca575f80fd5b84356001600160401b03808211156157e0575f80fd5b6157ec88838901615550565b90965094506020870135915080821115615804575f80fd5b5061581187828801615550565b95989497509550505050565b5f806020838503121561582e575f80fd5b82356001600160401b0380821115615844575f80fd5b818501915085601f830112615857575f80fd5b813581811115615865575f80fd5b866020606083028501011115615446575f80fd5b5f805f805f805f60a0888a03121561588f575f80fd5b873561589a81615391565b9650602088013595506040880135945060608801356001600160401b03808211156158c3575f80fd5b6158cf8b838c01615550565b909650945060808a01359150808211156158e7575f80fd5b506158f48a828b01615550565b989b979a50959850939692959293505050565b5f805f83850360a081121561591a575f80fd5b843561592581615391565b93506060601f1982011215615938575f80fd5b50602084019150608084013561594d81615391565b809150509250925092565b5f805f805f805f80888a03610140811215615971575f80fd5b893561597c81615391565b985060208a0135975060408a0135965060608a01356001600160401b03808211156159a5575f80fd5b6159b18d838e01615550565b909850965060808c01359150808211156159c9575f80fd5b506159d68c828d01615550565b90955093505060a0609f19820112156159ed575f80fd5b5060a0890190509295985092959890939650565b5f805f60408486031215615a13575f80fd5b8335925060208401356001600160401b03811115615a2f575f80fd5b615a3b86828701615550565b9497909650939450505050565b5f60208284031215615a58575f80fd5b813561181a81615391565b92835260208301919091526001600160a01b0316604082015260600190565b5f60208284031215615a92575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b80151581146113e0575f80fd5b5f60208284031215615aca575f80fd5b815161181a81615aad565b634e487b7160e01b5f52601160045260245ffd5b808201808211156113a8576113a8615ad5565b634e487b7160e01b5f52604160045260245ffd5b818103818111156113a8576113a8615ad5565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152606060208201525f615b64606083018587615b23565b905082604083015295945050505050565b6001600160a01b03929092168252602082015260400190565b858152608060208201525f615ba7608083018688615b23565b604083019490945250606001529392505050565b5f805f8060808587031215615bce575f80fd5b8451935060208501519250604085015191506060850151615bee81615aad565b939692955090935050565b80820281158282048414176113a8576113a8615ad5565b805163ffffffff81168114614040575f80fd5b5f8060408385031215615c34575f80fd5b615c3d83615c10565b9150615c4b60208401615c10565b90509250929050565b63ffffffff828116828216039080821115614de857614de8615ad5565b604051606081016001600160401b0381118282101715615c9f57634e487b7160e01b5f52604160045260245ffd5b60405290565b5f60608284031215615cb5575f80fd5b615cbd615c71565b8235815260208301356020820152604083013560408201528091505092915050565b838152826020820152606060408201525f6146ce60608301846154e9565b828152604060208201525f611dea60408301846154e9565b5f60408284031215615d25575f80fd5b604051604081018181106001600160401b0382111715615d5357634e487b7160e01b5f52604160045260245ffd5b604052825190915081906001600160f81b0381168114615d71575f80fd5b81526020830151615d8181615aad565b6020919091015292915050565b5f60c08284031215615d9e575f80fd5b615da6615c71565b615db08484615d15565b8152615dbf8460408501615d15565b6020820152615dd18460808501615d15565b60408201529392505050565b5f60208284031215615ded575f80fd5b813561181a81615aad565b6001600160a01b0385168152602080820185905260408083018590528335606084015290830135608083015261010082019083013560ff8116808214615e3c575f80fd5b60a084015250606083013560c083015260809092013560e0909101529392505050565b5f60208284031215615e6f575f80fd5b815161181a81615391565b63ffffffff818116838216019080821115614de857614de8615ad5565b634e487b7160e01b5f52601260045260245ffd5b5f82615eb957615eb9615e97565b500490565b5f82615ecc57615ecc615e97565b500690565b634e487b7160e01b5f52603160045260245ffdfec1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c602dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800e8b012900cb200ee5dfc3b895a32791b67d12891b09f117814f167a237783a02f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00059e9c54cf92ba46cc39c6b4acd51d5116e9d49fabee6193530ea918b54be94aa164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000000000000000000000000000e2ef9536daaaebff5b1c130957ab3e80056b06d8000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad000000000000000000000000a54b90ba34c5f326bc1485054080994e38fb4c60000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800
-----Decoded View---------------
Arg [0] : moduleType (bytes32): 0x636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000
Arg [1] : lidoLocator (address): 0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8
Arg [2] : parametersRegistry (address): 0xA4aD5236963f9Fe4229864712269D8d79B65C5Ad
Arg [3] : _accounting (address): 0xA54b90BA34C5f326BC1485054080994e38FB4C60
Arg [4] : exitPenalties (address): 0xD259b31083Be841E5C85b2D481Cfc17C14276800
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 636f6d6d756e6974792d6f6e636861696e2d7631000000000000000000000000
Arg [1] : 000000000000000000000000e2ef9536daaaebff5b1c130957ab3e80056b06d8
Arg [2] : 000000000000000000000000a4ad5236963f9fe4229864712269d8d79b65c5ad
Arg [3] : 000000000000000000000000a54b90ba34c5f326bc1485054080994e38fb4c60
Arg [4] : 000000000000000000000000d259b31083be841e5c85b2d481cfc17c14276800
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.