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
|
||
|---|---|---|---|---|---|---|---|
| On Exited And St... | 1498589 | 77 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498589 | 77 days ago | 0 ETH | ||||
| On Exited And St... | 1498589 | 77 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498589 | 77 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498581 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498581 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498581 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498581 | 78 days ago | 0 ETH | ||||
| On Exited And St... | 1498245 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498245 | 78 days ago | 0 ETH | ||||
| On Exited And St... | 1498245 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498245 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498240 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498240 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498240 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1498240 | 78 days ago | 0 ETH | ||||
| On Exited And St... | 1497902 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497902 | 78 days ago | 0 ETH | ||||
| On Exited And St... | 1497902 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497902 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497895 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497895 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497895 | 78 days ago | 0 ETH | ||||
| Get Staking Modu... | 1497895 | 78 days ago | 0 ETH | ||||
| On Exited And St... | 1497568 | 78 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:
NodeOperatorsRegistry
Compiler Version
v0.4.24+commit.e67f0147
Optimization Enabled:
Yes with 200 runs
Other Settings:
constantinople EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.4.24; import {AragonApp} from "@aragon/os/contracts/apps/AragonApp.sol"; import {SafeMath} from "@aragon/os/contracts/lib/math/SafeMath.sol"; import {UnstructuredStorage} from "@aragon/os/contracts/common/UnstructuredStorage.sol"; import {Math256} from "../../common/lib/Math256.sol"; import {MinFirstAllocationStrategy} from "../../common/lib/MinFirstAllocationStrategy.sol"; import {ILidoLocator} from "../../common/interfaces/ILidoLocator.sol"; import {SigningKeys} from "../lib/SigningKeys.sol"; import {Packed64x4} from "../lib/Packed64x4.sol"; import {Versioned} from "../utils/Versioned.sol"; interface IStETH { function sharesOf(address _account) external view returns (uint256); function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256); function approve(address _spender, uint256 _amount) external returns (bool); } /// @title Node Operator registry /// @notice Node Operator registry manages signing keys and other node operator data. /// @dev Must implement the full version of IStakingModule interface, not only the one declared locally. /// It's also responsible for distributing rewards to node operators. /// NOTE: the code below assumes moderate amount of node operators, i.e. up to `MAX_NODE_OPERATORS_COUNT`. contract NodeOperatorsRegistry is AragonApp, Versioned { using SafeMath for uint256; using UnstructuredStorage for bytes32; using SigningKeys for bytes32; using Packed64x4 for Packed64x4.Packed; // // EVENTS // event NodeOperatorAdded(uint256 nodeOperatorId, string name, address rewardAddress, uint64 stakingLimit); event NodeOperatorActiveSet(uint256 indexed nodeOperatorId, bool active); event NodeOperatorNameSet(uint256 indexed nodeOperatorId, string name); event NodeOperatorRewardAddressSet(uint256 indexed nodeOperatorId, address rewardAddress); event NodeOperatorTotalKeysTrimmed(uint256 indexed nodeOperatorId, uint64 totalKeysTrimmed); event KeysOpIndexSet(uint256 keysOpIndex); event StakingModuleTypeSet(bytes32 moduleType); event RewardsDistributed(address indexed rewardAddress, uint256 sharesAmount); event RewardDistributionStateChanged(RewardDistributionState state); event LocatorContractSet(address locatorAddress); event VettedSigningKeysCountChanged(uint256 indexed nodeOperatorId, uint256 approvedValidatorsCount); event DepositedSigningKeysCountChanged(uint256 indexed nodeOperatorId, uint256 depositedValidatorsCount); event ExitedSigningKeysCountChanged(uint256 indexed nodeOperatorId, uint256 exitedValidatorsCount); event TotalSigningKeysCountChanged(uint256 indexed nodeOperatorId, uint256 totalValidatorsCount); event NonceChanged(uint256 nonce); event TargetValidatorsCountChanged(uint256 indexed nodeOperatorId, uint256 targetValidatorsCount, uint256 targetLimitMode); event ValidatorExitStatusUpdated( uint256 indexed nodeOperatorId, bytes publicKey, uint256 eligibleToExitInSec, uint256 proofSlotTimestamp ); event ValidatorExitTriggered( uint256 indexed nodeOperatorId, bytes publicKey, uint256 withdrawalRequestPaidFee, uint256 exitType ); event ExitDeadlineThresholdChanged(uint256 threshold, uint256 reportingWindow); // Enum to represent the state of the reward distribution process enum RewardDistributionState { TransferredToModule, // New reward portion minted and transferred to the module ReadyForDistribution, // Operators' statistics updated, reward ready for distribution Distributed // Reward distributed among operators } // // ACL // // bytes32 public constant MANAGE_SIGNING_KEYS = keccak256("MANAGE_SIGNING_KEYS"); bytes32 public constant MANAGE_SIGNING_KEYS = 0x75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee; // bytes32 public constant SET_NODE_OPERATOR_LIMIT_ROLE = keccak256("SET_NODE_OPERATOR_LIMIT_ROLE"); bytes32 public constant SET_NODE_OPERATOR_LIMIT_ROLE = 0x07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754; // bytes32 public constant MANAGE_NODE_OPERATOR_ROLE = keccak256("MANAGE_NODE_OPERATOR_ROLE"); bytes32 public constant MANAGE_NODE_OPERATOR_ROLE = 0x78523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f8; // bytes32 public constant STAKING_ROUTER_ROLE = keccak256("STAKING_ROUTER_ROLE"); bytes32 public constant STAKING_ROUTER_ROLE = 0xbb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6; // // CONSTANTS // uint256 public constant MAX_NODE_OPERATORS_COUNT = 200; uint256 public constant MAX_NODE_OPERATOR_NAME_LENGTH = 255; uint256 public constant MAX_STUCK_PENALTY_DELAY = 365 days; uint256 internal constant UINT64_MAX = 0xFFFFFFFFFFFFFFFF; // SigningKeysStats /// @dev Operator's max validator keys count approved for deposit by the DAO uint8 internal constant TOTAL_VETTED_KEYS_COUNT_OFFSET = 0; /// @dev Number of keys in the EXITED state of this operator for all time uint8 internal constant TOTAL_EXITED_KEYS_COUNT_OFFSET = 1; /// @dev Total number of keys of this operator for all time uint8 internal constant TOTAL_KEYS_COUNT_OFFSET = 2; /// @dev Number of keys of this operator which were in DEPOSITED state for all time uint8 internal constant TOTAL_DEPOSITED_KEYS_COUNT_OFFSET = 3; // TargetValidatorsStats /// @dev Target limit mode, allows limiting target active validators count for operator (0 = disabled, 1 = soft mode, 2 = boosted mode) uint8 internal constant TARGET_LIMIT_MODE_OFFSET = 0; /// @dev relative target active validators limit for operator, set by DAO /// @notice used to check how many keys should go to exit, 0 - means all deposited keys would be exited uint8 internal constant TARGET_VALIDATORS_COUNT_OFFSET = 1; /// @dev actual operators's number of keys which could be deposited uint8 internal constant MAX_VALIDATORS_COUNT_OFFSET = 2; // Summary SigningKeysStats uint8 internal constant SUMMARY_MAX_VALIDATORS_COUNT_OFFSET = 0; /// @dev Number of keys of all operators which were in the EXITED state for all time uint8 internal constant SUMMARY_EXITED_KEYS_COUNT_OFFSET = 1; /// @dev [deprecated] Total number of keys of all operators for all time uint8 internal constant SUMMARY_TOTAL_KEYS_COUNT_OFFSET = 2; /// @dev Number of keys of all operators which were in the DEPOSITED state for all time uint8 internal constant SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET = 3; // // UNSTRUCTURED STORAGE POSITIONS // // bytes32 internal constant SIGNING_KEYS_MAPPING_NAME = keccak256("lido.NodeOperatorsRegistry.signingKeysMappingName"); bytes32 internal constant SIGNING_KEYS_MAPPING_NAME = 0xeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9; // bytes32 internal constant LIDO_LOCATOR_POSITION = keccak256("lido.NodeOperatorsRegistry.lidoLocator"); bytes32 internal constant LIDO_LOCATOR_POSITION = 0xfb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537; /// @dev Total number of operators // bytes32 internal constant TOTAL_OPERATORS_COUNT_POSITION = keccak256("lido.NodeOperatorsRegistry.totalOperatorsCount"); bytes32 internal constant TOTAL_OPERATORS_COUNT_POSITION = 0xe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8; /// @dev Cached number of active operators // bytes32 internal constant ACTIVE_OPERATORS_COUNT_POSITION = keccak256("lido.NodeOperatorsRegistry.activeOperatorsCount"); bytes32 internal constant ACTIVE_OPERATORS_COUNT_POSITION = 0x6f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1; /// @dev link to the index of operations with keys // bytes32 internal constant KEYS_OP_INDEX_POSITION = keccak256("lido.NodeOperatorsRegistry.keysOpIndex"); bytes32 internal constant KEYS_OP_INDEX_POSITION = 0xcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e; /// @dev module type // bytes32 internal constant TYPE_POSITION = keccak256("lido.NodeOperatorsRegistry.type"); bytes32 internal constant TYPE_POSITION = 0xbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0; // bytes32 internal constant REWARD_DISTRIBUTION_STATE = keccak256("lido.NodeOperatorsRegistry.rewardDistributionState"); bytes32 internal constant REWARD_DISTRIBUTION_STATE = 0x4ddbb0dcdc5f7692e494c15a7fca1f9eb65f31da0b5ce1c3381f6a1a1fd579b6; // bytes32 internal constant EXIT_DELAY_STATS = keccak256("lido.NodeOperatorsRegistry.exitDelayStats"); bytes32 internal constant EXIT_DELAY_STATS = 0x9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd8; /// @dev Exit delay stats offsets in Packed64x4: /// @dev The delay threshold in seconds after which a validator exit is considered late uint8 internal constant EXIT_DELAY_THRESHOLD_OFFSET = 0; /// @dev Timestamp before which validators reported as late will not result in penalties for their Node Operators. uint8 internal constant EXIT_PENALTY_CUTOFF_TIMESTAMP_OFFSET = 1; // // DATA TYPES // /// @dev Node Operator parameters and internal state struct NodeOperator { /// @dev Flag indicating if the operator can participate in further staking and reward distribution bool active; /// @dev Ethereum address on Execution Layer which receives stETH rewards for this operator address rewardAddress; /// @dev Human-readable name string name; /// @dev The below variables store the signing keys info of the node operator. /// signingKeysStats - contains packed variables: uint64 exitedSigningKeysCount, uint64 depositedSigningKeysCount, /// uint64 vettedSigningKeysCount, uint64 totalSigningKeysCount /// /// These variables can take values in the following ranges: /// /// 0 <= exitedSigningKeysCount <= depositedSigningKeysCount /// exitedSigningKeysCount <= depositedSigningKeysCount <= vettedSigningKeysCount /// depositedSigningKeysCount <= vettedSigningKeysCount <= totalSigningKeysCount /// depositedSigningKeysCount <= totalSigningKeysCount <= UINT64_MAX /// /// Additionally, the exitedSigningKeysCount and depositedSigningKeysCount values are monotonically increasing: /// : : : : : /// [....exitedSigningKeysCount....]-------->: : : /// [....depositedSigningKeysCount :.........]-------->: : /// [....vettedSigningKeysCount....:.........:<--------]-------->: /// [....totalSigningKeysCount.....:.........:<--------:---------]-------> /// : : : : : Packed64x4.Packed signingKeysStats; Packed64x4.Packed stuckPenaltyStats; Packed64x4.Packed targetValidatorsStats; } struct NodeOperatorSummary { Packed64x4.Packed summarySigningKeysStats; } // // STORAGE VARIABLES // /// @dev Mapping of all node operators. Mapping is used to be able to extend the struct. mapping(uint256 => NodeOperator) internal _nodeOperators; NodeOperatorSummary internal _nodeOperatorSummary; /// @dev Mapping of Node Operator exit delay keys mapping(bytes32 => bool) internal _validatorProcessedLateKeys; // // METHODS // function initialize( address _locator, bytes32 _type, uint256 _exitDeadlineThresholdInSeconds ) public onlyInit { // Initializations for v1 --> v2 _initialize_v2(_locator, _type); // Initializations for v2 --> v3 _initialize_v3(); // Initializations for v3 --> v4 _initialize_v4(_exitDeadlineThresholdInSeconds); initialized(); } function _initialize_v2(address _locator, bytes32 _type) internal { _onlyNonZeroAddress(_locator); LIDO_LOCATOR_POSITION.setStorageAddress(_locator); TYPE_POSITION.setStorageBytes32(_type); _setContractVersion(2); emit LocatorContractSet(_locator); emit StakingModuleTypeSet(_type); } function _initialize_v3() internal { _setContractVersion(3); _updateRewardDistributionState(RewardDistributionState.Distributed); } function _initialize_v4(uint256 _exitDeadlineThresholdInSeconds) internal { _setContractVersion(4); /// @dev The reportingWindowThreshold is set to 0 because it is not required during cold start. /// This parameter is only relevant when changing _exitDeadlineThresholdInSeconds in future upgrades. _setExitDeadlineThreshold(_exitDeadlineThresholdInSeconds, 0); } /// @notice A function to finalize upgrade to v2 (from v1). Removed and no longer used. /// For more details see https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-10.md /// See historical usage in commit: https://github.com/lidofinance/core/blob/c19480aa3366b26aa6eac17f85a6efae8b9f4f72/contracts/0.4.24/nos/NodeOperatorsRegistry.sol#L230 /// function finalizeUpgrade_v2(address _locator, bytes32 _type, uint256 _stuckPenaltyDelay) external /// @notice A function to finalize upgrade to v3 (from v2). Removed and no longer used. /// See historical usage in commit: https://github.com/lidofinance/core/blob/c19480aa3366b26aa6eac17f85a6efae8b9f4f72/contracts/0.4.24/nos/NodeOperatorsRegistry.sol#L298 /// function finalizeUpgrade_v3() external /// @notice Finalizes upgrade to version 4 by initializing new exit-related parameters. /// @param _exitDeadlineThresholdInSeconds Exit deadline threshold in seconds for validator exits. function finalizeUpgrade_v4(uint256 _exitDeadlineThresholdInSeconds) external { require(hasInitialized(), "CONTRACT_NOT_INITIALIZED"); _checkContractVersion(3); // Set allowance to 0 since the stuck keys logic has been removed and burning shares is no longer needed IStETH(getLocator().lido()).approve(getLocator().burner(), 0); _initialize_v4(_exitDeadlineThresholdInSeconds); } /// @notice Overrides default AragonApp behaviour to disallow recovery function transferToVault(address /* _token */) external { revert("NOT_SUPPORTED"); } /// @notice Add node operator named `name` with reward address `rewardAddress` and staking limit = 0 validators /// @param _name Human-readable name /// @param _rewardAddress Ethereum 1 address which receives stETH rewards for this operator /// @return id a unique key of the added operator function addNodeOperator(string _name, address _rewardAddress) external returns (uint256 id) { _onlyValidNodeOperatorName(_name); _onlyValidRewardAddress(_rewardAddress); _auth(MANAGE_NODE_OPERATOR_ROLE); id = getNodeOperatorsCount(); require(id < MAX_NODE_OPERATORS_COUNT, "MAX_OPERATORS_COUNT_EXCEEDED"); TOTAL_OPERATORS_COUNT_POSITION.setStorageUint256(id + 1); NodeOperator storage operator = _nodeOperators[id]; uint256 activeOperatorsCount = getActiveNodeOperatorsCount(); ACTIVE_OPERATORS_COUNT_POSITION.setStorageUint256(activeOperatorsCount + 1); operator.active = true; operator.name = _name; operator.rewardAddress = _rewardAddress; emit NodeOperatorAdded(id, _name, _rewardAddress, 0); } /// @notice Activates deactivated node operator with given id /// @param _nodeOperatorId Node operator id to activate function activateNodeOperator(uint256 _nodeOperatorId) external { _onlyExistedNodeOperator(_nodeOperatorId); _auth(MANAGE_NODE_OPERATOR_ROLE); _onlyCorrectNodeOperatorState(!getNodeOperatorIsActive(_nodeOperatorId)); ACTIVE_OPERATORS_COUNT_POSITION.setStorageUint256(getActiveNodeOperatorsCount() + 1); _nodeOperators[_nodeOperatorId].active = true; emit NodeOperatorActiveSet(_nodeOperatorId, true); _increaseValidatorsKeysNonce(); } /// @notice Deactivates active node operator with given id /// @param _nodeOperatorId Node operator id to deactivate function deactivateNodeOperator(uint256 _nodeOperatorId) external { _onlyExistedNodeOperator(_nodeOperatorId); _auth(MANAGE_NODE_OPERATOR_ROLE); _onlyCorrectNodeOperatorState(getNodeOperatorIsActive(_nodeOperatorId)); uint256 activeOperatorsCount = getActiveNodeOperatorsCount(); ACTIVE_OPERATORS_COUNT_POSITION.setStorageUint256(activeOperatorsCount.sub(1)); _nodeOperators[_nodeOperatorId].active = false; emit NodeOperatorActiveSet(_nodeOperatorId, false); Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 vettedSigningKeysCount = signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET); uint256 depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); // reset vetted keys count to the deposited validators count if (vettedSigningKeysCount > depositedSigningKeysCount) { signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, depositedSigningKeysCount); _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); emit VettedSigningKeysCountChanged(_nodeOperatorId, depositedSigningKeysCount); _updateSummaryMaxValidatorsCount(_nodeOperatorId); } _increaseValidatorsKeysNonce(); } /// @notice Change human-readable name of the node operator with given id /// @param _nodeOperatorId Node operator id to set name for /// @param _name New human-readable name of the node operator function setNodeOperatorName(uint256 _nodeOperatorId, string _name) external { _onlyValidNodeOperatorName(_name); _onlyExistedNodeOperator(_nodeOperatorId); _auth(MANAGE_NODE_OPERATOR_ROLE); _requireNotSameValue(keccak256(bytes(_nodeOperators[_nodeOperatorId].name)) != keccak256(bytes(_name))); _nodeOperators[_nodeOperatorId].name = _name; emit NodeOperatorNameSet(_nodeOperatorId, _name); } /// @notice Change reward address of the node operator with given id /// @param _nodeOperatorId Node operator id to set reward address for /// @param _rewardAddress Execution layer Ethereum address to set as reward address function setNodeOperatorRewardAddress(uint256 _nodeOperatorId, address _rewardAddress) external { _onlyValidRewardAddress(_rewardAddress); _onlyExistedNodeOperator(_nodeOperatorId); _auth(MANAGE_NODE_OPERATOR_ROLE); _requireNotSameValue(_nodeOperators[_nodeOperatorId].rewardAddress != _rewardAddress); _nodeOperators[_nodeOperatorId].rewardAddress = _rewardAddress; emit NodeOperatorRewardAddressSet(_nodeOperatorId, _rewardAddress); } /// @notice Set the maximum number of validators to stake for the node operator with given id /// @dev Current implementation preserves invariant: depositedSigningKeysCount <= vettedSigningKeysCount <= totalSigningKeysCount. /// If _vettedSigningKeysCount out of range [depositedSigningKeysCount, totalSigningKeysCount], the new vettedSigningKeysCount /// value will be set to the nearest range border. /// @param _nodeOperatorId Node operator id to set staking limit for /// @param _vettedSigningKeysCount New staking limit of the node operator function setNodeOperatorStakingLimit(uint256 _nodeOperatorId, uint64 _vettedSigningKeysCount) external { _onlyExistedNodeOperator(_nodeOperatorId); _authP(SET_NODE_OPERATOR_LIMIT_ROLE, arr(uint256(_nodeOperatorId), uint256(_vettedSigningKeysCount))); _onlyCorrectNodeOperatorState(getNodeOperatorIsActive(_nodeOperatorId)); _updateVettedSigningKeysCount(_nodeOperatorId, _vettedSigningKeysCount, true /* _allowIncrease */); _increaseValidatorsKeysNonce(); } /// @notice Called by StakingRouter to decrease the number of vetted keys for node operator with given id /// @param _nodeOperatorIds bytes packed array of the node operators id /// @param _vettedSigningKeysCounts bytes packed array of the new number of vetted keys for the node operators function decreaseVettedSigningKeysCount( bytes _nodeOperatorIds, bytes _vettedSigningKeysCounts ) external { _auth(STAKING_ROUTER_ROLE); uint256 nodeOperatorsCount = _checkReportPayload(_nodeOperatorIds.length, _vettedSigningKeysCounts.length); uint256 totalNodeOperatorsCount = getNodeOperatorsCount(); uint256 nodeOperatorId; uint256 vettedKeysCount; uint256 _nodeOperatorIdsOffset; uint256 _vettedKeysCountsOffset; /// @dev calldata layout: /// | func sig (4 bytes) | ABI-enc data | /// /// ABI-enc data: /// /// | 32 bytes | 32 bytes | 32 bytes | ... | 32 bytes | ...... | /// | ids len offset | counts len offset | ids len | ids | counts len | counts | assembly { _nodeOperatorIdsOffset := add(calldataload(4), 36) // arg1 calldata offset + 4 (signature len) + 32 (length slot) _vettedKeysCountsOffset := add(calldataload(36), 36) // arg2 calldata offset + 4 (signature len) + 32 (length slot)) } for (uint256 i; i < nodeOperatorsCount;) { /// @solidity memory-safe-assembly assembly { nodeOperatorId := shr(192, calldataload(add(_nodeOperatorIdsOffset, mul(i, 8)))) vettedKeysCount := shr(128, calldataload(add(_vettedKeysCountsOffset, mul(i, 16)))) i := add(i, 1) } _requireValidRange(nodeOperatorId < totalNodeOperatorsCount); _updateVettedSigningKeysCount(nodeOperatorId, vettedKeysCount, false /* only decrease */); } _increaseValidatorsKeysNonce(); } function _updateVettedSigningKeysCount( uint256 _nodeOperatorId, uint256 _vettedSigningKeysCount, bool _allowIncrease ) internal { Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 vettedSigningKeysCountBefore = signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET); uint256 depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); uint256 totalSigningKeysCount = signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); uint256 vettedSigningKeysCountAfter = Math256.min( totalSigningKeysCount, Math256.max(_vettedSigningKeysCount, depositedSigningKeysCount) ); if (vettedSigningKeysCountAfter == vettedSigningKeysCountBefore) return; require( _allowIncrease || vettedSigningKeysCountAfter < vettedSigningKeysCountBefore, "VETTED_KEYS_COUNT_INCREASED" ); signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, vettedSigningKeysCountAfter); _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); emit VettedSigningKeysCountChanged(_nodeOperatorId, vettedSigningKeysCountAfter); _updateSummaryMaxValidatorsCount(_nodeOperatorId); } /// @notice Called by StakingRouter to signal that stETH rewards were minted for this module. function onRewardsMinted(uint256 /* _totalShares */) external { _auth(STAKING_ROUTER_ROLE); _updateRewardDistributionState(RewardDistributionState.TransferredToModule); } function _checkReportPayload(uint256 idsLength, uint256 countsLength) internal pure returns (uint256 count) { count = idsLength / 8; require(countsLength / 16 == count && idsLength % 8 == 0 && countsLength % 16 == 0, "INVALID_REPORT_DATA"); } /// @notice Called by StakingRouter to update 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 _nodeOperatorIds, bytes _exitedValidatorsCounts ) external { _auth(STAKING_ROUTER_ROLE); uint256 nodeOperatorsCount = _checkReportPayload(_nodeOperatorIds.length, _exitedValidatorsCounts.length); uint256 totalNodeOperatorsCount = getNodeOperatorsCount(); uint256 nodeOperatorId; uint256 validatorsCount; uint256 _nodeOperatorIdsOffset; uint256 _exitedValidatorsCountsOffset; assembly { _nodeOperatorIdsOffset := add(calldataload(4), 36) // arg1 calldata offset + 4 (signature len) + 32 (length slot) _exitedValidatorsCountsOffset := add(calldataload(36), 36) // arg2 calldata offset + 4 (signature len) + 32 (length slot)) } for (uint256 i; i < nodeOperatorsCount;) { /// @solidity memory-safe-assembly assembly { nodeOperatorId := shr(192, calldataload(add(_nodeOperatorIdsOffset, mul(i, 8)))) validatorsCount := shr(128, calldataload(add(_exitedValidatorsCountsOffset, mul(i, 16)))) i := add(i, 1) } _requireValidRange(nodeOperatorId < totalNodeOperatorsCount); _updateExitedValidatorsCount(nodeOperatorId, validatorsCount, false); } _increaseValidatorsKeysNonce(); } /// @notice Permissionless method for distributing all accumulated module rewards among node operators /// based on the latest accounting report. /// /// @dev Rewards can be distributed after all necessary data required to distribute rewards among operators /// has been delivered, including exited and stuck keys. /// /// The reward distribution lifecycle: /// /// 1. TransferredToModule: Rewards are transferred to the module during an oracle main report. /// 2. ReadyForDistribution: All necessary data required to distribute rewards among operators has been delivered. /// 3. Distributed: Rewards have been successfully distributed. /// /// The function can only be called when the state is ReadyForDistribution. /// /// @dev Rewards can be distributed after node operators' statistics are updated until the next reward /// is transferred to the module during the next oracle frame. function distributeReward() external { require(getRewardDistributionState() == RewardDistributionState.ReadyForDistribution, "DISTRIBUTION_NOT_READY"); _updateRewardDistributionState(RewardDistributionState.Distributed); _distributeRewards(); } /// @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. function onExitedAndStuckValidatorsCountsUpdated() external { _auth(STAKING_ROUTER_ROLE); _updateRewardDistributionState(RewardDistributionState.ReadyForDistribution); } /// @notice Unsafely updates the number of validators in the EXITED/STUCK states for node operator with given id. /// @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 { _onlyExistedNodeOperator(_nodeOperatorId); _auth(STAKING_ROUTER_ROLE); _updateExitedValidatorsCount(_nodeOperatorId, _exitedValidatorsCount, true /* _allowDecrease */ ); _increaseValidatorsKeysNonce(); } function _updateExitedValidatorsCount(uint256 _nodeOperatorId, uint256 _exitedValidatorsCount, bool _allowDecrease) internal { Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 oldExitedValidatorsCount = signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET); if (_exitedValidatorsCount == oldExitedValidatorsCount) return; require( _allowDecrease || _exitedValidatorsCount > oldExitedValidatorsCount, "EXITED_VALIDATORS_COUNT_DECREASED" ); uint256 depositedValidatorsCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); // sustain invariant exited <= deposited _requireValidRange(_exitedValidatorsCount <= depositedValidatorsCount); signingKeysStats.set(TOTAL_EXITED_KEYS_COUNT_OFFSET, _exitedValidatorsCount); _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); emit ExitedSigningKeysCountChanged(_nodeOperatorId, _exitedValidatorsCount); Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); uint256 exitedValidatorsAbsDiff = Math256.absDiff(_exitedValidatorsCount, oldExitedValidatorsCount); if (_exitedValidatorsCount > oldExitedValidatorsCount) { summarySigningKeysStats.add(SUMMARY_EXITED_KEYS_COUNT_OFFSET, exitedValidatorsAbsDiff); } else { summarySigningKeysStats.sub(SUMMARY_EXITED_KEYS_COUNT_OFFSET, exitedValidatorsAbsDiff); } _saveSummarySigningKeysStats(summarySigningKeysStats); _updateSummaryMaxValidatorsCount(_nodeOperatorId); } /// @notice Updates the limit of the validators that can be used for deposit by DAO /// @param _nodeOperatorId Id of the node operator /// @param _isTargetLimitActive Flag indicating if the soft target limit is active /// @param _targetLimit Target limit of the node operator /// @dev This function is deprecated, use updateTargetValidatorsLimits instead function updateTargetValidatorsLimits(uint256 _nodeOperatorId, bool _isTargetLimitActive, uint256 _targetLimit) public { updateTargetValidatorsLimits(_nodeOperatorId, _isTargetLimitActive ? 1 : 0, _targetLimit); } /// @notice Updates the limit of the validators that can be used for deposit by DAO /// @param _nodeOperatorId Id of the node operator /// @param _targetLimitMode target limit mode (0 = disabled, 1 = soft mode, 2 = boosted mode) /// @param _targetLimit Target limit of the node operator function updateTargetValidatorsLimits(uint256 _nodeOperatorId, uint256 _targetLimitMode, uint256 _targetLimit) public { _onlyExistedNodeOperator(_nodeOperatorId); _auth(STAKING_ROUTER_ROLE); _requireValidRange(_targetLimit <= UINT64_MAX); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); operatorTargetStats.set(TARGET_LIMIT_MODE_OFFSET, _targetLimitMode); if (_targetLimitMode == 0) { _targetLimit = 0; } operatorTargetStats.set(TARGET_VALIDATORS_COUNT_OFFSET, _targetLimit); _saveOperatorTargetValidatorsStats(_nodeOperatorId, operatorTargetStats); emit TargetValidatorsCountChanged(_nodeOperatorId, _targetLimit, _targetLimitMode); _updateSummaryMaxValidatorsCount(_nodeOperatorId); _increaseValidatorsKeysNonce(); } // @dev Recalculate and update the max validator count for operator and summary stats function _updateSummaryMaxValidatorsCount(uint256 _nodeOperatorId) internal { (uint256 oldMaxSigningKeysCount, uint256 newMaxSigningKeysCount) = _applyNodeOperatorLimits(_nodeOperatorId); if (newMaxSigningKeysCount == oldMaxSigningKeysCount) return; Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); uint256 maxSigningKeysCountAbsDiff = Math256.absDiff(newMaxSigningKeysCount, oldMaxSigningKeysCount); if (newMaxSigningKeysCount > oldMaxSigningKeysCount) { summarySigningKeysStats.add(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET, maxSigningKeysCountAbsDiff); } else { summarySigningKeysStats.sub(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET, maxSigningKeysCountAbsDiff); } _saveSummarySigningKeysStats(summarySigningKeysStats); } /// @notice Invalidates all unused deposit data for all node operators function onWithdrawalCredentialsChanged() external { _auth(STAKING_ROUTER_ROLE); uint256 operatorsCount = getNodeOperatorsCount(); if (operatorsCount > 0) { _invalidateReadyToDepositKeysRange(0, operatorsCount - 1); } } /// @notice Invalidates all unused validators keys for node operators in the given range /// @param _indexFrom the first index (inclusive) of the node operator to invalidate keys for /// @param _indexTo the last index (inclusive) of the node operator to invalidate keys for function invalidateReadyToDepositKeysRange(uint256 _indexFrom, uint256 _indexTo) external { _auth(MANAGE_NODE_OPERATOR_ROLE); _invalidateReadyToDepositKeysRange(_indexFrom, _indexTo); } function _invalidateReadyToDepositKeysRange(uint256 _indexFrom, uint256 _indexTo) internal { _requireValidRange(_indexFrom <= _indexTo && _indexTo < getNodeOperatorsCount()); uint256 trimmedKeysCount; uint256 totalTrimmedKeysCount; uint256 totalSigningKeysCount; uint256 depositedSigningKeysCount; Packed64x4.Packed memory signingKeysStats; for (uint256 nodeOperatorId = _indexFrom; nodeOperatorId <= _indexTo; ++nodeOperatorId) { signingKeysStats = _loadOperatorSigningKeysStats(nodeOperatorId); totalSigningKeysCount = signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); if (totalSigningKeysCount == depositedSigningKeysCount) continue; assert(totalSigningKeysCount > depositedSigningKeysCount); trimmedKeysCount = totalSigningKeysCount - depositedSigningKeysCount; totalTrimmedKeysCount += trimmedKeysCount; signingKeysStats.set(TOTAL_KEYS_COUNT_OFFSET, depositedSigningKeysCount); signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, depositedSigningKeysCount); _saveOperatorSigningKeysStats(nodeOperatorId, signingKeysStats); _updateSummaryMaxValidatorsCount(nodeOperatorId); emit TotalSigningKeysCountChanged(nodeOperatorId, depositedSigningKeysCount); emit VettedSigningKeysCountChanged(nodeOperatorId, depositedSigningKeysCount); emit NodeOperatorTotalKeysTrimmed(nodeOperatorId, uint64(trimmedKeysCount)); } if (totalTrimmedKeysCount > 0) { _increaseValidatorsKeysNonce(); } } /// @notice Obtains deposit data to be used by StakingRouter to deposit to the Ethereum Deposit /// contract /// @param _depositsCount Number of deposits to be done /// @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 /* _depositCalldata */ ) external returns (bytes memory publicKeys, bytes memory signatures) { _auth(STAKING_ROUTER_ROLE); if (_depositsCount == 0) return (new bytes(0), new bytes(0)); ( uint256 allocatedKeysCount, uint256[] memory nodeOperatorIds, uint256[] memory activeKeysCountAfterAllocation ) = _getSigningKeysAllocationData(_depositsCount); require(allocatedKeysCount == _depositsCount, "INVALID_ALLOCATED_KEYS_COUNT"); (publicKeys, signatures) = _loadAllocatedSigningKeys( allocatedKeysCount, nodeOperatorIds, activeKeysCountAfterAllocation ); _increaseValidatorsKeysNonce(); } function _getNodeOperator(uint256 _nodeOperatorId) internal view returns (uint256 exitedSigningKeysCount, uint256 depositedSigningKeysCount, uint256 maxSigningKeysCount) { Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); exitedSigningKeysCount = signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET); depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); maxSigningKeysCount = operatorTargetStats.get(MAX_VALIDATORS_COUNT_OFFSET); // Validate data boundaries invariants here to not use SafeMath in caller methods assert(maxSigningKeysCount >= depositedSigningKeysCount && depositedSigningKeysCount >= exitedSigningKeysCount); } function _applyNodeOperatorLimits(uint256 _nodeOperatorId) internal returns (uint256 oldMaxSigningKeysCount, uint256 newMaxSigningKeysCount) { Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); uint256 depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); // It's expected that validators don't suffer from penalties most of the time, // so optimistically, set the count of max validators equal to the vetted validators count. newMaxSigningKeysCount = signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET); if (operatorTargetStats.get(TARGET_LIMIT_MODE_OFFSET) != 0) { // apply target limit when it's active and the node operator is not penalized newMaxSigningKeysCount = Math256.max( // max validators count can't be less than the deposited validators count // even when the target limit is less than the current active validators count depositedSigningKeysCount, Math256.min( // max validators count can't be greater than the vetted validators count newMaxSigningKeysCount, // SafeMath.add() isn't used below because the sum is always // less or equal to 2 * UINT64_MAX signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET) + operatorTargetStats.get(TARGET_VALIDATORS_COUNT_OFFSET) ) ); } oldMaxSigningKeysCount = operatorTargetStats.get(MAX_VALIDATORS_COUNT_OFFSET); if (oldMaxSigningKeysCount != newMaxSigningKeysCount) { operatorTargetStats.set(MAX_VALIDATORS_COUNT_OFFSET, newMaxSigningKeysCount); _saveOperatorTargetValidatorsStats(_nodeOperatorId, operatorTargetStats); } } function _getSigningKeysAllocationData(uint256 _keysCount) internal view returns (uint256 allocatedKeysCount, uint256[] memory nodeOperatorIds, uint256[] memory activeKeyCountsAfterAllocation) { uint256 activeNodeOperatorsCount = getActiveNodeOperatorsCount(); nodeOperatorIds = new uint256[](activeNodeOperatorsCount); activeKeyCountsAfterAllocation = new uint256[](activeNodeOperatorsCount); uint256[] memory activeKeysCapacities = new uint256[](activeNodeOperatorsCount); uint256 activeNodeOperatorIndex; uint256 nodeOperatorsCount = getNodeOperatorsCount(); uint256 maxSigningKeysCount; uint256 depositedSigningKeysCount; uint256 exitedSigningKeysCount; for (uint256 nodeOperatorId; nodeOperatorId < nodeOperatorsCount; ++nodeOperatorId) { (exitedSigningKeysCount, depositedSigningKeysCount, maxSigningKeysCount) = _getNodeOperator(nodeOperatorId); // the node operator has no available signing keys if (depositedSigningKeysCount == maxSigningKeysCount) continue; nodeOperatorIds[activeNodeOperatorIndex] = nodeOperatorId; activeKeyCountsAfterAllocation[activeNodeOperatorIndex] = depositedSigningKeysCount - exitedSigningKeysCount; activeKeysCapacities[activeNodeOperatorIndex] = maxSigningKeysCount - exitedSigningKeysCount; ++activeNodeOperatorIndex; } if (activeNodeOperatorIndex == 0) return (0, new uint256[](0), new uint256[](0)); /// @dev shrink the length of the resulting arrays if some active node operators have no available keys to be deposited if (activeNodeOperatorIndex < activeNodeOperatorsCount) { assembly { mstore(nodeOperatorIds, activeNodeOperatorIndex) mstore(activeKeyCountsAfterAllocation, activeNodeOperatorIndex) mstore(activeKeysCapacities, activeNodeOperatorIndex) } } (allocatedKeysCount, activeKeyCountsAfterAllocation) = MinFirstAllocationStrategy.allocate(activeKeyCountsAfterAllocation, activeKeysCapacities, _keysCount); /// @dev method NEVER allocates more keys than was requested assert(_keysCount >= allocatedKeysCount); } function _loadAllocatedSigningKeys( uint256 _keysCountToLoad, uint256[] memory _nodeOperatorIds, uint256[] memory _activeKeyCountsAfterAllocation ) internal returns (bytes memory pubkeys, bytes memory signatures) { (pubkeys, signatures) = SigningKeys.initKeysSigsBuf(_keysCountToLoad); uint256 loadedKeysCount = 0; uint256 depositedSigningKeysCountBefore; uint256 depositedSigningKeysCountAfter; uint256 keysCount; Packed64x4.Packed memory signingKeysStats; for (uint256 i; i < _nodeOperatorIds.length; ++i) { signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorIds[i]); depositedSigningKeysCountBefore = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); depositedSigningKeysCountAfter = signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET) + _activeKeyCountsAfterAllocation[i]; if (depositedSigningKeysCountAfter == depositedSigningKeysCountBefore) continue; // For gas savings SafeMath.add() wasn't used on depositedSigningKeysCountAfter // calculation, so below we check that operation finished without overflow // In case of overflow: // depositedSigningKeysCountAfter < signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET) // what violates invariant: // depositedSigningKeysCount >= exitedSigningKeysCount assert(depositedSigningKeysCountAfter > depositedSigningKeysCountBefore); keysCount = depositedSigningKeysCountAfter - depositedSigningKeysCountBefore; SIGNING_KEYS_MAPPING_NAME.loadKeysSigs( _nodeOperatorIds[i], depositedSigningKeysCountBefore, keysCount, pubkeys, signatures, loadedKeysCount ); loadedKeysCount += keysCount; emit DepositedSigningKeysCountChanged(_nodeOperatorIds[i], depositedSigningKeysCountAfter); signingKeysStats.set(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET, depositedSigningKeysCountAfter); _saveOperatorSigningKeysStats(_nodeOperatorIds[i], signingKeysStats); _updateSummaryMaxValidatorsCount(_nodeOperatorIds[i]); } assert(loadedKeysCount == _keysCountToLoad); Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); summarySigningKeysStats.add(SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET, loadedKeysCount); _saveSummarySigningKeysStats(summarySigningKeysStats); } /// @notice Returns the node operator by id /// @param _nodeOperatorId Node Operator id /// @param _fullInfo If true, name will be returned as well function getNodeOperator(uint256 _nodeOperatorId, bool _fullInfo) external view returns ( bool active, string name, address rewardAddress, uint64 totalVettedValidators, uint64 totalExitedValidators, uint64 totalAddedValidators, uint64 totalDepositedValidators ) { _onlyExistedNodeOperator(_nodeOperatorId); NodeOperator storage nodeOperator = _nodeOperators[_nodeOperatorId]; active = nodeOperator.active; rewardAddress = nodeOperator.rewardAddress; name = _fullInfo ? nodeOperator.name : ""; // reading name is 2+ SLOADs Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); totalVettedValidators = uint64(signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET)); totalExitedValidators = uint64(signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET)); totalAddedValidators = uint64(signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET)); totalDepositedValidators = uint64(signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET)); } /// @notice Returns the rewards distribution proportional to the effective stake for each node operator. /// @notice [DEPRECATED] The `penalized` array is no longer relevant and always contains only `false`. /// @param _totalRewardShares Total amount of reward shares to distribute. function getRewardsDistribution(uint256 _totalRewardShares) public view returns (address[] memory recipients, uint256[] memory shares, bool[] memory penalized) { uint256 nodeOperatorCount = getNodeOperatorsCount(); uint256 activeCount = getActiveNodeOperatorsCount(); recipients = new address[](activeCount); shares = new uint256[](activeCount); penalized = new bool[](activeCount); uint256 idx = 0; uint256 totalActiveValidatorsCount = 0; Packed64x4.Packed memory signingKeysStats; for (uint256 operatorId; operatorId < nodeOperatorCount; ++operatorId) { if (!getNodeOperatorIsActive(operatorId)) continue; signingKeysStats = _loadOperatorSigningKeysStats(operatorId); uint256 totalExitedValidators = signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET); uint256 totalDepositedValidators = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); // validate invariant to not use SafeMath.sub() assert(totalDepositedValidators >= totalExitedValidators); uint256 activeValidatorsCount = totalDepositedValidators - totalExitedValidators; // SafeMath.add() isn't used below because the following is always true: // totalActiveValidatorsCount <= MAX_NODE_OPERATORS_COUNT * UINT64_MAX totalActiveValidatorsCount += activeValidatorsCount; recipients[idx] = _nodeOperators[operatorId].rewardAddress; // prefill shares array with 'key share' for recipient, see below shares[idx] = activeValidatorsCount; // [DEPRECATED] Penalized flag is no longer relevant. Always false. // penalized[idx] = false; ++idx; } if (totalActiveValidatorsCount == 0) return (recipients, shares, penalized); for (idx = 0; idx < activeCount; ++idx) { /// @dev unsafe division used below for gas savings. It's safe in the current case /// because SafeMath.div() only validates that the divider isn't equal to zero. /// totalActiveValidatorsCount guaranteed greater than zero. shares[idx] = shares[idx].mul(_totalRewardShares) / totalActiveValidatorsCount; } return (recipients, shares, penalized); } /// @notice Add `_quantity` validator signing keys to the keys of the node operator #`_nodeOperatorId`. Concatenated keys are: `_pubkeys` /// @dev Along with each key the DAO has to provide a signatures for the /// (pubkey, withdrawal_credentials, 32000000000) message. /// Given that information, the contract'll be able to call /// deposit_contract.deposit on-chain. /// @param _nodeOperatorId Node Operator id /// @param _keysCount Number of signing keys provided /// @param _publicKeys Several concatenated validator signing keys /// @param _signatures Several concatenated signatures for (pubkey, withdrawal_credentials, 32000000000) messages function addSigningKeys(uint256 _nodeOperatorId, uint256 _keysCount, bytes _publicKeys, bytes _signatures) external { _addSigningKeys(_nodeOperatorId, _keysCount, _publicKeys, _signatures); } /// @notice Add `_quantity` validator signing keys of operator #`_id` to the set of usable keys. Concatenated keys are: `_pubkeys`. Can be done by node operator in question by using the designated rewards address. /// @dev Along with each key the DAO has to provide a signatures for the /// (pubkey, withdrawal_credentials, 32000000000) message. /// Given that information, the contract'll be able to call /// deposit_contract.deposit on-chain. /// @param _nodeOperatorId Node Operator id /// @param _keysCount Number of signing keys provided /// @param _publicKeys Several concatenated validator signing keys /// @param _signatures Several concatenated signatures for (pubkey, withdrawal_credentials, 32000000000) messages /// @dev DEPRECATED use addSigningKeys instead function addSigningKeysOperatorBH(uint256 _nodeOperatorId, uint256 _keysCount, bytes _publicKeys, bytes _signatures) external { _addSigningKeys(_nodeOperatorId, _keysCount, _publicKeys, _signatures); } function _addSigningKeys(uint256 _nodeOperatorId, uint256 _keysCount, bytes _publicKeys, bytes _signatures) internal { _onlyExistedNodeOperator(_nodeOperatorId); _onlyNodeOperatorManager(msg.sender, _nodeOperatorId); _requireValidRange(_keysCount != 0 && _keysCount <= UINT64_MAX); Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 totalSigningKeysCount = signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); _requireValidRange(totalSigningKeysCount.add(_keysCount) <= UINT64_MAX); totalSigningKeysCount = SIGNING_KEYS_MAPPING_NAME.saveKeysSigs(_nodeOperatorId, totalSigningKeysCount, _keysCount, _publicKeys, _signatures); emit TotalSigningKeysCountChanged(_nodeOperatorId, totalSigningKeysCount); signingKeysStats.set(TOTAL_KEYS_COUNT_OFFSET, totalSigningKeysCount); _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); _increaseValidatorsKeysNonce(); } /// @notice Removes a validator signing key #`_index` from the keys of the node operator #`_nodeOperatorId` /// @param _nodeOperatorId Node Operator id /// @param _index Index of the key, starting with 0 /// @dev DEPRECATED use removeSigningKeys instead function removeSigningKey(uint256 _nodeOperatorId, uint256 _index) external { _removeUnusedSigningKeys(_nodeOperatorId, _index, 1); } /// @notice Removes an #`_keysCount` of validator signing keys starting from #`_index` of operator #`_id` usable keys. Executed on behalf of DAO. /// @param _nodeOperatorId Node Operator id /// @param _fromIndex Index of the key, starting with 0 /// @param _keysCount Number of keys to remove function removeSigningKeys(uint256 _nodeOperatorId, uint256 _fromIndex, uint256 _keysCount) external { _removeUnusedSigningKeys(_nodeOperatorId, _fromIndex, _keysCount); } /// @notice Removes a validator signing key #`_index` of operator #`_id` from the set of usable keys. Executed on behalf of Node Operator. /// @param _nodeOperatorId Node Operator id /// @param _index Index of the key, starting with 0 /// @dev DEPRECATED use removeSigningKeys instead function removeSigningKeyOperatorBH(uint256 _nodeOperatorId, uint256 _index) external { _removeUnusedSigningKeys(_nodeOperatorId, _index, 1); } /// @notice Removes an #`_keysCount` of validator signing keys starting from #`_index` of operator #`_id` usable keys. Executed on behalf of Node Operator. /// @param _nodeOperatorId Node Operator id /// @param _fromIndex Index of the key, starting with 0 /// @param _keysCount Number of keys to remove /// @dev DEPRECATED use removeSigningKeys instead function removeSigningKeysOperatorBH(uint256 _nodeOperatorId, uint256 _fromIndex, uint256 _keysCount) external { _removeUnusedSigningKeys(_nodeOperatorId, _fromIndex, _keysCount); } /// @notice Returns true if the given validator public key has already been reported as exiting. /// @dev The function hashes the input public key using keccak256 and checks if it exists in the _validatorProcessedLateKeys mapping. /// @param _publicKey The BLS public key of the validator (serialized as bytes). /// @return True if the validator exit for the provided key has been reported, false otherwise. function isValidatorExitingKeyReported(bytes _publicKey) public view returns (bool) { bytes32 processedKeyHash = keccak256(_publicKey); return _validatorProcessedLateKeys[processedKeyHash]; } /// @notice Returns the number of seconds after which a validator is considered late for specified node operator. /// @dev The operatorId argument is ignored and present only to comply with the IStakingModule interface. /// @return uint256 The exit deadline threshold in seconds for all node operators. function exitDeadlineThreshold(uint256 /* operatorId */) public view returns (uint256) { return _exitDeadlineThreshold(); } function _exitDeadlineThreshold() internal view returns (uint256) { return Packed64x4.Packed(EXIT_DELAY_STATS.getStorageUint256()).get(EXIT_DELAY_THRESHOLD_OFFSET); } /// @notice Returns the Timestamp before which validators reported as late will not result in penalties for their Node Operators.. /// @return uint256 The cutoff timestamp used when evaluating late exits. function exitPenaltyCutoffTimestamp() public view returns (uint256) { return Packed64x4.Packed(EXIT_DELAY_STATS.getStorageUint256()).get(EXIT_PENALTY_CUTOFF_TIMESTAMP_OFFSET); } /// @notice Sets the validator exit deadline threshold and the reporting window for late exits. /// @dev Updates the cutoff timestamp before which a validator that was requested to exit cannot be reported as late. /// @param _threshold Number of seconds a validator has to exit after becoming eligible. /// @param _lateReportingWindow Additional number of seconds during which a late exit can still be reported. function setExitDeadlineThreshold(uint256 _threshold, uint256 _lateReportingWindow) external { _auth(MANAGE_NODE_OPERATOR_ROLE); _setExitDeadlineThreshold(_threshold, _lateReportingWindow); } function _setExitDeadlineThreshold(uint256 _threshold, uint256 _lateReportingWindow) internal { require(_threshold > 0, "INVALID_EXIT_DELAY_THRESHOLD"); // Set the cutoff timestamp to the current time minus the threshold and reportingWindow period uint256 currentCutoffTimestamp = block.timestamp - _threshold - _lateReportingWindow; require(exitPenaltyCutoffTimestamp() <= currentCutoffTimestamp, "INVALID_EXIT_PENALTY_CUTOFF_TIMESTAMP"); Packed64x4.Packed memory stats = Packed64x4.Packed(0); stats.set(EXIT_DELAY_THRESHOLD_OFFSET, _threshold); stats.set(EXIT_PENALTY_CUTOFF_TIMESTAMP_OFFSET, currentCutoffTimestamp); EXIT_DELAY_STATS.setStorageUint256(stats.v); emit ExitDeadlineThresholdChanged(_threshold, _lateReportingWindow); } /// @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 triggered to exit 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. function onValidatorExitTriggered( uint256 _nodeOperatorId, bytes _publicKey, uint256 _withdrawalRequestPaidFee, uint256 _exitType ) external { _auth(STAKING_ROUTER_ROLE); emit ValidatorExitTriggered(_nodeOperatorId, _publicKey, _withdrawalRequestPaidFee, _exitType); } /// @notice Determines whether a validator's exit status should be updated and will have an effect on the Node Operator. /// @param _publicKey The public key of the validator. /// @param _proofSlotTimestamp The timestamp (slot time) when the validator was last known to be in an active ongoing state. /// @param _eligibleToExitInSec The number of seconds the validator was eligible to exit but did not. /// @return True if the validator has exceeded the exit deadline threshold and hasn't been reported yet. function isValidatorExitDelayPenaltyApplicable( uint256, // _nodeOperatorId uint256 _proofSlotTimestamp, bytes _publicKey, uint256 _eligibleToExitInSec ) external view returns (bool) { // Check if the key is already reported if (isValidatorExitingKeyReported(_publicKey)) { return false; } return _eligibleToExitInSec >= _exitDeadlineThreshold() && _proofSlotTimestamp - _eligibleToExitInSec >= exitPenaltyCutoffTimestamp(); } /// @notice Handles tracking and penalization logic for a node operator who failed to exit their validator within the defined 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 marks the validator as processed to avoid duplicate reports. /// @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 after request but has not exited. function reportValidatorExitDelay( uint256 _nodeOperatorId, uint256 _proofSlotTimestamp, bytes _publicKey, uint256 _eligibleToExitInSec ) external { _auth(STAKING_ROUTER_ROLE); require(_publicKey.length == 48, "INVALID_PUBLIC_KEY"); // Check if exit delay exceeds the threshold require(_eligibleToExitInSec >= _exitDeadlineThreshold(), "EXIT_DELAY_BELOW_THRESHOLD"); // Check if the proof slot timestamp is within the allowed reporting window require(_proofSlotTimestamp - _eligibleToExitInSec >= exitPenaltyCutoffTimestamp(), "TOO_LATE_FOR_EXIT_DELAY_REPORT"); bytes32 processedKeyHash = keccak256(_publicKey); // Skip if key is already processed (i.e., not in NotProcessed state) if (_validatorProcessedLateKeys[processedKeyHash]) { return; } // Mark the validator exit key as processed _validatorProcessedLateKeys[processedKeyHash] = true; emit ValidatorExitStatusUpdated(_nodeOperatorId, _publicKey, _eligibleToExitInSec, _proofSlotTimestamp); } function _removeUnusedSigningKeys(uint256 _nodeOperatorId, uint256 _fromIndex, uint256 _keysCount) internal { _onlyExistedNodeOperator(_nodeOperatorId); _onlyNodeOperatorManager(msg.sender, _nodeOperatorId); // preserve the previous behavior of the method here and just return earlier if (_keysCount == 0) return; Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 totalSigningKeysCount = signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); // comparing _fromIndex.add(_keysCount) <= totalSigningKeysCount is enough as totalSigningKeysCount is always less than UINT64_MAX _requireValidRange( _fromIndex >= signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET) && _fromIndex.add(_keysCount) <= totalSigningKeysCount ); totalSigningKeysCount = SIGNING_KEYS_MAPPING_NAME.removeKeysSigs(_nodeOperatorId, _fromIndex, _keysCount, totalSigningKeysCount); signingKeysStats.set(TOTAL_KEYS_COUNT_OFFSET, totalSigningKeysCount); emit TotalSigningKeysCountChanged(_nodeOperatorId, totalSigningKeysCount); uint256 vettedSigningKeysCount = signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET); if (_fromIndex < vettedSigningKeysCount) { // decreasing the staking limit so the key at _index can't be used anymore signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, _fromIndex); emit VettedSigningKeysCountChanged(_nodeOperatorId, _fromIndex); } _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); _updateSummaryMaxValidatorsCount(_nodeOperatorId); _increaseValidatorsKeysNonce(); } /// @notice Returns total number of signing keys of the node operator #`_nodeOperatorId` function getTotalSigningKeyCount(uint256 _nodeOperatorId) external view returns (uint256) { _onlyExistedNodeOperator(_nodeOperatorId); Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); return signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); } /// @notice Returns number of usable signing keys of the node operator #`_nodeOperatorId` function getUnusedSigningKeyCount(uint256 _nodeOperatorId) external view returns (uint256) { _onlyExistedNodeOperator(_nodeOperatorId); Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); return signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET).sub(signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET)); } /// @notice Returns n-th signing key of the node operator #`_nodeOperatorId` /// @param _nodeOperatorId Node Operator id /// @param _index Index of the key, starting with 0 /// @return key Key /// @return depositSignature Signature needed for a deposit_contract.deposit call /// @return used Flag indication if the key was used in the staking function getSigningKey(uint256 _nodeOperatorId, uint256 _index) external view returns (bytes key, bytes depositSignature, bool used) { bool[] memory keyUses; (key, depositSignature, keyUses) = getSigningKeys(_nodeOperatorId, _index, 1); used = keyUses[0]; } /// @notice Returns n signing keys of the node operator #`_nodeOperatorId` /// @param _nodeOperatorId Node Operator id /// @param _offset Offset of the key, starting with 0 /// @param _limit Number of keys to return /// @return pubkeys Keys concatenated into the bytes batch /// @return signatures Signatures concatenated into the bytes batch needed for a deposit_contract.deposit call /// @return used Array of flags indicated if the key was used in the staking function getSigningKeys(uint256 _nodeOperatorId, uint256 _offset, uint256 _limit) public view returns (bytes memory pubkeys, bytes memory signatures, bool[] memory used) { _onlyExistedNodeOperator(_nodeOperatorId); Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); _requireValidRange(_offset.add(_limit) <= signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET)); uint256 depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); (pubkeys, signatures) = SigningKeys.initKeysSigsBuf(_limit); used = new bool[](_limit); SIGNING_KEYS_MAPPING_NAME.loadKeysSigs(_nodeOperatorId, _offset, _limit, pubkeys, signatures, 0); for (uint256 i; i < _limit; ++i) { used[i] = (_offset + i) < depositedSigningKeysCount; } } /// @notice Returns the type of the staking module function getType() external view returns (bytes32) { return TYPE_POSITION.getStorageBytes32(); } function getStakingModuleSummary() external view returns (uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount) { Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); totalExitedValidators = summarySigningKeysStats.get(SUMMARY_EXITED_KEYS_COUNT_OFFSET); totalDepositedValidators = summarySigningKeysStats.get(SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET); depositableValidatorsCount = summarySigningKeysStats.get(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET).sub(totalDepositedValidators); } function getNodeOperatorSummary(uint256 _nodeOperatorId) external view returns ( uint256 targetLimitMode, uint256 targetValidatorsCount, uint256 stuckValidatorsCount, uint256 refundedValidatorsCount, uint256 stuckPenaltyEndTimestamp, uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount ) { _onlyExistedNodeOperator(_nodeOperatorId); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); targetLimitMode = operatorTargetStats.get(TARGET_LIMIT_MODE_OFFSET); targetValidatorsCount = operatorTargetStats.get(TARGET_VALIDATORS_COUNT_OFFSET); stuckValidatorsCount = 0; refundedValidatorsCount = 0; stuckPenaltyEndTimestamp = 0; (totalExitedValidators, totalDepositedValidators, depositableValidatorsCount) = _getNodeOperatorValidatorsSummary(_nodeOperatorId); } function _getNodeOperatorValidatorsSummary(uint256 _nodeOperatorId) internal view returns ( uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount ) { uint256 totalMaxValidators; (totalExitedValidators, totalDepositedValidators, totalMaxValidators) = _getNodeOperator(_nodeOperatorId); depositableValidatorsCount = totalMaxValidators - totalDepositedValidators; } /// @notice [DEPRECATED] Penalty logic removed. Always returns `false`. /// @dev _nodeOperatorId Ignored. /// @return Always returns `false`. function isOperatorPenalized(uint256 /* _nodeOperatorId */) public pure returns (bool) { return false; } /// @notice [DEPRECATED] Penalty logic removed. Always returns `true`. /// @dev _nodeOperatorId Ignored. /// @return Always returns `true`. function isOperatorPenaltyCleared(uint256 /* _nodeOperatorId */) public pure returns (bool) { return true; } /// @notice Returns total number of node operators function getNodeOperatorsCount() public view returns (uint256) { return TOTAL_OPERATORS_COUNT_POSITION.getStorageUint256(); } /// @notice Returns number of active node operators function getActiveNodeOperatorsCount() public view returns (uint256) { return ACTIVE_OPERATORS_COUNT_POSITION.getStorageUint256(); } /// @notice Returns if the node operator with given id is active function getNodeOperatorIsActive(uint256 _nodeOperatorId) public view returns (bool) { return _nodeOperators[_nodeOperatorId].active; } /// @notice Returns up to `_limit` node operator ids starting from the `_offset`. function getNodeOperatorIds(uint256 _offset, uint256 _limit) external view returns (uint256[] memory nodeOperatorIds) { uint256 nodeOperatorsCount = getNodeOperatorsCount(); if (_offset >= nodeOperatorsCount || _limit == 0) return; nodeOperatorIds = new uint256[](Math256.min(_limit, nodeOperatorsCount - _offset)); for (uint256 i = 0; i < nodeOperatorIds.length; ++i) { nodeOperatorIds[i] = _offset + i; } } /// @notice Returns a counter that MUST change it's value when any of the following happens: /// 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 function getNonce() external view returns (uint256) { return KEYS_OP_INDEX_POSITION.getStorageUint256(); } /// @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 DEPRECATED use getNonce() instead function getKeysOpIndex() external view returns (uint256) { return KEYS_OP_INDEX_POSITION.getStorageUint256(); } /// @notice distributes rewards among node operators /// @return the amount of stETH shares distributed among node operators function _distributeRewards() internal returns (uint256 distributed) { IStETH stETH = IStETH(getLocator().lido()); uint256 sharesToDistribute = stETH.sharesOf(address(this)); if (sharesToDistribute == 0) { return; } (address[] memory recipients, uint256[] memory shares,) = getRewardsDistribution(sharesToDistribute); for (uint256 idx; idx < recipients.length; ++idx) { /// @dev skip ultra-low amounts processing to avoid transfer zero amount in case of a penalty if (shares[idx] < 2) continue; stETH.transferShares(recipients[idx], shares[idx]); distributed = distributed.add(shares[idx]); emit RewardsDistributed(recipients[idx], shares[idx]); } } function getLocator() public view returns (ILidoLocator) { return ILidoLocator(LIDO_LOCATOR_POSITION.getStorageAddress()); } /// @notice [DEPRECATED] Stuck penalty delay logic removed. Always returns 0. /// @return Always returns 0. function getStuckPenaltyDelay() public pure returns (uint256) { return 0; } /// @dev Get the current reward distribution state, anyone can monitor this state /// and distribute reward (call distributeReward method) among operators when it's `ReadyForDistribution` function getRewardDistributionState() public view returns (RewardDistributionState) { uint256 state = REWARD_DISTRIBUTION_STATE.getStorageUint256(); return RewardDistributionState(state); } function _updateRewardDistributionState(RewardDistributionState _state) internal { REWARD_DISTRIBUTION_STATE.setStorageUint256(uint256(_state)); emit RewardDistributionStateChanged(_state); } function _increaseValidatorsKeysNonce() internal { uint256 keysOpIndex = KEYS_OP_INDEX_POSITION.getStorageUint256() + 1; KEYS_OP_INDEX_POSITION.setStorageUint256(keysOpIndex); /// @dev [DEPRECATED] event preserved for tooling compatibility emit KeysOpIndexSet(keysOpIndex); emit NonceChanged(keysOpIndex); } function _loadSummarySigningKeysStats() internal view returns (Packed64x4.Packed memory) { return _nodeOperatorSummary.summarySigningKeysStats; } function _saveSummarySigningKeysStats(Packed64x4.Packed memory _val) internal { _nodeOperatorSummary.summarySigningKeysStats = _val; } function _loadOperatorTargetValidatorsStats(uint256 _nodeOperatorId) internal view returns (Packed64x4.Packed memory) { return _nodeOperators[_nodeOperatorId].targetValidatorsStats; } function _saveOperatorTargetValidatorsStats(uint256 _nodeOperatorId, Packed64x4.Packed memory _val) internal { _nodeOperators[_nodeOperatorId].targetValidatorsStats = _val; } function _loadOperatorSigningKeysStats(uint256 _nodeOperatorId) internal view returns (Packed64x4.Packed memory) { return _nodeOperators[_nodeOperatorId].signingKeysStats; } function _saveOperatorSigningKeysStats(uint256 _nodeOperatorId, Packed64x4.Packed memory _val) internal { _nodeOperators[_nodeOperatorId].signingKeysStats = _val; } function _requireAuth(bool _pass) internal pure { require(_pass, "APP_AUTH_FAILED"); } function _requireNotSameValue(bool _pass) internal pure { require(_pass, "VALUE_IS_THE_SAME"); } function _requireValidRange(bool _pass) internal pure { require(_pass, "OUT_OF_RANGE"); } function _onlyCorrectNodeOperatorState(bool _pass) internal pure { require(_pass, "WRONG_OPERATOR_ACTIVE_STATE"); } function _auth(bytes32 _role) internal view { _requireAuth(canPerform(msg.sender, _role, new uint256[](0))); } function _authP(bytes32 _role, uint256[] _params) internal view { _requireAuth(canPerform(msg.sender, _role, _params)); } function _onlyNodeOperatorManager(address _sender, uint256 _nodeOperatorId) internal view { bool isRewardAddress = _sender == _nodeOperators[_nodeOperatorId].rewardAddress; bool isActive = _nodeOperators[_nodeOperatorId].active; _requireAuth((isRewardAddress && isActive) || canPerform(_sender, MANAGE_SIGNING_KEYS, arr(_nodeOperatorId))); } function _onlyExistedNodeOperator(uint256 _nodeOperatorId) internal view { _requireValidRange(_nodeOperatorId < getNodeOperatorsCount()); } function _onlyValidNodeOperatorName(string _name) internal pure { require(bytes(_name).length > 0 && bytes(_name).length <= MAX_NODE_OPERATOR_NAME_LENGTH, "WRONG_NAME_LENGTH"); } function _onlyValidRewardAddress(address _rewardAddress) internal view { _onlyNonZeroAddress(_rewardAddress); // The Lido address is forbidden explicitly because stETH transfers on this contract will revert // See onExitedAndStuckValidatorsCountsUpdated() and StETH._transferShares() for details require(_rewardAddress != getLocator().lido(), "LIDO_REWARD_ADDRESS"); } function _onlyNonZeroAddress(address _a) internal pure { require(_a != address(0), "ZERO_ADDRESS"); } }
/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
contract ACLSyntaxSugar {
function arr() internal pure returns (uint256[]) {
return new uint256[](0);
}
function arr(bytes32 _a) internal pure returns (uint256[] r) {
return arr(uint256(_a));
}
function arr(bytes32 _a, bytes32 _b) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b));
}
function arr(address _a) internal pure returns (uint256[] r) {
return arr(uint256(_a));
}
function arr(address _a, address _b) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b));
}
function arr(address _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
return arr(uint256(_a), _b, _c);
}
function arr(address _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
return arr(uint256(_a), _b, _c, _d);
}
function arr(address _a, uint256 _b) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b));
}
function arr(address _a, address _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b), _c, _d, _e);
}
function arr(address _a, address _b, address _c) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b), uint256(_c));
}
function arr(address _a, address _b, uint256 _c) internal pure returns (uint256[] r) {
return arr(uint256(_a), uint256(_b), uint256(_c));
}
function arr(uint256 _a) internal pure returns (uint256[] r) {
r = new uint256[](1);
r[0] = _a;
}
function arr(uint256 _a, uint256 _b) internal pure returns (uint256[] r) {
r = new uint256[](2);
r[0] = _a;
r[1] = _b;
}
function arr(uint256 _a, uint256 _b, uint256 _c) internal pure returns (uint256[] r) {
r = new uint256[](3);
r[0] = _a;
r[1] = _b;
r[2] = _c;
}
function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d) internal pure returns (uint256[] r) {
r = new uint256[](4);
r[0] = _a;
r[1] = _b;
r[2] = _c;
r[3] = _d;
}
function arr(uint256 _a, uint256 _b, uint256 _c, uint256 _d, uint256 _e) internal pure returns (uint256[] r) {
r = new uint256[](5);
r[0] = _a;
r[1] = _b;
r[2] = _c;
r[3] = _d;
r[4] = _e;
}
}
contract ACLHelpers {
function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
return uint8(_x >> (8 * 30));
}
function decodeParamId(uint256 _x) internal pure returns (uint8 b) {
return uint8(_x >> (8 * 31));
}
function decodeParamsList(uint256 _x) internal pure returns (uint32 a, uint32 b, uint32 c) {
a = uint32(_x);
b = uint32(_x >> (8 * 4));
c = uint32(_x >> (8 * 8));
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
interface IACL {
function initialize(address permissionsCreator) external;
// TODO: this should be external
// See https://github.com/ethereum/solidity/issues/4832
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "../common/UnstructuredStorage.sol";
import "../kernel/IKernel.sol";
contract AppStorage {
using UnstructuredStorage for bytes32;
/* Hardcoded constants to save gas
bytes32 internal constant KERNEL_POSITION = keccak256("aragonOS.appStorage.kernel");
bytes32 internal constant APP_ID_POSITION = keccak256("aragonOS.appStorage.appId");
*/
bytes32 internal constant KERNEL_POSITION = 0x4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b;
bytes32 internal constant APP_ID_POSITION = 0xd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b;
function kernel() public view returns (IKernel) {
return IKernel(KERNEL_POSITION.getStorageAddress());
}
function appId() public view returns (bytes32) {
return APP_ID_POSITION.getStorageBytes32();
}
function setKernel(IKernel _kernel) internal {
KERNEL_POSITION.setStorageAddress(address(_kernel));
}
function setAppId(bytes32 _appId) internal {
APP_ID_POSITION.setStorageBytes32(_appId);
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./AppStorage.sol";
import "../acl/ACLSyntaxSugar.sol";
import "../common/Autopetrified.sol";
import "../common/ConversionHelpers.sol";
import "../common/ReentrancyGuard.sol";
import "../common/VaultRecoverable.sol";
import "../evmscript/EVMScriptRunner.sol";
// Contracts inheriting from AragonApp are, by default, immediately petrified upon deployment so
// that they can never be initialized.
// Unless overriden, this behaviour enforces those contracts to be usable only behind an AppProxy.
// ReentrancyGuard, EVMScriptRunner, and ACLSyntaxSugar are not directly used by this contract, but
// are included so that they are automatically usable by subclassing contracts
contract AragonApp is AppStorage, Autopetrified, VaultRecoverable, ReentrancyGuard, EVMScriptRunner, ACLSyntaxSugar {
string private constant ERROR_AUTH_FAILED = "APP_AUTH_FAILED";
modifier auth(bytes32 _role) {
require(canPerform(msg.sender, _role, new uint256[](0)), ERROR_AUTH_FAILED);
_;
}
modifier authP(bytes32 _role, uint256[] _params) {
require(canPerform(msg.sender, _role, _params), ERROR_AUTH_FAILED);
_;
}
/**
* @dev Check whether an action can be performed by a sender for a particular role on this app
* @param _sender Sender of the call
* @param _role Role on this app
* @param _params Permission params for the role
* @return Boolean indicating whether the sender has the permissions to perform the action.
* Always returns false if the app hasn't been initialized yet.
*/
function canPerform(address _sender, bytes32 _role, uint256[] _params) public view returns (bool) {
if (!hasInitialized()) {
return false;
}
IKernel linkedKernel = kernel();
if (address(linkedKernel) == address(0)) {
return false;
}
return linkedKernel.hasPermission(
_sender,
address(this),
_role,
ConversionHelpers.dangerouslyCastUintArrayToBytes(_params)
);
}
/**
* @dev Get the recovery vault for the app
* @return Recovery vault address for the app
*/
function getRecoveryVault() public view returns (address) {
// Funds recovery via a vault is only available when used with a kernel
return kernel().getRecoveryVault(); // if kernel is not set, it will revert
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./Petrifiable.sol";
contract Autopetrified is Petrifiable {
constructor() public {
// Immediately petrify base (non-proxy) instances of inherited contracts on deploy.
// This renders them uninitializable (and unusable without a proxy).
petrify();
}
}pragma solidity ^0.4.24;
library ConversionHelpers {
string private constant ERROR_IMPROPER_LENGTH = "CONVERSION_IMPROPER_LENGTH";
function dangerouslyCastUintArrayToBytes(uint256[] memory _input) internal pure returns (bytes memory output) {
// Force cast the uint256[] into a bytes array, by overwriting its length
// Note that the bytes array doesn't need to be initialized as we immediately overwrite it
// with the input and a new length. The input becomes invalid from this point forward.
uint256 byteLength = _input.length * 32;
assembly {
output := _input
mstore(output, byteLength)
}
}
function dangerouslyCastBytesToUintArray(bytes memory _input) internal pure returns (uint256[] memory output) {
// Force cast the bytes array into a uint256[], by overwriting its length
// Note that the uint256[] doesn't need to be initialized as we immediately overwrite it
// with the input and a new length. The input becomes invalid from this point forward.
uint256 intsLength = _input.length / 32;
require(_input.length == intsLength * 32, ERROR_IMPROPER_LENGTH);
assembly {
output := _input
mstore(output, intsLength)
}
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
// aragonOS and aragon-apps rely on address(0) to denote native ETH, in
// contracts where both tokens and ETH are accepted
contract EtherTokenConstant {
address internal constant ETH = address(0);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./TimeHelpers.sol";
import "./UnstructuredStorage.sol";
contract Initializable is TimeHelpers {
using UnstructuredStorage for bytes32;
// keccak256("aragonOS.initializable.initializationBlock")
bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e;
string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED";
string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED";
modifier onlyInit {
require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED);
_;
}
modifier isInitialized {
require(hasInitialized(), ERROR_NOT_INITIALIZED);
_;
}
/**
* @return Block number in which the contract was initialized
*/
function getInitializationBlock() public view returns (uint256) {
return INITIALIZATION_BLOCK_POSITION.getStorageUint256();
}
/**
* @return Whether the contract has been initialized by the time of the current block
*/
function hasInitialized() public view returns (bool) {
uint256 initializationBlock = getInitializationBlock();
return initializationBlock != 0 && getBlockNumber() >= initializationBlock;
}
/**
* @dev Function to be called by top level contract after initialization has finished.
*/
function initialized() internal onlyInit {
INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber());
}
/**
* @dev Function to be called by top level contract after initialization to enable the contract
* at a future block number rather than immediately.
*/
function initializedAt(uint256 _blockNumber) internal onlyInit {
INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber);
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
contract IsContract {
/*
* NOTE: this should NEVER be used for authentication
* (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
*
* This is only intended to be used as a sanity check that an address is actually a contract,
* RATHER THAN an address not being a contract.
*/
function isContract(address _target) internal view returns (bool) {
if (_target == address(0)) {
return false;
}
uint256 size;
assembly { size := extcodesize(_target) }
return size > 0;
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
interface IVaultRecoverable {
event RecoverToVault(address indexed vault, address indexed token, uint256 amount);
function transferToVault(address token) external;
function allowRecoverability(address token) external view returns (bool);
function getRecoveryVault() external view returns (address);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./Initializable.sol";
contract Petrifiable is Initializable {
// Use block UINT256_MAX (which should be never) as the initializable date
uint256 internal constant PETRIFIED_BLOCK = uint256(-1);
function isPetrified() public view returns (bool) {
return getInitializationBlock() == PETRIFIED_BLOCK;
}
/**
* @dev Function to be called by top level contract to prevent being initialized.
* Useful for freezing base contracts when they're used behind proxies.
*/
function petrify() internal onlyInit {
initializedAt(PETRIFIED_BLOCK);
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "../common/UnstructuredStorage.sol";
contract ReentrancyGuard {
using UnstructuredStorage for bytes32;
/* Hardcoded constants to save gas
bytes32 internal constant REENTRANCY_MUTEX_POSITION = keccak256("aragonOS.reentrancyGuard.mutex");
*/
bytes32 private constant REENTRANCY_MUTEX_POSITION = 0xe855346402235fdd185c890e68d2c4ecad599b88587635ee285bce2fda58dacb;
string private constant ERROR_REENTRANT = "REENTRANCY_REENTRANT_CALL";
modifier nonReentrant() {
// Ensure mutex is unlocked
require(!REENTRANCY_MUTEX_POSITION.getStorageBool(), ERROR_REENTRANT);
// Lock mutex before function call
REENTRANCY_MUTEX_POSITION.setStorageBool(true);
// Perform function call
_;
// Unlock mutex after function call
REENTRANCY_MUTEX_POSITION.setStorageBool(false);
}
}// Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol)
// and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143)
pragma solidity ^0.4.24;
import "../lib/token/ERC20.sol";
library SafeERC20 {
// Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`:
// https://github.com/ethereum/solidity/issues/3544
bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb;
string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED";
string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED";
function invokeAndCheckSuccess(address _addr, bytes memory _calldata)
private
returns (bool)
{
bool ret;
assembly {
let ptr := mload(0x40) // free memory pointer
let success := call(
gas, // forward all gas
_addr, // address
0, // no value
add(_calldata, 0x20), // calldata start
mload(_calldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
)
if gt(success, 0) {
// Check number of bytes returned from last function call
switch returndatasize
// No bytes returned: assume success
case 0 {
ret := 1
}
// 32 bytes returned: check if non-zero
case 0x20 {
// Only return success if returned data was true
// Already have output in ptr
ret := eq(mload(ptr), 1)
}
// Not sure what was returned: don't mark as success
default { }
}
}
return ret;
}
function staticInvoke(address _addr, bytes memory _calldata)
private
view
returns (bool, uint256)
{
bool success;
uint256 ret;
assembly {
let ptr := mload(0x40) // free memory pointer
success := staticcall(
gas, // forward all gas
_addr, // address
add(_calldata, 0x20), // calldata start
mload(_calldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
)
if gt(success, 0) {
ret := mload(ptr)
}
}
return (success, ret);
}
/**
* @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) {
bytes memory transferCallData = abi.encodeWithSelector(
TRANSFER_SELECTOR,
_to,
_amount
);
return invokeAndCheckSuccess(_token, transferCallData);
}
/**
* @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
bytes memory transferFromCallData = abi.encodeWithSelector(
_token.transferFrom.selector,
_from,
_to,
_amount
);
return invokeAndCheckSuccess(_token, transferFromCallData);
}
/**
* @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) {
bytes memory approveCallData = abi.encodeWithSelector(
_token.approve.selector,
_spender,
_amount
);
return invokeAndCheckSuccess(_token, approveCallData);
}
/**
* @dev Static call into ERC20.balanceOf().
* Reverts if the call fails for some reason (should never fail).
*/
function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) {
bytes memory balanceOfCallData = abi.encodeWithSelector(
_token.balanceOf.selector,
_owner
);
(bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData);
require(success, ERROR_TOKEN_BALANCE_REVERTED);
return tokenBalance;
}
/**
* @dev Static call into ERC20.allowance().
* Reverts if the call fails for some reason (should never fail).
*/
function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) {
bytes memory allowanceCallData = abi.encodeWithSelector(
_token.allowance.selector,
_owner,
_spender
);
(bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData);
require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
return allowance;
}
/**
* @dev Static call into ERC20.totalSupply().
* Reverts if the call fails for some reason (should never fail).
*/
function staticTotalSupply(ERC20 _token) internal view returns (uint256) {
bytes memory totalSupplyCallData = abi.encodeWithSelector(_token.totalSupply.selector);
(bool success, uint256 totalSupply) = staticInvoke(_token, totalSupplyCallData);
require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);
return totalSupply;
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./Uint256Helpers.sol";
contract TimeHelpers {
using Uint256Helpers for uint256;
/**
* @dev Returns the current block number.
* Using a function rather than `block.number` allows us to easily mock the block number in
* tests.
*/
function getBlockNumber() internal view returns (uint256) {
return block.number;
}
/**
* @dev Returns the current block number, converted to uint64.
* Using a function rather than `block.number` allows us to easily mock the block number in
* tests.
*/
function getBlockNumber64() internal view returns (uint64) {
return getBlockNumber().toUint64();
}
/**
* @dev Returns the current timestamp.
* Using a function rather than `block.timestamp` allows us to easily mock it in
* tests.
*/
function getTimestamp() internal view returns (uint256) {
return block.timestamp; // solium-disable-line security/no-block-members
}
/**
* @dev Returns the current timestamp, converted to uint64.
* Using a function rather than `block.timestamp` allows us to easily mock it in
* tests.
*/
function getTimestamp64() internal view returns (uint64) {
return getTimestamp().toUint64();
}
}pragma solidity ^0.4.24;
library Uint256Helpers {
uint256 private constant MAX_UINT64 = uint64(-1);
string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
function toUint64(uint256 a) internal pure returns (uint64) {
require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG);
return uint64(a);
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
library UnstructuredStorage {
function getStorageBool(bytes32 position) internal view returns (bool data) {
assembly { data := sload(position) }
}
function getStorageAddress(bytes32 position) internal view returns (address data) {
assembly { data := sload(position) }
}
function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) {
assembly { data := sload(position) }
}
function getStorageUint256(bytes32 position) internal view returns (uint256 data) {
assembly { data := sload(position) }
}
function setStorageBool(bytes32 position, bool data) internal {
assembly { sstore(position, data) }
}
function setStorageAddress(bytes32 position, address data) internal {
assembly { sstore(position, data) }
}
function setStorageBytes32(bytes32 position, bytes32 data) internal {
assembly { sstore(position, data) }
}
function setStorageUint256(bytes32 position, uint256 data) internal {
assembly { sstore(position, data) }
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "../lib/token/ERC20.sol";
import "./EtherTokenConstant.sol";
import "./IsContract.sol";
import "./IVaultRecoverable.sol";
import "./SafeERC20.sol";
contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
using SafeERC20 for ERC20;
string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED";
string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT";
string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED";
/**
* @notice Send funds to recovery Vault. This contract should never receive funds,
* but in case it does, this function allows one to recover them.
* @param _token Token balance to be sent to recovery vault.
*/
function transferToVault(address _token) external {
require(allowRecoverability(_token), ERROR_DISALLOWED);
address vault = getRecoveryVault();
require(isContract(vault), ERROR_VAULT_NOT_CONTRACT);
uint256 balance;
if (_token == ETH) {
balance = address(this).balance;
vault.transfer(balance);
} else {
ERC20 token = ERC20(_token);
balance = token.staticBalanceOf(this);
require(token.safeTransfer(vault, balance), ERROR_TOKEN_TRANSFER_FAILED);
}
emit RecoverToVault(vault, _token, balance);
}
/**
* @dev By default deriving from AragonApp makes it recoverable
* @param token Token address that would be recovered
* @return bool whether the app allows the recovery
*/
function allowRecoverability(address token) public view returns (bool) {
return true;
}
// Cast non-implemented interface to be public so we can use it internally
function getRecoveryVault() public view returns (address);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./IEVMScriptExecutor.sol";
import "./IEVMScriptRegistry.sol";
import "../apps/AppStorage.sol";
import "../kernel/KernelConstants.sol";
import "../common/Initializable.sol";
contract EVMScriptRunner is AppStorage, Initializable, EVMScriptRegistryConstants, KernelNamespaceConstants {
string private constant ERROR_EXECUTOR_UNAVAILABLE = "EVMRUN_EXECUTOR_UNAVAILABLE";
string private constant ERROR_PROTECTED_STATE_MODIFIED = "EVMRUN_PROTECTED_STATE_MODIFIED";
/* This is manually crafted in assembly
string private constant ERROR_EXECUTOR_INVALID_RETURN = "EVMRUN_EXECUTOR_INVALID_RETURN";
*/
event ScriptResult(address indexed executor, bytes script, bytes input, bytes returnData);
function getEVMScriptExecutor(bytes _script) public view returns (IEVMScriptExecutor) {
return IEVMScriptExecutor(getEVMScriptRegistry().getScriptExecutor(_script));
}
function getEVMScriptRegistry() public view returns (IEVMScriptRegistry) {
address registryAddr = kernel().getApp(KERNEL_APP_ADDR_NAMESPACE, EVMSCRIPT_REGISTRY_APP_ID);
return IEVMScriptRegistry(registryAddr);
}
function runScript(bytes _script, bytes _input, address[] _blacklist)
internal
isInitialized
protectState
returns (bytes)
{
IEVMScriptExecutor executor = getEVMScriptExecutor(_script);
require(address(executor) != address(0), ERROR_EXECUTOR_UNAVAILABLE);
bytes4 sig = executor.execScript.selector;
bytes memory data = abi.encodeWithSelector(sig, _script, _input, _blacklist);
bytes memory output;
assembly {
let success := delegatecall(
gas, // forward all gas
executor, // address
add(data, 0x20), // calldata start
mload(data), // calldata length
0, // don't write output (we'll handle this ourselves)
0 // don't write output
)
output := mload(0x40) // free mem ptr get
switch success
case 0 {
// If the call errored, forward its full error data
returndatacopy(output, 0, returndatasize)
revert(output, returndatasize)
}
default {
switch gt(returndatasize, 0x3f)
case 0 {
// Need at least 0x40 bytes returned for properly ABI-encoded bytes values,
// revert with "EVMRUN_EXECUTOR_INVALID_RETURN"
// See remix: doing a `revert("EVMRUN_EXECUTOR_INVALID_RETURN")` always results in
// this memory layout
mstore(output, 0x08c379a000000000000000000000000000000000000000000000000000000000) // error identifier
mstore(add(output, 0x04), 0x0000000000000000000000000000000000000000000000000000000000000020) // starting offset
mstore(add(output, 0x24), 0x000000000000000000000000000000000000000000000000000000000000001e) // reason length
mstore(add(output, 0x44), 0x45564d52554e5f4558454355544f525f494e56414c49445f52455455524e0000) // reason
revert(output, 100) // 100 = 4 + 3 * 32 (error identifier + 3 words for the ABI encoded error)
}
default {
// Copy result
//
// Needs to perform an ABI decode for the expected `bytes` return type of
// `executor.execScript()` as solidity will automatically ABI encode the returned bytes as:
// [ position of the first dynamic length return value = 0x20 (32 bytes) ]
// [ output length (32 bytes) ]
// [ output content (N bytes) ]
//
// Perform the ABI decode by ignoring the first 32 bytes of the return data
let copysize := sub(returndatasize, 0x20)
returndatacopy(output, 0x20, copysize)
mstore(0x40, add(output, copysize)) // free mem ptr set
}
}
}
emit ScriptResult(address(executor), _script, _input, output);
return output;
}
modifier protectState {
address preKernel = address(kernel());
bytes32 preAppId = appId();
_; // exec
require(address(kernel()) == preKernel, ERROR_PROTECTED_STATE_MODIFIED);
require(appId() == preAppId, ERROR_PROTECTED_STATE_MODIFIED);
}
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
interface IEVMScriptExecutor {
function execScript(bytes script, bytes input, address[] blacklist) external returns (bytes);
function executorType() external pure returns (bytes32);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "./IEVMScriptExecutor.sol";
contract EVMScriptRegistryConstants {
/* Hardcoded constants to save gas
bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = apmNamehash("evmreg");
*/
bytes32 internal constant EVMSCRIPT_REGISTRY_APP_ID = 0xddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd61;
}
interface IEVMScriptRegistry {
function addScriptExecutor(IEVMScriptExecutor executor) external returns (uint id);
function disableScriptExecutor(uint256 executorId) external;
// TODO: this should be external
// See https://github.com/ethereum/solidity/issues/4832
function getScriptExecutor(bytes script) public view returns (IEVMScriptExecutor);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
import "../acl/IACL.sol";
import "../common/IVaultRecoverable.sol";
interface IKernelEvents {
event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
}
// This should be an interface, but interfaces can't inherit yet :(
contract IKernel is IKernelEvents, IVaultRecoverable {
function acl() public view returns (IACL);
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);
function setApp(bytes32 namespace, bytes32 appId, address app) public;
function getApp(bytes32 namespace, bytes32 appId) public view returns (address);
}/*
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.4.24;
contract KernelAppIds {
/* Hardcoded constants to save gas
bytes32 internal constant KERNEL_CORE_APP_ID = apmNamehash("kernel");
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = apmNamehash("acl");
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = apmNamehash("vault");
*/
bytes32 internal constant KERNEL_CORE_APP_ID = 0x3b4bf6bf3ad5000ecf0f989d5befde585c6860fea3e574a4fab4c49d1c177d9c;
bytes32 internal constant KERNEL_DEFAULT_ACL_APP_ID = 0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a;
bytes32 internal constant KERNEL_DEFAULT_VAULT_APP_ID = 0x7e852e0fcfce6551c13800f1e7476f982525c2b5277ba14b24339c68416336d1;
}
contract KernelNamespaceConstants {
/* Hardcoded constants to save gas
bytes32 internal constant KERNEL_CORE_NAMESPACE = keccak256("core");
bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = keccak256("base");
bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = keccak256("app");
*/
bytes32 internal constant KERNEL_CORE_NAMESPACE = 0xc681a85306374a5ab27f0bbc385296a54bcd314a1948b6cf61c4ea1bc44bb9f8;
bytes32 internal constant KERNEL_APP_BASES_NAMESPACE = 0xf1f3eb40f5bc1ad1344716ced8b8a0431d840b5783aea1fd01786bc26f35ac0f;
bytes32 internal constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
}// See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol
// Adapted to use pragma ^0.4.24 and satisfy our linter rules
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW";
string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW";
string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW";
string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO";
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
// 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-solidity/pull/522
if (_a == 0) {
return 0;
}
uint256 c = _a * _b;
require(c / _a == _b, ERROR_MUL_OVERFLOW);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0
uint256 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b <= _a, ERROR_SUB_UNDERFLOW);
uint256 c = _a - _b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
uint256 c = _a + _b;
require(c >= _a, ERROR_ADD_OVERFLOW);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, ERROR_DIV_ZERO);
return a % b;
}
}// See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/d51e38758e1d985661534534d5c61e27bece5042/contracts/math/SafeMath.sol
// Adapted for uint64, pragma ^0.4.24, and satisfying our linter rules
// Also optimized the mul() implementation, see https://github.com/aragon/aragonOS/pull/417
pragma solidity ^0.4.24;
/**
* @title SafeMath64
* @dev Math operations for uint64 with safety checks that revert on error
*/
library SafeMath64 {
string private constant ERROR_ADD_OVERFLOW = "MATH64_ADD_OVERFLOW";
string private constant ERROR_SUB_UNDERFLOW = "MATH64_SUB_UNDERFLOW";
string private constant ERROR_MUL_OVERFLOW = "MATH64_MUL_OVERFLOW";
string private constant ERROR_DIV_ZERO = "MATH64_DIV_ZERO";
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint64 _a, uint64 _b) internal pure returns (uint64) {
uint256 c = uint256(_a) * uint256(_b);
require(c < 0x010000000000000000, ERROR_MUL_OVERFLOW); // 2**64 (less gas this way)
return uint64(c);
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint64 _a, uint64 _b) internal pure returns (uint64) {
require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0
uint64 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint64 _a, uint64 _b) internal pure returns (uint64) {
require(_b <= _a, ERROR_SUB_UNDERFLOW);
uint64 c = _a - _b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint64 _a, uint64 _b) internal pure returns (uint64) {
uint64 c = _a + _b;
require(c >= _a, ERROR_ADD_OVERFLOW);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint64 a, uint64 b) internal pure returns (uint64) {
require(b != 0, ERROR_DIV_ZERO);
return a % b;
}
}// See https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.4.24;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 {
function totalSupply() public view returns (uint256);
function balanceOf(address _who) public view returns (uint256);
function allowance(address _owner, address _spender)
public view returns (uint256);
function transfer(address _to, uint256 _value) public returns (bool);
function approve(address _spender, uint256 _value)
public returns (bool);
function transferFrom(address _from, address _to, uint256 _value)
public returns (bool);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: MIT // Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol // See contracts/COMPILERS.md // solhint-disable-next-line pragma solidity ^0.4.24; import {SafeMath} from "@aragon/os/contracts/lib/math/SafeMath.sol"; /// @notice Provides an interface for gas-efficient operations on four uint64 type /// variables tightly packed into one uint256 variable stored in memory library Packed64x4 { using SafeMath for uint256; using Packed64x4 for Packed64x4.Packed; uint256 internal constant UINT64_MAX = 0xFFFFFFFFFFFFFFFF; struct Packed { uint256 v; } /// @dev Returns uint64 variable stored on position `n` as uint256 function get(Packed memory _self, uint8 n) internal pure returns (uint256 r) { r = (_self.v >> (64 * n)) & UINT64_MAX; } /// @dev Writes value stored in passed `x` variable on position `n`. /// The passed value must be less or equal to UINT64_MAX. /// If the passed value exceeds UINT64_MAX method will /// revert with a "PACKED_OVERFLOW" error message function set(Packed memory _self, uint8 n, uint256 x) internal pure { require(x <= UINT64_MAX, "PACKED_OVERFLOW"); _self.v = _self.v & ~(UINT64_MAX << (64 * n)) | ((x & UINT64_MAX) << (64 * n)); } /// @dev Adds value stored in passed `x` variable to variable stored on position `n` /// using SafeMath lib function add(Packed memory _self, uint8 n, uint256 x) internal pure { set(_self, n, get(_self, n).add(x)); } /// @dev Subtract value stored in passed `x` variable from variable stored on position `n` /// using SafeMath lib function sub(Packed memory _self, uint8 n, uint256 x) internal pure { set(_self, n, get(_self, n).sub(x)); } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md pragma solidity 0.4.24; import {SafeMath} from "@aragon/os/contracts/lib/math/SafeMath.sol"; import {SafeMath64} from "@aragon/os/contracts/lib/math/SafeMath64.sol"; /// @title Library for manage operator keys in storage /// @author KRogLA library SigningKeys { using SafeMath for uint256; using SafeMath64 for uint64; using SigningKeys for bytes32; uint64 internal constant PUBKEY_LENGTH = 48; uint64 internal constant SIGNATURE_LENGTH = 96; uint256 internal constant UINT64_MAX = 0xFFFFFFFFFFFFFFFF; event SigningKeyAdded(uint256 indexed nodeOperatorId, bytes pubkey); event SigningKeyRemoved(uint256 indexed nodeOperatorId, bytes pubkey); function getKeyOffset(bytes32 _position, uint256 _nodeOperatorId, uint256 _keyIndex) internal pure returns (uint256) { return uint256(keccak256(abi.encodePacked(_position, _nodeOperatorId, _keyIndex))); } /// @dev store opeartor keys to storage /// @param _position storage slot /// @param _nodeOperatorId operator id /// @param _startIndex start index /// @param _keysCount keys count to load /// @param _pubkeys kes buffer to read from /// @param _signatures signatures buffer to read from /// @return new total keys count function saveKeysSigs( bytes32 _position, uint256 _nodeOperatorId, uint256 _startIndex, uint256 _keysCount, bytes _pubkeys, bytes _signatures ) internal returns (uint256) { require(_keysCount > 0 && _startIndex.add(_keysCount) <= UINT64_MAX, "INVALID_KEYS_COUNT"); require( _pubkeys.length == _keysCount.mul(PUBKEY_LENGTH) && _signatures.length == _keysCount.mul(SIGNATURE_LENGTH), "LENGTH_MISMATCH" ); uint256 curOffset; bool isEmpty; bytes memory tmpKey = new bytes(48); for (uint256 i; i < _keysCount;) { curOffset = _position.getKeyOffset(_nodeOperatorId, _startIndex); assembly { let _ofs := add(add(_pubkeys, 0x20), mul(i, 48)) //PUBKEY_LENGTH = 48 let _part1 := mload(_ofs) // bytes 0..31 let _part2 := mload(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 } require(!isEmpty, "EMPTY_KEY"); 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(add(_signatures, 0x20), mul(i, 96)) //SIGNATURE_LENGTH = 96 sstore(add(curOffset, 2), mload(_ofs)) sstore(add(curOffset, 3), mload(add(_ofs, 0x20))) sstore(add(curOffset, 4), mload(add(_ofs, 0x40))) i := add(i, 1) _startIndex := add(_startIndex, 1) } emit SigningKeyAdded(_nodeOperatorId, tmpKey); } return _startIndex; } /// @dev remove opeartor keys from storage /// @param _position storage slot /// @param _nodeOperatorId operator id /// @param _startIndex start index /// @param _keysCount keys count to load /// @param _totalKeysCount current total keys count for operator /// @return new _totalKeysCount function removeKeysSigs( bytes32 _position, uint256 _nodeOperatorId, uint256 _startIndex, uint256 _keysCount, uint256 _totalKeysCount ) internal returns (uint256) { require( _keysCount > 0 && _startIndex.add(_keysCount) <= _totalKeysCount && _totalKeysCount <= UINT64_MAX, "INVALID_KEYS_COUNT" ); uint256 curOffset; uint256 lastOffset; uint256 j; bytes memory tmpKey = new bytes(48); // removing from the last index for (uint256 i = _startIndex + _keysCount; i > _startIndex;) { curOffset = _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 = _position.getKeyOffset(_nodeOperatorId, _totalKeysCount - 1); // move last key to deleted key index for (j = 0; j < 5;) { 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 SigningKeyRemoved(_nodeOperatorId, tmpKey); } return _totalKeysCount; } /// @dev laod opeartor keys from storage /// @param _position storage slot /// @param _nodeOperatorId operator id /// @param _startIndex start index /// @param _keysCount keys count to load /// @param _pubkeys preallocated kes 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( bytes32 _position, uint256 _nodeOperatorId, uint256 _startIndex, uint256 _keysCount, bytes memory _pubkeys, bytes memory _signatures, uint256 _bufOffset ) internal view { uint256 curOffset; for (uint256 i; i < _keysCount;) { curOffset = _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 initKeysSigsBuf(uint256 _count) internal pure returns (bytes memory, bytes memory) { return (new bytes(_count.mul(PUBKEY_LENGTH)), new bytes(_count.mul(SIGNATURE_LENGTH))); } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.4.24; import "@aragon/os/contracts/common/UnstructuredStorage.sol"; /** * @title Adapted code of /contracts/0.8.9/utils/Versioned.sol * * This contract contains only core part of original Versioned.sol * to reduce contract size */ contract Versioned { using UnstructuredStorage for bytes32; event ContractVersionSet(uint256 version); /// @dev Storage slot: uint256 version /// Version of the initialized contract storage. /// The version stored in CONTRACT_VERSION_POSITION equals to: /// - 0 right after the deployment, before an initializer is invoked (and only at that moment); /// - N after calling initialize(), where N is the initially deployed contract version; /// - N after upgrading contract by calling finalizeUpgrade_vN(). bytes32 internal constant CONTRACT_VERSION_POSITION = 0x4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6; // keccak256("lido.Versioned.contractVersion"); uint256 internal constant PETRIFIED_VERSION_MARK = uint256(-1); constructor() public { // lock version in the implementation's storage to prevent initialization CONTRACT_VERSION_POSITION.setStorageUint256(PETRIFIED_VERSION_MARK); } /// @notice Returns the current contract version. function getContractVersion() public view returns (uint256) { return CONTRACT_VERSION_POSITION.getStorageUint256(); } function _checkContractVersion(uint256 version) internal view { require(version == getContractVersion(), "UNEXPECTED_CONTRACT_VERSION"); } function _setContractVersion(uint256 version) internal { CONTRACT_VERSION_POSITION.setStorageUint256(version); emit ContractVersionSet(version); } }
// SPDX-FileCopyrightText: 2025 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md // solhint-disable-next-line lido/fixed-compiler-version pragma solidity >=0.4.24 <0.9.0; interface ILidoLocator { function accountingOracle() external view returns(address); function depositSecurityModule() external view returns(address); function elRewardsVault() external view returns(address); function lido() external view returns(address); function oracleReportSanityChecker() external view returns(address); function burner() external view returns(address); function stakingRouter() external view returns(address); function treasury() external view returns(address); function validatorsExitBusOracle() external view returns(address); function withdrawalQueue() external view returns(address); function withdrawalVault() external view returns(address); function postTokenRebaseReceiver() external view returns(address); function oracleDaemonConfig() external view returns(address); function validatorExitDelayVerifier() external view returns (address); function triggerableWithdrawalsGateway() external view returns (address); function accounting() external view returns (address); function predepositGuarantee() external view returns (address); function wstETH() external view returns (address); function vaultHub() external view returns (address); function vaultFactory() external view returns (address); function lazyOracle() external view returns (address); function operatorGrid() external view returns (address); /// @notice Returns core Lido protocol component addresses in a single call /// @dev This function provides a gas-efficient way to fetch multiple component addresses in a single call function coreComponents() external view returns( address elRewardsVault, address oracleReportSanityChecker, address stakingRouter, address treasury, address withdrawalQueue, address withdrawalVault ); /// @notice Returns addresses of components involved in processing oracle reports in the Lido contract /// @dev This function provides a gas-efficient way to fetch multiple component addresses in a single call function oracleReportComponents() external view returns( address accountingOracle, address oracleReportSanityChecker, address burner, address withdrawalQueue, address postTokenRebaseReceiver, address stakingRouter, address vaultHub ); }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: MIT // Copied from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0457042d93d9dfd760dbaa06a4d2f1216fdbe297/contracts/utils/math/Math.sol // See contracts/COMPILERS.md // solhint-disable-next-line pragma solidity >=0.4.24 <0.9.0; library Math256 { /// @dev Returns the largest of two numbers. function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /// @dev Returns the smallest of two numbers. function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /// @dev Returns the largest of two numbers. function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /// @dev Returns the smallest of two numbers. function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /// @dev Returns the ceiling of the division of two numbers. /// /// This differs from standard division with `/` in that it rounds up instead /// of rounding down. function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /// @dev Returns absolute difference of two numbers. function absDiff(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a - b : b - a; } }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 /* See contracts/COMPILERS.md */ // solhint-disable-next-line pragma solidity >=0.4.24 <0.9.0; import {Math256} from "./Math256.sol"; /// @notice Library with methods to calculate "proportional" allocations among buckets with different /// capacity and level of filling. /// @dev The current implementation favors buckets with the least fill factor library MinFirstAllocationStrategy { uint256 private constant MAX_UINT256 = 2**256 - 1; /// @notice Allocates passed maxAllocationSize among the buckets. The resulting allocation doesn't exceed the /// capacities of the buckets. An algorithm starts filling from the least populated buckets to equalize the fill factor. /// For example, for buckets: [9998, 70, 0], capacities: [10000, 101, 100], and maxAllocationSize: 101, the allocation happens /// following way: /// 1. top up the bucket with index 2 on 70. Intermediate state of the buckets: [9998, 70, 70]. According to the definition, /// the rest allocation must be proportionally split among the buckets with the same values. /// 2. top up the bucket with index 1 on 15. Intermediate state of the buckets: [9998, 85, 70]. /// 3. top up the bucket with index 2 on 15. Intermediate state of the buckets: [9998, 85, 85]. /// 4. top up the bucket with index 1 on 1. Nothing to distribute. The final state of the buckets: [9998, 86, 85] /// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation. /// @param buckets The array of current allocations in the buckets /// @param capacities The array of capacities of the buckets /// @param allocationSize The desired value to allocate among the buckets /// @return allocated The total value allocated among the buckets. Can't exceed the allocationSize value function allocate( uint256[] memory buckets, uint256[] memory capacities, uint256 allocationSize ) public pure returns (uint256 allocated, uint256[] memory) { uint256 allocatedToBestCandidate = 0; while (allocated < allocationSize) { allocatedToBestCandidate = allocateToBestCandidate(buckets, capacities, allocationSize - allocated); if (allocatedToBestCandidate == 0) { break; } allocated += allocatedToBestCandidate; } return (allocated, buckets); } /// @notice Allocates the max allowed value not exceeding allocationSize to the bucket with the least value. /// The candidate search happens according to the following algorithm: /// 1. Find the first least filled bucket which has free space. Count the number of such buckets. /// 2. If no buckets are found terminate the search - no free buckets /// 3. Find the first bucket with free space, which has the least value greater /// than the bucket found in step 1. To preserve proportional allocation the resulting allocation can't exceed this value. /// 4. Calculate the allocation size as: /// min( /// (count of least filling buckets > 1 ? ceilDiv(allocationSize, count of least filling buckets) : allocationSize), /// fill factor of the bucket found in step 3, /// free space of the least filled bucket /// ) /// @dev Method modifies the passed buckets array to reduce the gas costs on memory allocation. /// @param buckets The array of current allocations in the buckets /// @param capacities The array of capacities of the buckets /// @param allocationSize The desired value to allocate to the bucket /// @return allocated The total value allocated to the bucket. Can't exceed the allocationSize value function allocateToBestCandidate( uint256[] memory buckets, uint256[] memory capacities, uint256 allocationSize ) internal pure returns (uint256 allocated) { uint256 bestCandidateIndex = buckets.length; uint256 bestCandidateAllocation = MAX_UINT256; uint256 bestCandidatesCount = 0; if (allocationSize == 0) { return 0; } for (uint256 i = 0; i < buckets.length; ++i) { if (buckets[i] >= capacities[i]) { continue; } else if (bestCandidateAllocation > buckets[i]) { bestCandidateIndex = i; bestCandidatesCount = 1; bestCandidateAllocation = buckets[i]; } else if (bestCandidateAllocation == buckets[i]) { bestCandidatesCount += 1; } } if (bestCandidatesCount == 0) { return 0; } // cap the allocation by the smallest larger allocation than the found best one uint256 allocationSizeUpperBound = MAX_UINT256; for (uint256 j = 0; j < buckets.length; ++j) { if (buckets[j] >= capacities[j]) { continue; } else if (buckets[j] > bestCandidateAllocation && buckets[j] < allocationSizeUpperBound) { allocationSizeUpperBound = buckets[j]; } } allocated = Math256.min( bestCandidatesCount > 1 ? Math256.ceilDiv(allocationSize, bestCandidatesCount) : allocationSize, Math256.min(allocationSizeUpperBound, capacities[bestCandidateIndex]) - bestCandidateAllocation ); buckets[bestCandidateIndex] += allocated; } }
{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "constantinople",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/common/lib/MinFirstAllocationStrategy.sol": {
"MinFirstAllocationStrategy": "0x4a08c1501a886861c17341317ff7885a5a1e5db6"
}
}
}Contract ABI
API[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_targetLimitMode","type":"uint256"},{"name":"_targetLimit","type":"uint256"}],"name":"updateTargetValidatorsLimits","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_keysCount","type":"uint256"},{"name":"_publicKeys","type":"bytes"},{"name":"_signatures","type":"bytes"}],"name":"addSigningKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getType","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"exitDeadlineThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_script","type":"bytes"}],"name":"getEVMScriptExecutor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRecoveryVault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_offset","type":"uint256"},{"name":"_limit","type":"uint256"}],"name":"getNodeOperatorIds","outputs":[{"name":"nodeOperatorIds","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_proofSlotTimestamp","type":"uint256"},{"name":"_publicKey","type":"bytes"},{"name":"_eligibleToExitInSec","type":"uint256"}],"name":"reportValidatorExitDelay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_offset","type":"uint256"},{"name":"_limit","type":"uint256"}],"name":"getSigningKeys","outputs":[{"name":"pubkeys","type":"bytes"},{"name":"signatures","type":"bytes"},{"name":"used","type":"bool[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fromIndex","type":"uint256"},{"name":"_keysCount","type":"uint256"}],"name":"removeSigningKeysOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorIsActive","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_name","type":"string"}],"name":"setNodeOperatorName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_totalRewardShares","type":"uint256"}],"name":"getRewardsDistribution","outputs":[{"name":"recipients","type":"address[]"},{"name":"shares","type":"uint256[]"},{"name":"penalized","type":"bool[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_indexFrom","type":"uint256"},{"name":"_indexTo","type":"uint256"}],"name":"invalidateReadyToDepositKeysRange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_locator","type":"address"},{"name":"_type","type":"bytes32"},{"name":"_exitDeadlineThresholdInSeconds","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_publicKey","type":"bytes"},{"name":"_withdrawalRequestPaidFee","type":"uint256"},{"name":"_exitType","type":"uint256"}],"name":"onValidatorExitTriggered","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getStuckPenaltyDelay","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"removeSigningKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getRewardDistributionState","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fromIndex","type":"uint256"},{"name":"_keysCount","type":"uint256"}],"name":"removeSigningKeys","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"isOperatorPenalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"deactivateNodeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"}],"name":"allowRecoverability","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"STAKING_ROUTER_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_keysCount","type":"uint256"},{"name":"_publicKeys","type":"bytes"},{"name":"_signatures","type":"bytes"}],"name":"addSigningKeysOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"appId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"},{"name":"_proofSlotTimestamp","type":"uint256"},{"name":"_publicKey","type":"bytes"},{"name":"_eligibleToExitInSec","type":"uint256"}],"name":"isValidatorExitDelayPenaltyApplicable","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getActiveNodeOperatorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_rewardAddress","type":"address"}],"name":"addNodeOperator","outputs":[{"name":"id","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getContractVersion","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInitializationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getUnusedSigningKeyCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"","type":"uint256"}],"name":"onRewardsMinted","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MANAGE_NODE_OPERATOR_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_exitDeadlineThresholdInSeconds","type":"uint256"}],"name":"finalizeUpgrade_v4","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"distributeReward","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"onWithdrawalCredentialsChanged","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"activateNodeOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_exitedValidatorsCount","type":"uint256"}],"name":"unsafeUpdateValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_rewardAddress","type":"address"}],"name":"setNodeOperatorRewardAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_fullInfo","type":"bool"}],"name":"getNodeOperator","outputs":[{"name":"active","type":"bool"},{"name":"name","type":"string"},{"name":"rewardAddress","type":"address"},{"name":"totalVettedValidators","type":"uint64"},{"name":"totalExitedValidators","type":"uint64"},{"name":"totalAddedValidators","type":"uint64"},{"name":"totalDepositedValidators","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStakingModuleSummary","outputs":[{"name":"totalExitedValidators","type":"uint256"},{"name":"totalDepositedValidators","type":"uint256"},{"name":"depositableValidatorsCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorIds","type":"bytes"},{"name":"_exitedValidatorsCounts","type":"bytes"}],"name":"updateExitedValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"","type":"address"}],"name":"transferToVault","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_sender","type":"address"},{"name":"_role","type":"bytes32"},{"name":"_params","type":"uint256[]"}],"name":"canPerform","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getEVMScriptRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNodeOperatorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_isTargetLimitActive","type":"bool"},{"name":"_targetLimit","type":"uint256"}],"name":"updateTargetValidatorsLimits","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_vettedSigningKeysCount","type":"uint64"}],"name":"setNodeOperatorStakingLimit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getNodeOperatorSummary","outputs":[{"name":"targetLimitMode","type":"uint256"},{"name":"targetValidatorsCount","type":"uint256"},{"name":"stuckValidatorsCount","type":"uint256"},{"name":"refundedValidatorsCount","type":"uint256"},{"name":"stuckPenaltyEndTimestamp","type":"uint256"},{"name":"totalExitedValidators","type":"uint256"},{"name":"totalDepositedValidators","type":"uint256"},{"name":"depositableValidatorsCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"getSigningKey","outputs":[{"name":"key","type":"bytes"},{"name":"depositSignature","type":"bytes"},{"name":"used","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_NODE_OPERATOR_NAME_LENGTH","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorIds","type":"bytes"},{"name":"_vettedSigningKeysCounts","type":"bytes"}],"name":"decreaseVettedSigningKeysCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_publicKey","type":"bytes"}],"name":"isValidatorExitingKeyReported","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_lateReportingWindow","type":"uint256"}],"name":"setExitDeadlineThreshold","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_depositsCount","type":"uint256"},{"name":"","type":"bytes"}],"name":"obtainDepositData","outputs":[{"name":"publicKeys","type":"bytes"},{"name":"signatures","type":"bytes"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"exitPenaltyCutoffTimestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getKeysOpIndex","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kernel","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLocator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"SET_NODE_OPERATOR_LIMIT_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"getTotalSigningKeyCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isPetrified","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_STUCK_PENALTY_DELAY","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"onExitedAndStuckValidatorsCountsUpdated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_NODE_OPERATORS_COUNT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"removeSigningKeyOperatorBH","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MANAGE_SIGNING_KEYS","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"isOperatorPenaltyCleared","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"rewardAddress","type":"address"},{"indexed":false,"name":"stakingLimit","type":"uint64"}],"name":"NodeOperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"NodeOperatorActiveSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"name","type":"string"}],"name":"NodeOperatorNameSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"rewardAddress","type":"address"}],"name":"NodeOperatorRewardAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"totalKeysTrimmed","type":"uint64"}],"name":"NodeOperatorTotalKeysTrimmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"keysOpIndex","type":"uint256"}],"name":"KeysOpIndexSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"moduleType","type":"bytes32"}],"name":"StakingModuleTypeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"rewardAddress","type":"address"},{"indexed":false,"name":"sharesAmount","type":"uint256"}],"name":"RewardsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"state","type":"uint8"}],"name":"RewardDistributionStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"locatorAddress","type":"address"}],"name":"LocatorContractSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"approvedValidatorsCount","type":"uint256"}],"name":"VettedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"depositedValidatorsCount","type":"uint256"}],"name":"DepositedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"exitedValidatorsCount","type":"uint256"}],"name":"ExitedSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"totalValidatorsCount","type":"uint256"}],"name":"TotalSigningKeysCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"nonce","type":"uint256"}],"name":"NonceChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"targetValidatorsCount","type":"uint256"},{"indexed":false,"name":"targetLimitMode","type":"uint256"}],"name":"TargetValidatorsCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"publicKey","type":"bytes"},{"indexed":false,"name":"eligibleToExitInSec","type":"uint256"},{"indexed":false,"name":"proofSlotTimestamp","type":"uint256"}],"name":"ValidatorExitStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"publicKey","type":"bytes"},{"indexed":false,"name":"withdrawalRequestPaidFee","type":"uint256"},{"indexed":false,"name":"exitType","type":"uint256"}],"name":"ValidatorExitTriggered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"threshold","type":"uint256"},{"indexed":false,"name":"reportingWindow","type":"uint256"}],"name":"ExitDeadlineThresholdChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"version","type":"uint256"}],"name":"ContractVersionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"executor","type":"address"},{"indexed":false,"name":"script","type":"bytes"},{"indexed":false,"name":"input","type":"bytes"},{"indexed":false,"name":"returnData","type":"bytes"}],"name":"ScriptResult","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"vault","type":"address"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"RecoverToVault","type":"event"}]Contract Creation Code
6080604052620000146200005460201b60201c565b6200004e7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a660001962000156602090811b62003c2117901c565b6200026b565b620000646200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a454400000000000000006020820152901562000140576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101562000104578181015183820152602001620000ea565b50505050905090810190601f168015620001325780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50620001546000196200018d60201b60201c565b565b9055565b600062000188600080516020620057bd83398151915260001b600019166200026760201b620032f71760201c565b905090565b6200019d6200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156200023c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360008381101562000104578181015183820152602001620000ea565b5062000264600080516020620057bd8339815191528262000156602090811b62003c2117901c565b50565b5490565b615542806200027b6000396000f3006080604052600436106103265760003560e01c63ffffffff1680630803fac01461032b57806308a679ad14610354578063096b7b351461037457806315dae03e146103a857806328d6d36b146103cf5780632914b9bd146103e757806332f0a3b51461045c5780634febc81b1461047157806357f9c341146104dc57806359e25c12146105075780635ddde810146106445780635e2fb908146106625780635e57d7421461067a57806362dcfda11461069e57806365cc369a1461077b578063684560a214610796578063693cc600146107bd5780636da7d0a7146107e75780636ef355f1146107fc5780636f817294146108175780637038b1411461064457806375049ad81461085057806375a080d5146108685780637e7db6e11461088057806380231f15146108a1578063805911ae1461037457806380afdea8146108b657806383b57a4e146108cb5780638469cbd3146108f657806385fa63d71461090b5780638aa10435146109395780638b3dd7491461094e5780638ca7c052146109635780638d7e40171461097b5780638ece9995146109935780638ee1c0a8146109a85780638f73c5ae146109c057806390c09bdb146109d557806391dcd6b2146109ea5780639412036814610a02578063973e932814610a1d5780639a56983c14610a415780639abddf0914610b1d5780639b00c14614610b505780639d4941d814610b7c578063a1658fad14610b9d578063a479e50814610c04578063a70c70e414610c19578063a9e7a84614610c2e578063ae962acf14610c4e578063b3076c3c14610c73578063b449402a14610ccc578063b497183314610dd0578063b643189b14610de5578063ba2406fd14610e11578063be1575d314610e6a578063bee41b5814610e85578063cfe5871214610f87578063d07442f114610f9c578063d087d28814610f9c578063d4aae0c414610fb1578063d8343dcb14610fc6578063d8e71cd114610fdb578063db9887ea14610ff0578063de4796ed14611008578063e204d09b1461101d578063e864299e14611032578063ec5af3a414611047578063ed5cfa41146107fc578063f31bd9c11461105c578063fbc77ef114611071575b600080fd5b34801561033757600080fd5b50610340611089565b604080519115158252519081900360200190f35b34801561036057600080fd5b506103726004356024356044356110b3565b005b34801561038057600080fd5b5061037260048035906024803591604435808301929082013591606435918201910135611185565b3480156103b457600080fd5b506103bd6111f6565b60408051918252519081900360200190f35b3480156103db57600080fd5b506103bd600435611227565b3480156103f357600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526104409436949293602493928401919081908401838280828437509497506112379650505050505050565b60408051600160a060020a039092168252519081900360200190f35b34801561046857600080fd5b5061044061131a565b34801561047d57600080fd5b5061048c60043560243561138f565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156104c85781810151838201526020016104b0565b505050509050019250505060405180910390f35b3480156104e857600080fd5b5061037260048035906024803591604435918201910135606435611426565b34801561051357600080fd5b5061052560043560243560443561161c565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b8381101561056a578181015183820152602001610552565b50505050905090810190601f1680156105975780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b838110156105ca5781810151838201526020016105b2565b50505050905090810190601f1680156105f75780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561062c578181015183820152602001610614565b50505050905001965050505050505060405180910390f35b34801561065057600080fd5b5061037260043560243560443561171a565b34801561066e57600080fd5b5061034060043561172a565b34801561068657600080fd5b5061037260048035906024803590810191013561173f565b3480156106aa57600080fd5b506106b66004356118b7565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156106fe5781810151838201526020016106e6565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561073d578181015183820152602001610725565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561062c578181015183820152602001610614565b34801561078757600080fd5b50610372600435602435611ac2565b3480156107a257600080fd5b50610372600160a060020a0360043516602435604435611ae7565b3480156107c957600080fd5b50610372600480359060248035908101910135604435606435611bd4565b3480156107f357600080fd5b506103bd611c53565b34801561080857600080fd5b50610372600435602435611c58565b34801561082357600080fd5b5061082c611c64565b6040518082600281111561083c57fe5b60ff16815260200191505060405180910390f35b34801561085c57600080fd5b50610340600435611c9e565b34801561087457600080fd5b50610372600435611ca4565b34801561088c57600080fd5b50610340600160a060020a0360043516611df4565b3480156108ad57600080fd5b506103bd611dfa565b3480156108c257600080fd5b506103bd611e0c565b3480156108d757600080fd5b5061034060048035906024803591604435918201910135606435611e37565b34801561090257600080fd5b506103bd611eab565b34801561091757600080fd5b506103bd6024600480358281019291013590600160a060020a03903516611ec4565b34801561094557600080fd5b506103bd6120b5565b34801561095a57600080fd5b506103bd6120e0565b34801561096f57600080fd5b506103bd60043561210b565b34801561098757600080fd5b50610372600435612161565b34801561099f57600080fd5b506103bd612185565b3480156109b457600080fd5b50610372600435612197565b3480156109cc57600080fd5b5061037261236c565b3480156109e157600080fd5b506103726123e8565b3480156109f657600080fd5b50610372600435612422565b348015610a0e57600080fd5b506103726004356024356124d5565b348015610a2957600080fd5b50610372600435600160a060020a0360243516612509565b348015610a4d57600080fd5b50610a5e60043560243515156125d5565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610adc578181015183820152602001610ac4565b50505050905090810190601f168015610b095780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b348015610b2957600080fd5b50610b32612728565b60408051938452602084019290925282820152519081900360600190f35b348015610b5c57600080fd5b506103726024600480358281019290820135918135918201910135612782565b348015610b8857600080fd5b50610372600160a060020a036004351661281b565b348015610ba957600080fd5b506040805160206004604435818101358381028086018501909652808552610340958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061286b9650505050505050565b348015610c1057600080fd5b506104406129b8565b348015610c2557600080fd5b506103bd612a6d565b348015610c3a57600080fd5b506103726004356024351515604435612a98565b348015610c5a57600080fd5b5061037260043567ffffffffffffffff60243516612ab4565b348015610c7f57600080fd5b50610c8b600435612b1c565b604080519889526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610cd857600080fd5b50610ce7600435602435612b97565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610d32578181015183820152602001610d1a565b50505050905090810190601f168015610d5f5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610d92578181015183820152602001610d7a565b50505050905090810190601f168015610dbf5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610ddc57600080fd5b506103bd612bd5565b348015610df157600080fd5b506103726024600480358281019290820135918135918201910135612bda565b348015610e1d57600080fd5b506040805160206004803580820135601f8101849004840285018401909552848452610340943694929360249392840191908190840183828082843750949750612c5e9650505050505050565b348015610e7657600080fd5b50610372600435602435612cd5565b348015610e9157600080fd5b50610ea9600480359060248035908101910135612cf6565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610eea578181015183820152602001610ed2565b50505050905090810190601f168015610f175780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610f4a578181015183820152602001610f32565b50505050905090810190601f168015610f775780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610f9357600080fd5b506103bd612dc4565b348015610fa857600080fd5b506103bd612e11565b348015610fbd57600080fd5b50610440612e3c565b348015610fd257600080fd5b50610440612e67565b348015610fe757600080fd5b506103bd612e92565b348015610ffc57600080fd5b506103bd600435612eb6565b34801561101457600080fd5b50610340612ee5565b34801561102957600080fd5b506103bd612ef8565b34801561103e57600080fd5b50610372612f00565b34801561105357600080fd5b506103bd612f23565b34801561106857600080fd5b506103bd612f28565b34801561107d57600080fd5b50610340600435611df4565b6000806110946120e0565b905080158015906110ac5750806110a9612f4c565b10155b91505b5090565b6110bb6153d6565b6110c484612f50565b6110db6000805160206154b7833981519152612f62565b6110ef67ffffffffffffffff831115612fa0565b6110f884612ff7565b905061110c8160008563ffffffff61302316565b82151561111857600091505b61112a8160018463ffffffff61302316565b61113484826130aa565b6040805183815260208101859052815186927ff92eb109ce5b449e9b121c352c6aeb4319538a90738cb95d84f08e41274e92d2928290030190a2611177846130c3565b61117f61313a565b50505050565b6111ee868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750613203945050505050565b505050505050565b60006112217fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d06132f7565b90505b90565b60006112316132fb565b92915050565b60006112416129b8565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561129c578181015183820152602001611284565b50505050905090810190601f1680156112c95780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b1580156112e857600080fd5b505af11580156112fc573d6000803e3d6000fd5b505050506040513d602081101561131257600080fd5b505192915050565b6000611324612e3c565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561135e57600080fd5b505af1158015611372573d6000803e3d6000fd5b505050506040513d602081101561138857600080fd5b5051905090565b606060008061139c612a6d565b915081851015806113ab575083155b156113b55761141e565b6113c184868403613339565b6040519080825280602002602001820160405280156113ea578160200160208202803883390190505b509250600090505b825181101561141e57808501838281518110151561140c57fe5b602090810290910101526001016113f2565b505092915050565b600061143f6000805160206154b7833981519152612f62565b60308314611497576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f5055424c49435f4b45590000000000000000000000000000604482015290519081900360640190fd5b61149f6132fb565b8210156114f6576040805160e560020a62461bcd02815260206004820152601a60248201527f455849545f44454c41595f42454c4f575f5448524553484f4c44000000000000604482015290519081900360640190fd5b6114fe612dc4565b8286031015611557576040805160e560020a62461bcd02815260206004820152601e60248201527f544f4f5f4c4154455f464f525f455849545f44454c41595f5245504f52540000604482015290519081900360640190fd5b838360405180838380828437604080519190930181900390206000818152600260205292909220549195505060ff16159250611595915050576111ee565b600081815260026020908152604091829020805460ff1916600117905581519081018490529081018690526060808252810184905286907f7f781065728a5d3f9c1ee91ef0c0b8a9455ebc228dae6d35514b32c7370349c9908690869086908a908060808101868680828437604051920182900397509095505050505050a2505050505050565b60608060606116296153d6565b60008061163589612f50565b61163e8961334f565b925061166b61165484600263ffffffff61337b16565b6116648a8a63ffffffff61339316565b1115612fa0565b61167c83600363ffffffff61337b16565b91506116878761342d565b604080518a81526020808c0282010190915291975095508780156116b5578160200160208202803883390190505b5093506116dc6000805160206154f78339815191528a8a8a8a8a600063ffffffff6134b816565b8681101561170e57818189011084828151811015156116f757fe5b9115156020928302909101909101526001016116dc565b50505093509350939050565b61172583838361352f565b505050565b60009081526020819052604090205460ff1690565b61177882828080601f01602080910402602001604051908101604052809392919081815260200183838082843750613676945050505050565b61178183612f50565b611798600080516020615477833981519152612f62565b611844828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561182c5780601f1061180a57610100808354040283529182019161182c565b820191906000526020600020905b815481529060010190602001808311611818575b505091505060405180910390206000191614156136df565b60008381526020819052604090206118609060010183836153e8565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b60608060606000806000806118ca6153d6565b6000806000806118d8612a6d565b98506118e2611eab565b97508760405190808252806020026020018201604052801561190e578160200160208202803883390190505b509b508760405190808252806020026020018201604052801561193b578160200160208202803883390190505b509a5087604051908082528060200260200182016040528015611968578160200160208202803883390190505b50995060009650600095505b88841015611a43576119858461172a565b151561199057611a38565b6119998461334f565b94506119ac85600163ffffffff61337b16565b92506119bf85600363ffffffff61337b16565b9150828210156119cb57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d9089908110611a0057fe5b600160a060020a039092166020928302909101909101528a5181908c9089908110611a2757fe5b602090810290910101526001909601955b836001019350611974565b851515611a4f57611ab2565b600096505b87871015611ab25785611a858e8d8a815181101515611a6f57fe5b602090810290910101519063ffffffff61373616565b811515611a8e57fe5b048b88815181101515611a9d57fe5b60209081029091010152600190960195611a54565b5050505050505050509193909250565b611ad9600080516020615477833981519152612f62565b611ae382826137e1565b5050565b611aef6120e0565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015611bb05760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611b75578181015183820152602001611b5d565b50505050905090810190601f168015611ba25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50611bbb838361395d565b611bc3613a43565b611bcc81613a57565b611725613a6c565b611beb6000805160206154b7833981519152612f62565b847fbc3a9275fc3a367bea48e061d624141756b6b28245cee6c39703d9289aa90f628585858560405180806020018481526020018381526020018281038252868682818152602001925080828437604051920182900397509095505050505050a25050505050565b600090565b611ae38282600161352f565b600080611c907f4ddbb0dcdc5f7692e494c15a7fca1f9eb65f31da0b5ce1c3381f6a1a1fd579b66132f7565b90508060028111156110ac57fe5b50600090565b6000611cae6153d6565b600080611cba85612f50565b611cd1600080516020615477833981519152612f62565b611ce2611cdd8661172a565b613b36565b611cea611eab565b9350611d1b611d0085600163ffffffff613b8d16565b6000805160206154978339815191529063ffffffff613c2116565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a2611d6c8561334f565b9250611d7f83600063ffffffff61337b16565b9150611d9283600363ffffffff61337b16565b905080821115611de557611dae8360008363ffffffff61302316565b611db88584613c25565b60408051828152905186916000805160206154d7833981519152919081900360200190a2611de5856130c3565b611ded61313a565b5050505050565b50600190565b6000805160206154b783398151915281565b60006112217fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b6132f7565b6000611e7284848080601f01602080910402602001604051908101604052809392919081815260200183838082843750612c5e945050505050565b15611e7f57506000611ea2565b611e876132fb565b8210158015611e9f5750611e99612dc4565b82860310155b90505b95945050505050565b60006112216000805160206154978339815191526132f7565b6000806000611f0286868080601f01602080910402602001604051908101604052809392919081815260200183838082843750613676945050505050565b611f0b84613c3e565b611f22600080516020615477833981519152612f62565b611f2a612a6d565b925060c88310611f84576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611fb77fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613c2116565b60008381526020819052604090209150611fcf611eab565b9050611ff26000805160206154978339815191526001830163ffffffff613c2116565b815460ff19166001908117835561200c90830187876153e8565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b60006112217f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a66132f7565b60006112217febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e6132f7565b60006121156153d6565b61211e83612f50565b6121278361334f565b905061215a61213d82600363ffffffff61337b16565b61214e83600263ffffffff61337b16565b9063ffffffff613b8d16565b9392505050565b6121786000805160206154b7833981519152612f62565b6121826000613d18565b50565b60008051602061547783398151915281565b61219f611089565b15156121f5576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6121ff6003613d9c565b612207612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561224157600080fd5b505af1158015612255573d6000803e3d6000fd5b505050506040513d602081101561226b57600080fd5b5051600160a060020a031663095ea7b3612283612e67565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156122bd57600080fd5b505af11580156122d1573d6000803e3d6000fd5b505050506040513d60208110156122e757600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260006024830181905290516044808401936020939083900390910190829087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d602081101561236057600080fd5b50612182905081613a57565b6001612376611c64565b600281111561238157fe5b146123d6576040805160e560020a62461bcd02815260206004820152601660248201527f444953545249425554494f4e5f4e4f545f524541445900000000000000000000604482015290519081900360640190fd5b6123e06002613d18565b612182613dfa565b60006124016000805160206154b7833981519152612f62565b612409612a6d565b90506000811115612182576121826000600183036137e1565b61242b81612f50565b612442600080516020615477833981519152612f62565b61245461244e8261172a565b15613b36565b61247d61245f611eab565b6000805160206154978339815191529060010163ffffffff613c2116565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261218261313a565b6124de82612f50565b6124f56000805160206154b7833981519152612f62565b612501828260016140b3565b611ae361313a565b61251281613c3e565b61251b82612f50565b612532600080516020615477833981519152612f62565b60008281526020819052604090205461255e90600160a060020a038381166101009092041614156136df565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b600060606000806000806000806125ea6153d6565b6125f38b612f50565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089612634576040805160208101909152600081526126c1565b60018281018054604080516020600295841615610100026000190190931694909404601f8101839004830285018301909152808452908301828280156126bb5780601f10612690576101008083540402835291602001916126bb565b820191906000526020600020905b81548152906001019060200180831161269e57829003601f168201915b50505050505b97506126cc8b61334f565b90506126df81600063ffffffff61337b16565b95506126f281600163ffffffff61337b16565b945061270581600263ffffffff61337b16565b935061271881600363ffffffff61337b16565b9250505092959891949750929550565b60008060006127356153d6565b61273d614244565b905061275081600163ffffffff61337b16565b935061276381600363ffffffff61337b16565b925061277a8361214e83600063ffffffff61337b16565b915050909192565b60008080808080806127a16000805160206154b7833981519152612f62565b6127ab8a89614260565b96506127b5612a6d565b95506024600435019250602480350191505b86811015612806576008810283013560c01c94506010810282013560801c93506001016127f5868610612fa0565b612801858560006140b3565b6127c7565b61280e61313a565b5050505050505050505050565b6040805160e560020a62461bcd02815260206004820152600d60248201527f4e4f545f535550504f5254454400000000000000000000000000000000000000604482015290519081900360640190fd5b600080612876611089565b151561288557600091506129b0565b61288d612e3c565b9050600160a060020a03811615156128a857600091506129b0565b80600160a060020a031663fdef91068630876128c3886142da565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561293257818101518382015260200161291a565b50505050905090810190601f16801561295f5780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561298157600080fd5b505af1158015612995573d6000803e3d6000fd5b505050506040513d60208110156129ab57600080fd5b505191505b509392505050565b6000806129c3612e3c565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b1580156112e857600080fd5b60006112217fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86132f7565b6117258383612aa8576000612aab565b60015b60ff16836110b3565b612abd82612f50565b612afa7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612af58467ffffffffffffffff85166142e4565b61433f565b612b06611cdd8361172a565b612501828267ffffffffffffffff16600161434d565b600080600080600080600080612b306153d6565b612b398a612f50565b612b428a612ff7565b9050612b5581600063ffffffff61337b16565b9850612b6881600163ffffffff61337b16565b9750600096506000955060009450612b7f8a61446b565b80945081955082965050505050919395975091939597565b60608060006060612baa8686600161161c565b8051929650909450915081906000908110612bc157fe5b906020019060200201519150509250925092565b60ff81565b6000808080808080612bf96000805160206154b7833981519152612f62565b612c038a89614260565b9650612c0d612a6d565b95506024600435019250602480350191505b86811015612806576008810283013560c01c94506010810282013560801c9350600101612c4d868610612fa0565b612c598585600061434d565b612c1f565b600080826040518082805190602001908083835b60208310612c915780518252601f199092019160209182019101612c72565b51815160209384036101000a6000190180199092169116179052604080519290940182900390912060009081526002909152919091205460ff169695505050505050565b612cec600080516020615477833981519152612f62565b611ae3828261448b565b60608060008180612d146000805160206154b7833981519152612f62565b871515612d3a576040805160008082526020820190815281830190925295509350612db9565b612d4388614622565b91945092509050878314612da1576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612dac83838361491b565b9095509350612db961313a565b505050935093915050565b60408051602081019091526000906112219060019080612e037f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd86132f7565b90529063ffffffff61337b16565b60006112217fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e6132f7565b60006112217f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b6132f7565b60006112217ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035376132f7565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612ec06153d6565b612ec983612f50565b612ed28361334f565b905061215a81600263ffffffff61337b16565b6000600019612ef26120e0565b14905090565b6301e1338081565b612f176000805160206154b7833981519152612f62565b612f216001613d18565b565b60c881565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b4390565b612182612f5b612a6d565b8210612fa0565b612182612f9b33836000604051908082528060200260200182016040528015612f95578160200160208202803883390190505b5061286b565b614af7565b801515612182576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b612fff6153d6565b50600090815260208181526040918290208251918201909252600490910154815290565b67ffffffffffffffff811115613083576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600490910155565b6000806130ce6153d6565b60006130d985614b4e565b93509350838314156130ea57611ded565b6130f2614244565b91506130fe8385614c2b565b90508383111561311f5761311a8260008363ffffffff614c4216565b613131565b6131318260008363ffffffff614c6616565b611ded82614c79565b60006131657fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e6132f7565b600101905061319a7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613c2116565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b61320b6153d6565b600061321686612f50565b6132203387614c7f565b61323f851580159061323a575067ffffffffffffffff8611155b612fa0565b6132488661334f565b915061325b82600263ffffffff61337b16565b905061327967ffffffffffffffff611664838863ffffffff61339316565b61329b6000805160206154f7833981519152878388888863ffffffff614cec16565b60408051828152905191925087917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a26132e58260028363ffffffff61302316565b6132ef8683613c25565b6111ee61313a565b5490565b604080516020810190915260009061122190829080612e037f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd86132f7565b6000818310613348578161215a565b5090919050565b6133576153d6565b50600090815260208181526040918290208251918201909252600290910154815290565b905167ffffffffffffffff604090920260ff161c1690565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156134215760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b508091505b5092915050565b60608061344183603063ffffffff61373616565b6040519080825280601f01601f19166020018201604052801561346e578160200160208202803883390190505b5061348084606063ffffffff61373616565b6040519080825280601f01601f1916602001820160405280156134ad578160200160208202803883390190505b509092509050915091565b6000805b85811015613524576134d7898989840163ffffffff614f9f16565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016134bc565b505050505050505050565b6135376153d6565b60008061354386612f50565b61354d3387614c7f565b831515613559576111ee565b6135628661334f565b925061357583600263ffffffff61337b16565b91506135a661358b84600363ffffffff61337b16565b861015801561323a575082611664878763ffffffff61339316565b6135c76000805160206154f78339815191528787878663ffffffff61503216565b91506135db8360028463ffffffff61302316565b60408051838152905187917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261362283600063ffffffff61337b16565b9050808510156136635761363e8360008763ffffffff61302316565b60408051868152905187916000805160206154d7833981519152919081900360200190a25b61366d8684613c25565b6132ef866130c3565b60008151118015613689575060ff815111155b1515612182576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b801515612182576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156137495760009150613426565b5082820282848281151561375957fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146134215760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b6000806000806137ef6153d6565b600061380e87891115801561323a5750613807612a6d565b8810612fa0565b50865b868111613942576138218161334f565b915061383482600263ffffffff61337b16565b935061384782600363ffffffff61337b16565b9250828414156138565761393a565b82841161385f57fe5b82840395509385019361387a8260028563ffffffff61302316565b61388c8260008563ffffffff61302316565b6138968183613c25565b61389f816130c3565b60408051848152905182917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a260408051848152905182916000805160206154d7833981519152919081900360200190a26040805167ffffffffffffffff88168152905182917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b600101613811565b60008511156139535761395361313a565b5050505050505050565b6139668261523b565b6139967ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378363ffffffff613c2116565b6139c67fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08263ffffffff613c2116565b6139d0600261529b565b60408051600160a060020a038416815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805182815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a15050565b613a4d600361529b565b612f216002613d18565b613a61600461529b565b61218281600061448b565b613a746120e0565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613af95760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b50612f21613b05612f4c565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613c2116565b5490565b801515612182576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613c195760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613c478161523b565b613c4f612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613c8957600080fd5b505af1158015613c9d573d6000803e3d6000fd5b505050506040513d6020811015613cb357600080fd5b5051600160a060020a0382811691161415612182576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613d54816002811115613d2757fe5b7f4ddbb0dcdc5f7692e494c15a7fca1f9eb65f31da0b5ce1c3381f6a1a1fd579b69063ffffffff613c2116565b7f7545d380f29a8ae65fafb1acdf2c7762ec02d5607fecbea9dd8d8245e1616d938160405180826002811115613d8657fe5b60ff16815260200191505060405180910390a150565b613da46120b5565b8114612182576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008060006060806000613e0c612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613e4657600080fd5b505af1158015613e5a573d6000803e3d6000fd5b505050506040513d6020811015613e7057600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919650600160a060020a0387169163f5eb42dc916024808201926020929091908290030181600087803b158015613ed757600080fd5b505af1158015613eeb573d6000803e3d6000fd5b505050506040513d6020811015613f0157600080fd5b50519350831515613f11576140ab565b613f1a846118b7565b50925092505b82518110156140ab5760028282815181101515613f3957fe5b906020019060200201511015613f4e576140a3565b84600160a060020a0316638fcb4e5b8483815181101515613f6b57fe5b906020019060200201518484815181101515613f8357fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015613fdc57600080fd5b505af1158015613ff0573d6000803e3d6000fd5b505050506040513d602081101561400657600080fd5b505081516140319083908390811061401a57fe5b60209081029091010151879063ffffffff61339316565b9550828181518110151561404157fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece086838381518110151561408357fe5b906020019060200201516040518082815260200191505060405180910390a25b600101613f20565b505050505090565b6140bb6153d6565b6000806140c66153d6565b60006140d18861334f565b94506140e485600163ffffffff61337b16565b9350838714156140f357613953565b85806140fe57508387115b151561417a576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b61418b85600363ffffffff61337b16565b925061419983881115612fa0565b6141ab8560018963ffffffff61302316565b6141b58886613c25565b60408051888152905189917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a26141f3614244565b91506141ff8785614c2b565b9050838711156142205761421b8260018363ffffffff614c4216565b614232565b6142328260018363ffffffff614c6616565b61423b82614c79565b613953886130c3565b61424c6153d6565b506040805160208101909152600154815290565b600882046010820481148015614277575060088306155b8015614284575060108206155b1515611231576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b8051602002815290565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561431457fe5b60209081029091010152805182908290600190811061432f57fe5b6020908102909101015292915050565b611ae3612f9b33848461286b565b6143556153d6565b6000806000806143648861334f565b945061437785600063ffffffff61337b16565b935061438a85600363ffffffff61337b16565b925061439d85600263ffffffff61337b16565b91506143b2826143ad8986615301565b613339565b9050838114156143c157613953565b85806143cc57508381105b1515614422576040805160e560020a62461bcd02815260206004820152601b60248201527f5645545445445f4b4559535f434f554e545f494e435245415345440000000000604482015290519081900360640190fd5b6144348560008363ffffffff61302316565b61443e8886613c25565b60408051828152905189916000805160206154d7833981519152919081900360200190a2613953886130c3565b60008060008061447a85615310565b919790965090869003945092505050565b60006144956153d6565b600084116144ed576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f455849545f44454c41595f5448524553484f4c4400000000604482015290519081900360640190fd5b82844203039150816144fd612dc4565b1115614579576040805160e560020a62461bcd02815260206004820152602560248201527f494e56414c49445f455849545f50454e414c54595f4355544f46465f54494d4560448201527f5354414d50000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b506040805160208101909152600080825261459c9082908663ffffffff61302316565b6145ae8160018463ffffffff61302316565b80516145e1907f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd89063ffffffff613c2116565b604080518581526020810185905281517fcdd9ecf0c02860ba025eac9d0b0c3fad00cc8a9139ca9eec4076fe8ae57d43af929181900390910190a150505050565b60006060806000606060008060008060008061463c611eab565b975087604051908082528060200260200182016040528015614668578160200160208202803883390190505b50995087604051908082528060200260200182016040528015614695578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146c2578160200160208202803883390190505b5096506146cd612a6d565b94505b84811015614754576146e181615310565b955093509150828414156146f45761474c565b808a8781518110151561470357fe5b602090810290910101528851828403908a908890811061471f57fe5b6020908102909101015286518285039088908890811061473b57fe5b602090810290910101526001909501945b6001016146d0565b85151561477c57604080516000808252602082018181528284019093529c509a50985061490c565b8786101561478e57858a528589528587525b734a08c1501a886861c17341317ff7885a5a1e5db6632529fbc98a898f6040518463ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b838110156148045781810151838201526020016147ec565b50505050905001838103825285818151815260200191508051906020019060200280838360005b8381101561484357818101518382015260200161482b565b505050509050019550505050505060006040518083038186803b15801561486957600080fd5b505af415801561487d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160409081528110156148a657600080fd5b8151602083018051919392830192916401000000008111156148c757600080fd5b820160208101848111156148da57600080fd5b81518560208202830111640100000000821117156148f757600080fd5b50949f509c505050508b8d1015905061490c57fe5b50505050505050509193909250565b60608060008060008061492c6153d6565b60006149366153d6565b61493f8c61342d565b9099509750600096505b8a51821015614aba576149728b8381518110151561496357fe5b9060200190602002015161334f565b925061498583600363ffffffff61337b16565b9550898281518110151561499557fe5b602090810290910101516149b084600163ffffffff61337b16565b019450858514156149c057614aaf565b8585116149c957fe5b8585039350614a098b838151811015156149df57fe5b602090810290910101516000805160206154f78339815191529088878d8d8d63ffffffff6134b816565b8a51968401968b9083908110614a1b57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a2614a6e8360038763ffffffff61302316565b614a8f8b83815181101515614a7f57fe5b9060200190602002015184613c25565b614aaf8b83815181101515614aa057fe5b906020019060200201516130c3565b816001019150614949565b868c14614ac357fe5b614acb614244565b9050614adf8160038963ffffffff614c4216565b614ae881614c79565b50505050505050935093915050565b801515612182576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600080614b596153d6565b614b616153d6565b6000614b6c8661334f565b9250614b7786612ff7565b9150614b8a83600363ffffffff61337b16565b9050614b9d83600063ffffffff61337b16565b9350614bb082600063ffffffff61337b16565b15614bed57614bea81614be586614bce86600163ffffffff61337b16565b614bdf88600163ffffffff61337b16565b01613339565b615301565b93505b614bfe82600263ffffffff61337b16565b9450838514614c2357614c198260028663ffffffff61302316565b614c2386836130aa565b505050915091565b6000818311614c3c5782820361215a565b50900390565b6117258383614c6184614c55888861337b565b9063ffffffff61339316565b613023565b6117258383614c618461214e888861337b565b51600155565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff1661117f828015614cb35750815b80612f9b5750612f9b857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ce787615395565b61286b565b6000806000606060008088118015614d1b575067ffffffffffffffff614d188a8a63ffffffff61339316565b11155b1515614d71576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614d8288603063ffffffff61373616565b8751148015614da15750614d9d88606063ffffffff61373616565b8651145b1515614df7576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b87811015614f9057614e338b8b8b63ffffffff614f9f16565b60308281028901602081810151918301519286018390528501819052919550171592508215614eac576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b83811015614f51578181015183820152602001614f39565b50505050905090810190601f168015614f7e5780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614e1a565b50969998505050505050505050565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b60208310614ffb5780518252601f199092019160209182019101614fdc565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561505a5750866150578a8a63ffffffff61339316565b11155b801561506e575067ffffffffffffffff8711155b15156150c4576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b8881111561522c576151088b8b600019840163ffffffff614f9f16565b9450600185015460801c603083015284546020830152868110156151625761513b8b8b6000198a0163ffffffff614f9f16565b9350600092505b600583101561515e578284015483860155600183019250615142565b8394505b600092505b600583101561518157600083860155600183019250615167565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156151ed5781810151838201526020016151d5565b50505050905090810190601f16801561521a5780820380516001836020036101000a031916815260200191505b509250505060405180910390a26150eb565b50949998505050505050505050565b600160a060020a0381161515612182576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b6152cb7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613c2116565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000818311613348578161215a565b600080600061531d6153d6565b6153256153d6565b61532e8661334f565b915061533986612ff7565b905061534c82600163ffffffff61337b16565b945061535f82600363ffffffff61337b16565b935061537281600263ffffffff61337b16565b92508383101580156153845750848410155b151561538c57fe5b50509193909250565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156153c757fe5b60209081029091010152919050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106154295782800160ff19823516178555615456565b82800160010185558215615456579182015b8281111561545657823582559160200191906001019061543b565b506110af926112249250905b808211156110af5760008155600101615462560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a7230582044d97113ae573e6dd62525ae9db79fe6039abb4ccbfdd510fd8319b8326b66790029ebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e
Deployed Bytecode
0x6080604052600436106103265760003560e01c63ffffffff1680630803fac01461032b57806308a679ad14610354578063096b7b351461037457806315dae03e146103a857806328d6d36b146103cf5780632914b9bd146103e757806332f0a3b51461045c5780634febc81b1461047157806357f9c341146104dc57806359e25c12146105075780635ddde810146106445780635e2fb908146106625780635e57d7421461067a57806362dcfda11461069e57806365cc369a1461077b578063684560a214610796578063693cc600146107bd5780636da7d0a7146107e75780636ef355f1146107fc5780636f817294146108175780637038b1411461064457806375049ad81461085057806375a080d5146108685780637e7db6e11461088057806380231f15146108a1578063805911ae1461037457806380afdea8146108b657806383b57a4e146108cb5780638469cbd3146108f657806385fa63d71461090b5780638aa10435146109395780638b3dd7491461094e5780638ca7c052146109635780638d7e40171461097b5780638ece9995146109935780638ee1c0a8146109a85780638f73c5ae146109c057806390c09bdb146109d557806391dcd6b2146109ea5780639412036814610a02578063973e932814610a1d5780639a56983c14610a415780639abddf0914610b1d5780639b00c14614610b505780639d4941d814610b7c578063a1658fad14610b9d578063a479e50814610c04578063a70c70e414610c19578063a9e7a84614610c2e578063ae962acf14610c4e578063b3076c3c14610c73578063b449402a14610ccc578063b497183314610dd0578063b643189b14610de5578063ba2406fd14610e11578063be1575d314610e6a578063bee41b5814610e85578063cfe5871214610f87578063d07442f114610f9c578063d087d28814610f9c578063d4aae0c414610fb1578063d8343dcb14610fc6578063d8e71cd114610fdb578063db9887ea14610ff0578063de4796ed14611008578063e204d09b1461101d578063e864299e14611032578063ec5af3a414611047578063ed5cfa41146107fc578063f31bd9c11461105c578063fbc77ef114611071575b600080fd5b34801561033757600080fd5b50610340611089565b604080519115158252519081900360200190f35b34801561036057600080fd5b506103726004356024356044356110b3565b005b34801561038057600080fd5b5061037260048035906024803591604435808301929082013591606435918201910135611185565b3480156103b457600080fd5b506103bd6111f6565b60408051918252519081900360200190f35b3480156103db57600080fd5b506103bd600435611227565b3480156103f357600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526104409436949293602493928401919081908401838280828437509497506112379650505050505050565b60408051600160a060020a039092168252519081900360200190f35b34801561046857600080fd5b5061044061131a565b34801561047d57600080fd5b5061048c60043560243561138f565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156104c85781810151838201526020016104b0565b505050509050019250505060405180910390f35b3480156104e857600080fd5b5061037260048035906024803591604435918201910135606435611426565b34801561051357600080fd5b5061052560043560243560443561161c565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b8381101561056a578181015183820152602001610552565b50505050905090810190601f1680156105975780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b838110156105ca5781810151838201526020016105b2565b50505050905090810190601f1680156105f75780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561062c578181015183820152602001610614565b50505050905001965050505050505060405180910390f35b34801561065057600080fd5b5061037260043560243560443561171a565b34801561066e57600080fd5b5061034060043561172a565b34801561068657600080fd5b5061037260048035906024803590810191013561173f565b3480156106aa57600080fd5b506106b66004356118b7565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156106fe5781810151838201526020016106e6565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561073d578181015183820152602001610725565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561062c578181015183820152602001610614565b34801561078757600080fd5b50610372600435602435611ac2565b3480156107a257600080fd5b50610372600160a060020a0360043516602435604435611ae7565b3480156107c957600080fd5b50610372600480359060248035908101910135604435606435611bd4565b3480156107f357600080fd5b506103bd611c53565b34801561080857600080fd5b50610372600435602435611c58565b34801561082357600080fd5b5061082c611c64565b6040518082600281111561083c57fe5b60ff16815260200191505060405180910390f35b34801561085c57600080fd5b50610340600435611c9e565b34801561087457600080fd5b50610372600435611ca4565b34801561088c57600080fd5b50610340600160a060020a0360043516611df4565b3480156108ad57600080fd5b506103bd611dfa565b3480156108c257600080fd5b506103bd611e0c565b3480156108d757600080fd5b5061034060048035906024803591604435918201910135606435611e37565b34801561090257600080fd5b506103bd611eab565b34801561091757600080fd5b506103bd6024600480358281019291013590600160a060020a03903516611ec4565b34801561094557600080fd5b506103bd6120b5565b34801561095a57600080fd5b506103bd6120e0565b34801561096f57600080fd5b506103bd60043561210b565b34801561098757600080fd5b50610372600435612161565b34801561099f57600080fd5b506103bd612185565b3480156109b457600080fd5b50610372600435612197565b3480156109cc57600080fd5b5061037261236c565b3480156109e157600080fd5b506103726123e8565b3480156109f657600080fd5b50610372600435612422565b348015610a0e57600080fd5b506103726004356024356124d5565b348015610a2957600080fd5b50610372600435600160a060020a0360243516612509565b348015610a4d57600080fd5b50610a5e60043560243515156125d5565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610adc578181015183820152602001610ac4565b50505050905090810190601f168015610b095780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b348015610b2957600080fd5b50610b32612728565b60408051938452602084019290925282820152519081900360600190f35b348015610b5c57600080fd5b506103726024600480358281019290820135918135918201910135612782565b348015610b8857600080fd5b50610372600160a060020a036004351661281b565b348015610ba957600080fd5b506040805160206004604435818101358381028086018501909652808552610340958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061286b9650505050505050565b348015610c1057600080fd5b506104406129b8565b348015610c2557600080fd5b506103bd612a6d565b348015610c3a57600080fd5b506103726004356024351515604435612a98565b348015610c5a57600080fd5b5061037260043567ffffffffffffffff60243516612ab4565b348015610c7f57600080fd5b50610c8b600435612b1c565b604080519889526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610cd857600080fd5b50610ce7600435602435612b97565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610d32578181015183820152602001610d1a565b50505050905090810190601f168015610d5f5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610d92578181015183820152602001610d7a565b50505050905090810190601f168015610dbf5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610ddc57600080fd5b506103bd612bd5565b348015610df157600080fd5b506103726024600480358281019290820135918135918201910135612bda565b348015610e1d57600080fd5b506040805160206004803580820135601f8101849004840285018401909552848452610340943694929360249392840191908190840183828082843750949750612c5e9650505050505050565b348015610e7657600080fd5b50610372600435602435612cd5565b348015610e9157600080fd5b50610ea9600480359060248035908101910135612cf6565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610eea578181015183820152602001610ed2565b50505050905090810190601f168015610f175780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610f4a578181015183820152602001610f32565b50505050905090810190601f168015610f775780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610f9357600080fd5b506103bd612dc4565b348015610fa857600080fd5b506103bd612e11565b348015610fbd57600080fd5b50610440612e3c565b348015610fd257600080fd5b50610440612e67565b348015610fe757600080fd5b506103bd612e92565b348015610ffc57600080fd5b506103bd600435612eb6565b34801561101457600080fd5b50610340612ee5565b34801561102957600080fd5b506103bd612ef8565b34801561103e57600080fd5b50610372612f00565b34801561105357600080fd5b506103bd612f23565b34801561106857600080fd5b506103bd612f28565b34801561107d57600080fd5b50610340600435611df4565b6000806110946120e0565b905080158015906110ac5750806110a9612f4c565b10155b91505b5090565b6110bb6153d6565b6110c484612f50565b6110db6000805160206154b7833981519152612f62565b6110ef67ffffffffffffffff831115612fa0565b6110f884612ff7565b905061110c8160008563ffffffff61302316565b82151561111857600091505b61112a8160018463ffffffff61302316565b61113484826130aa565b6040805183815260208101859052815186927ff92eb109ce5b449e9b121c352c6aeb4319538a90738cb95d84f08e41274e92d2928290030190a2611177846130c3565b61117f61313a565b50505050565b6111ee868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750613203945050505050565b505050505050565b60006112217fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d06132f7565b90505b90565b60006112316132fb565b92915050565b60006112416129b8565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561129c578181015183820152602001611284565b50505050905090810190601f1680156112c95780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b1580156112e857600080fd5b505af11580156112fc573d6000803e3d6000fd5b505050506040513d602081101561131257600080fd5b505192915050565b6000611324612e3c565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561135e57600080fd5b505af1158015611372573d6000803e3d6000fd5b505050506040513d602081101561138857600080fd5b5051905090565b606060008061139c612a6d565b915081851015806113ab575083155b156113b55761141e565b6113c184868403613339565b6040519080825280602002602001820160405280156113ea578160200160208202803883390190505b509250600090505b825181101561141e57808501838281518110151561140c57fe5b602090810290910101526001016113f2565b505092915050565b600061143f6000805160206154b7833981519152612f62565b60308314611497576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f5055424c49435f4b45590000000000000000000000000000604482015290519081900360640190fd5b61149f6132fb565b8210156114f6576040805160e560020a62461bcd02815260206004820152601a60248201527f455849545f44454c41595f42454c4f575f5448524553484f4c44000000000000604482015290519081900360640190fd5b6114fe612dc4565b8286031015611557576040805160e560020a62461bcd02815260206004820152601e60248201527f544f4f5f4c4154455f464f525f455849545f44454c41595f5245504f52540000604482015290519081900360640190fd5b838360405180838380828437604080519190930181900390206000818152600260205292909220549195505060ff16159250611595915050576111ee565b600081815260026020908152604091829020805460ff1916600117905581519081018490529081018690526060808252810184905286907f7f781065728a5d3f9c1ee91ef0c0b8a9455ebc228dae6d35514b32c7370349c9908690869086908a908060808101868680828437604051920182900397509095505050505050a2505050505050565b60608060606116296153d6565b60008061163589612f50565b61163e8961334f565b925061166b61165484600263ffffffff61337b16565b6116648a8a63ffffffff61339316565b1115612fa0565b61167c83600363ffffffff61337b16565b91506116878761342d565b604080518a81526020808c0282010190915291975095508780156116b5578160200160208202803883390190505b5093506116dc6000805160206154f78339815191528a8a8a8a8a600063ffffffff6134b816565b8681101561170e57818189011084828151811015156116f757fe5b9115156020928302909101909101526001016116dc565b50505093509350939050565b61172583838361352f565b505050565b60009081526020819052604090205460ff1690565b61177882828080601f01602080910402602001604051908101604052809392919081815260200183838082843750613676945050505050565b61178183612f50565b611798600080516020615477833981519152612f62565b611844828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561182c5780601f1061180a57610100808354040283529182019161182c565b820191906000526020600020905b815481529060010190602001808311611818575b505091505060405180910390206000191614156136df565b60008381526020819052604090206118609060010183836153e8565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b60608060606000806000806118ca6153d6565b6000806000806118d8612a6d565b98506118e2611eab565b97508760405190808252806020026020018201604052801561190e578160200160208202803883390190505b509b508760405190808252806020026020018201604052801561193b578160200160208202803883390190505b509a5087604051908082528060200260200182016040528015611968578160200160208202803883390190505b50995060009650600095505b88841015611a43576119858461172a565b151561199057611a38565b6119998461334f565b94506119ac85600163ffffffff61337b16565b92506119bf85600363ffffffff61337b16565b9150828210156119cb57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d9089908110611a0057fe5b600160a060020a039092166020928302909101909101528a5181908c9089908110611a2757fe5b602090810290910101526001909601955b836001019350611974565b851515611a4f57611ab2565b600096505b87871015611ab25785611a858e8d8a815181101515611a6f57fe5b602090810290910101519063ffffffff61373616565b811515611a8e57fe5b048b88815181101515611a9d57fe5b60209081029091010152600190960195611a54565b5050505050505050509193909250565b611ad9600080516020615477833981519152612f62565b611ae382826137e1565b5050565b611aef6120e0565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015611bb05760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611b75578181015183820152602001611b5d565b50505050905090810190601f168015611ba25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50611bbb838361395d565b611bc3613a43565b611bcc81613a57565b611725613a6c565b611beb6000805160206154b7833981519152612f62565b847fbc3a9275fc3a367bea48e061d624141756b6b28245cee6c39703d9289aa90f628585858560405180806020018481526020018381526020018281038252868682818152602001925080828437604051920182900397509095505050505050a25050505050565b600090565b611ae38282600161352f565b600080611c907f4ddbb0dcdc5f7692e494c15a7fca1f9eb65f31da0b5ce1c3381f6a1a1fd579b66132f7565b90508060028111156110ac57fe5b50600090565b6000611cae6153d6565b600080611cba85612f50565b611cd1600080516020615477833981519152612f62565b611ce2611cdd8661172a565b613b36565b611cea611eab565b9350611d1b611d0085600163ffffffff613b8d16565b6000805160206154978339815191529063ffffffff613c2116565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a2611d6c8561334f565b9250611d7f83600063ffffffff61337b16565b9150611d9283600363ffffffff61337b16565b905080821115611de557611dae8360008363ffffffff61302316565b611db88584613c25565b60408051828152905186916000805160206154d7833981519152919081900360200190a2611de5856130c3565b611ded61313a565b5050505050565b50600190565b6000805160206154b783398151915281565b60006112217fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b6132f7565b6000611e7284848080601f01602080910402602001604051908101604052809392919081815260200183838082843750612c5e945050505050565b15611e7f57506000611ea2565b611e876132fb565b8210158015611e9f5750611e99612dc4565b82860310155b90505b95945050505050565b60006112216000805160206154978339815191526132f7565b6000806000611f0286868080601f01602080910402602001604051908101604052809392919081815260200183838082843750613676945050505050565b611f0b84613c3e565b611f22600080516020615477833981519152612f62565b611f2a612a6d565b925060c88310611f84576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611fb77fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613c2116565b60008381526020819052604090209150611fcf611eab565b9050611ff26000805160206154978339815191526001830163ffffffff613c2116565b815460ff19166001908117835561200c90830187876153e8565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b60006112217f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a66132f7565b60006112217febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e6132f7565b60006121156153d6565b61211e83612f50565b6121278361334f565b905061215a61213d82600363ffffffff61337b16565b61214e83600263ffffffff61337b16565b9063ffffffff613b8d16565b9392505050565b6121786000805160206154b7833981519152612f62565b6121826000613d18565b50565b60008051602061547783398151915281565b61219f611089565b15156121f5576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6121ff6003613d9c565b612207612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561224157600080fd5b505af1158015612255573d6000803e3d6000fd5b505050506040513d602081101561226b57600080fd5b5051600160a060020a031663095ea7b3612283612e67565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156122bd57600080fd5b505af11580156122d1573d6000803e3d6000fd5b505050506040513d60208110156122e757600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260006024830181905290516044808401936020939083900390910190829087803b15801561233657600080fd5b505af115801561234a573d6000803e3d6000fd5b505050506040513d602081101561236057600080fd5b50612182905081613a57565b6001612376611c64565b600281111561238157fe5b146123d6576040805160e560020a62461bcd02815260206004820152601660248201527f444953545249425554494f4e5f4e4f545f524541445900000000000000000000604482015290519081900360640190fd5b6123e06002613d18565b612182613dfa565b60006124016000805160206154b7833981519152612f62565b612409612a6d565b90506000811115612182576121826000600183036137e1565b61242b81612f50565b612442600080516020615477833981519152612f62565b61245461244e8261172a565b15613b36565b61247d61245f611eab565b6000805160206154978339815191529060010163ffffffff613c2116565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261218261313a565b6124de82612f50565b6124f56000805160206154b7833981519152612f62565b612501828260016140b3565b611ae361313a565b61251281613c3e565b61251b82612f50565b612532600080516020615477833981519152612f62565b60008281526020819052604090205461255e90600160a060020a038381166101009092041614156136df565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b600060606000806000806000806125ea6153d6565b6125f38b612f50565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089612634576040805160208101909152600081526126c1565b60018281018054604080516020600295841615610100026000190190931694909404601f8101839004830285018301909152808452908301828280156126bb5780601f10612690576101008083540402835291602001916126bb565b820191906000526020600020905b81548152906001019060200180831161269e57829003601f168201915b50505050505b97506126cc8b61334f565b90506126df81600063ffffffff61337b16565b95506126f281600163ffffffff61337b16565b945061270581600263ffffffff61337b16565b935061271881600363ffffffff61337b16565b9250505092959891949750929550565b60008060006127356153d6565b61273d614244565b905061275081600163ffffffff61337b16565b935061276381600363ffffffff61337b16565b925061277a8361214e83600063ffffffff61337b16565b915050909192565b60008080808080806127a16000805160206154b7833981519152612f62565b6127ab8a89614260565b96506127b5612a6d565b95506024600435019250602480350191505b86811015612806576008810283013560c01c94506010810282013560801c93506001016127f5868610612fa0565b612801858560006140b3565b6127c7565b61280e61313a565b5050505050505050505050565b6040805160e560020a62461bcd02815260206004820152600d60248201527f4e4f545f535550504f5254454400000000000000000000000000000000000000604482015290519081900360640190fd5b600080612876611089565b151561288557600091506129b0565b61288d612e3c565b9050600160a060020a03811615156128a857600091506129b0565b80600160a060020a031663fdef91068630876128c3886142da565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561293257818101518382015260200161291a565b50505050905090810190601f16801561295f5780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561298157600080fd5b505af1158015612995573d6000803e3d6000fd5b505050506040513d60208110156129ab57600080fd5b505191505b509392505050565b6000806129c3612e3c565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b1580156112e857600080fd5b60006112217fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86132f7565b6117258383612aa8576000612aab565b60015b60ff16836110b3565b612abd82612f50565b612afa7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612af58467ffffffffffffffff85166142e4565b61433f565b612b06611cdd8361172a565b612501828267ffffffffffffffff16600161434d565b600080600080600080600080612b306153d6565b612b398a612f50565b612b428a612ff7565b9050612b5581600063ffffffff61337b16565b9850612b6881600163ffffffff61337b16565b9750600096506000955060009450612b7f8a61446b565b80945081955082965050505050919395975091939597565b60608060006060612baa8686600161161c565b8051929650909450915081906000908110612bc157fe5b906020019060200201519150509250925092565b60ff81565b6000808080808080612bf96000805160206154b7833981519152612f62565b612c038a89614260565b9650612c0d612a6d565b95506024600435019250602480350191505b86811015612806576008810283013560c01c94506010810282013560801c9350600101612c4d868610612fa0565b612c598585600061434d565b612c1f565b600080826040518082805190602001908083835b60208310612c915780518252601f199092019160209182019101612c72565b51815160209384036101000a6000190180199092169116179052604080519290940182900390912060009081526002909152919091205460ff169695505050505050565b612cec600080516020615477833981519152612f62565b611ae3828261448b565b60608060008180612d146000805160206154b7833981519152612f62565b871515612d3a576040805160008082526020820190815281830190925295509350612db9565b612d4388614622565b91945092509050878314612da1576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612dac83838361491b565b9095509350612db961313a565b505050935093915050565b60408051602081019091526000906112219060019080612e037f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd86132f7565b90529063ffffffff61337b16565b60006112217fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e6132f7565b60006112217f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b6132f7565b60006112217ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035376132f7565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612ec06153d6565b612ec983612f50565b612ed28361334f565b905061215a81600263ffffffff61337b16565b6000600019612ef26120e0565b14905090565b6301e1338081565b612f176000805160206154b7833981519152612f62565b612f216001613d18565b565b60c881565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b4390565b612182612f5b612a6d565b8210612fa0565b612182612f9b33836000604051908082528060200260200182016040528015612f95578160200160208202803883390190505b5061286b565b614af7565b801515612182576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b612fff6153d6565b50600090815260208181526040918290208251918201909252600490910154815290565b67ffffffffffffffff811115613083576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600490910155565b6000806130ce6153d6565b60006130d985614b4e565b93509350838314156130ea57611ded565b6130f2614244565b91506130fe8385614c2b565b90508383111561311f5761311a8260008363ffffffff614c4216565b613131565b6131318260008363ffffffff614c6616565b611ded82614c79565b60006131657fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e6132f7565b600101905061319a7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613c2116565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b61320b6153d6565b600061321686612f50565b6132203387614c7f565b61323f851580159061323a575067ffffffffffffffff8611155b612fa0565b6132488661334f565b915061325b82600263ffffffff61337b16565b905061327967ffffffffffffffff611664838863ffffffff61339316565b61329b6000805160206154f7833981519152878388888863ffffffff614cec16565b60408051828152905191925087917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a26132e58260028363ffffffff61302316565b6132ef8683613c25565b6111ee61313a565b5490565b604080516020810190915260009061122190829080612e037f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd86132f7565b6000818310613348578161215a565b5090919050565b6133576153d6565b50600090815260208181526040918290208251918201909252600290910154815290565b905167ffffffffffffffff604090920260ff161c1690565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156134215760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b508091505b5092915050565b60608061344183603063ffffffff61373616565b6040519080825280601f01601f19166020018201604052801561346e578160200160208202803883390190505b5061348084606063ffffffff61373616565b6040519080825280601f01601f1916602001820160405280156134ad578160200160208202803883390190505b509092509050915091565b6000805b85811015613524576134d7898989840163ffffffff614f9f16565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016134bc565b505050505050505050565b6135376153d6565b60008061354386612f50565b61354d3387614c7f565b831515613559576111ee565b6135628661334f565b925061357583600263ffffffff61337b16565b91506135a661358b84600363ffffffff61337b16565b861015801561323a575082611664878763ffffffff61339316565b6135c76000805160206154f78339815191528787878663ffffffff61503216565b91506135db8360028463ffffffff61302316565b60408051838152905187917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261362283600063ffffffff61337b16565b9050808510156136635761363e8360008763ffffffff61302316565b60408051868152905187916000805160206154d7833981519152919081900360200190a25b61366d8684613c25565b6132ef866130c3565b60008151118015613689575060ff815111155b1515612182576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b801515612182576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156137495760009150613426565b5082820282848281151561375957fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146134215760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b6000806000806137ef6153d6565b600061380e87891115801561323a5750613807612a6d565b8810612fa0565b50865b868111613942576138218161334f565b915061383482600263ffffffff61337b16565b935061384782600363ffffffff61337b16565b9250828414156138565761393a565b82841161385f57fe5b82840395509385019361387a8260028563ffffffff61302316565b61388c8260008563ffffffff61302316565b6138968183613c25565b61389f816130c3565b60408051848152905182917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a260408051848152905182916000805160206154d7833981519152919081900360200190a26040805167ffffffffffffffff88168152905182917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b600101613811565b60008511156139535761395361313a565b5050505050505050565b6139668261523b565b6139967ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378363ffffffff613c2116565b6139c67fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08263ffffffff613c2116565b6139d0600261529b565b60408051600160a060020a038416815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805182815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a15050565b613a4d600361529b565b612f216002613d18565b613a61600461529b565b61218281600061448b565b613a746120e0565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613af95760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b50612f21613b05612f4c565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613c2116565b5490565b801515612182576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613c195760405160e560020a62461bcd02815260040180806020018281038252838181518152602001915080519060200190808383600083811015611b75578181015183820152602001611b5d565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613c478161523b565b613c4f612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613c8957600080fd5b505af1158015613c9d573d6000803e3d6000fd5b505050506040513d6020811015613cb357600080fd5b5051600160a060020a0382811691161415612182576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613d54816002811115613d2757fe5b7f4ddbb0dcdc5f7692e494c15a7fca1f9eb65f31da0b5ce1c3381f6a1a1fd579b69063ffffffff613c2116565b7f7545d380f29a8ae65fafb1acdf2c7762ec02d5607fecbea9dd8d8245e1616d938160405180826002811115613d8657fe5b60ff16815260200191505060405180910390a150565b613da46120b5565b8114612182576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008060006060806000613e0c612e67565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613e4657600080fd5b505af1158015613e5a573d6000803e3d6000fd5b505050506040513d6020811015613e7057600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919650600160a060020a0387169163f5eb42dc916024808201926020929091908290030181600087803b158015613ed757600080fd5b505af1158015613eeb573d6000803e3d6000fd5b505050506040513d6020811015613f0157600080fd5b50519350831515613f11576140ab565b613f1a846118b7565b50925092505b82518110156140ab5760028282815181101515613f3957fe5b906020019060200201511015613f4e576140a3565b84600160a060020a0316638fcb4e5b8483815181101515613f6b57fe5b906020019060200201518484815181101515613f8357fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015613fdc57600080fd5b505af1158015613ff0573d6000803e3d6000fd5b505050506040513d602081101561400657600080fd5b505081516140319083908390811061401a57fe5b60209081029091010151879063ffffffff61339316565b9550828181518110151561404157fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece086838381518110151561408357fe5b906020019060200201516040518082815260200191505060405180910390a25b600101613f20565b505050505090565b6140bb6153d6565b6000806140c66153d6565b60006140d18861334f565b94506140e485600163ffffffff61337b16565b9350838714156140f357613953565b85806140fe57508387115b151561417a576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b61418b85600363ffffffff61337b16565b925061419983881115612fa0565b6141ab8560018963ffffffff61302316565b6141b58886613c25565b60408051888152905189917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a26141f3614244565b91506141ff8785614c2b565b9050838711156142205761421b8260018363ffffffff614c4216565b614232565b6142328260018363ffffffff614c6616565b61423b82614c79565b613953886130c3565b61424c6153d6565b506040805160208101909152600154815290565b600882046010820481148015614277575060088306155b8015614284575060108206155b1515611231576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b8051602002815290565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561431457fe5b60209081029091010152805182908290600190811061432f57fe5b6020908102909101015292915050565b611ae3612f9b33848461286b565b6143556153d6565b6000806000806143648861334f565b945061437785600063ffffffff61337b16565b935061438a85600363ffffffff61337b16565b925061439d85600263ffffffff61337b16565b91506143b2826143ad8986615301565b613339565b9050838114156143c157613953565b85806143cc57508381105b1515614422576040805160e560020a62461bcd02815260206004820152601b60248201527f5645545445445f4b4559535f434f554e545f494e435245415345440000000000604482015290519081900360640190fd5b6144348560008363ffffffff61302316565b61443e8886613c25565b60408051828152905189916000805160206154d7833981519152919081900360200190a2613953886130c3565b60008060008061447a85615310565b919790965090869003945092505050565b60006144956153d6565b600084116144ed576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f455849545f44454c41595f5448524553484f4c4400000000604482015290519081900360640190fd5b82844203039150816144fd612dc4565b1115614579576040805160e560020a62461bcd02815260206004820152602560248201527f494e56414c49445f455849545f50454e414c54595f4355544f46465f54494d4560448201527f5354414d50000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b506040805160208101909152600080825261459c9082908663ffffffff61302316565b6145ae8160018463ffffffff61302316565b80516145e1907f9fe52a88cbf7bfbe5e42abc45469ad27b2231a10bcbcd0a227c7ca0835cecbd89063ffffffff613c2116565b604080518581526020810185905281517fcdd9ecf0c02860ba025eac9d0b0c3fad00cc8a9139ca9eec4076fe8ae57d43af929181900390910190a150505050565b60006060806000606060008060008060008061463c611eab565b975087604051908082528060200260200182016040528015614668578160200160208202803883390190505b50995087604051908082528060200260200182016040528015614695578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146c2578160200160208202803883390190505b5096506146cd612a6d565b94505b84811015614754576146e181615310565b955093509150828414156146f45761474c565b808a8781518110151561470357fe5b602090810290910101528851828403908a908890811061471f57fe5b6020908102909101015286518285039088908890811061473b57fe5b602090810290910101526001909501945b6001016146d0565b85151561477c57604080516000808252602082018181528284019093529c509a50985061490c565b8786101561478e57858a528589528587525b734a08c1501a886861c17341317ff7885a5a1e5db6632529fbc98a898f6040518463ffffffff1660e01b8152600401808060200180602001848152602001838103835286818151815260200191508051906020019060200280838360005b838110156148045781810151838201526020016147ec565b50505050905001838103825285818151815260200191508051906020019060200280838360005b8381101561484357818101518382015260200161482b565b505050509050019550505050505060006040518083038186803b15801561486957600080fd5b505af415801561487d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160409081528110156148a657600080fd5b8151602083018051919392830192916401000000008111156148c757600080fd5b820160208101848111156148da57600080fd5b81518560208202830111640100000000821117156148f757600080fd5b50949f509c505050508b8d1015905061490c57fe5b50505050505050509193909250565b60608060008060008061492c6153d6565b60006149366153d6565b61493f8c61342d565b9099509750600096505b8a51821015614aba576149728b8381518110151561496357fe5b9060200190602002015161334f565b925061498583600363ffffffff61337b16565b9550898281518110151561499557fe5b602090810290910101516149b084600163ffffffff61337b16565b019450858514156149c057614aaf565b8585116149c957fe5b8585039350614a098b838151811015156149df57fe5b602090810290910101516000805160206154f78339815191529088878d8d8d63ffffffff6134b816565b8a51968401968b9083908110614a1b57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a2614a6e8360038763ffffffff61302316565b614a8f8b83815181101515614a7f57fe5b9060200190602002015184613c25565b614aaf8b83815181101515614aa057fe5b906020019060200201516130c3565b816001019150614949565b868c14614ac357fe5b614acb614244565b9050614adf8160038963ffffffff614c4216565b614ae881614c79565b50505050505050935093915050565b801515612182576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600080614b596153d6565b614b616153d6565b6000614b6c8661334f565b9250614b7786612ff7565b9150614b8a83600363ffffffff61337b16565b9050614b9d83600063ffffffff61337b16565b9350614bb082600063ffffffff61337b16565b15614bed57614bea81614be586614bce86600163ffffffff61337b16565b614bdf88600163ffffffff61337b16565b01613339565b615301565b93505b614bfe82600263ffffffff61337b16565b9450838514614c2357614c198260028663ffffffff61302316565b614c2386836130aa565b505050915091565b6000818311614c3c5782820361215a565b50900390565b6117258383614c6184614c55888861337b565b9063ffffffff61339316565b613023565b6117258383614c618461214e888861337b565b51600155565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff1661117f828015614cb35750815b80612f9b5750612f9b857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ce787615395565b61286b565b6000806000606060008088118015614d1b575067ffffffffffffffff614d188a8a63ffffffff61339316565b11155b1515614d71576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614d8288603063ffffffff61373616565b8751148015614da15750614d9d88606063ffffffff61373616565b8651145b1515614df7576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b87811015614f9057614e338b8b8b63ffffffff614f9f16565b60308281028901602081810151918301519286018390528501819052919550171592508215614eac576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b83811015614f51578181015183820152602001614f39565b50505050905090810190601f168015614f7e5780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614e1a565b50969998505050505050505050565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b60208310614ffb5780518252601f199092019160209182019101614fdc565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561505a5750866150578a8a63ffffffff61339316565b11155b801561506e575067ffffffffffffffff8711155b15156150c4576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b8881111561522c576151088b8b600019840163ffffffff614f9f16565b9450600185015460801c603083015284546020830152868110156151625761513b8b8b6000198a0163ffffffff614f9f16565b9350600092505b600583101561515e578284015483860155600183019250615142565b8394505b600092505b600583101561518157600083860155600183019250615167565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156151ed5781810151838201526020016151d5565b50505050905090810190601f16801561521a5780820380516001836020036101000a031916815260200191505b509250505060405180910390a26150eb565b50949998505050505050505050565b600160a060020a0381161515612182576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b6152cb7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613c2116565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000818311613348578161215a565b600080600061531d6153d6565b6153256153d6565b61532e8661334f565b915061533986612ff7565b905061534c82600163ffffffff61337b16565b945061535f82600363ffffffff61337b16565b935061537281600263ffffffff61337b16565b92508383101580156153845750848410155b151561538c57fe5b50509193909250565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156153c757fe5b60209081029091010152919050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106154295782800160ff19823516178555615456565b82800160010185558215615456579182015b8281111561545657823582559160200191906001019061543b565b506110af926112249250905b808211156110af5760008155600101615462560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a7230582044d97113ae573e6dd62525ae9db79fe6039abb4ccbfdd510fd8319b8326b66790029
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.