Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
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: 2023 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 {IBurner} from "../../common/interfaces/IBurner.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 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 StuckPenaltyDelayChanged(uint256 stuckPenaltyDelay); event StuckPenaltyStateChanged( uint256 indexed nodeOperatorId, uint256 stuckValidatorsCount, uint256 refundedValidatorsCount, uint256 stuckPenaltyEndTimestamp ); event TargetValidatorsCountChanged(uint256 indexed nodeOperatorId, uint256 targetValidatorsCount); event NodeOperatorPenalized(address indexed recipientAddress, uint256 sharesPenalizedAmount); // // 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 ACTIVATE_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 Flag enable/disable limiting target active validators count for operator uint8 internal constant IS_TARGET_LIMIT_ACTIVE_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; // StuckPenaltyStats /// @dev stuck keys count from oracle report uint8 internal constant STUCK_VALIDATORS_COUNT_OFFSET = 0; /// @dev refunded keys count from dao uint8 internal constant REFUNDED_VALIDATORS_COUNT_OFFSET = 1; /// @dev extra penalty time after stuck keys resolved (refunded and/or exited) /// @notice field is also used as flag for "half-cleaned" penalty status /// Operator is PENALIZED if `STUCK_VALIDATORS_COUNT > REFUNDED_VALIDATORS_COUNT` or /// `STUCK_VALIDATORS_COUNT <= REFUNDED_VALIDATORS_COUNT && STUCK_PENALTY_END_TIMESTAMP <= refund timestamp + STUCK_PENALTY_DELAY` /// When operator refund all stuck validators and time has pass STUCK_PENALTY_DELAY, but STUCK_PENALTY_END_TIMESTAMP not zeroed, /// then Operator can receive rewards but can't get new deposits until the new Oracle report or `clearNodeOperatorPenalty` is called. uint8 internal constant STUCK_PENALTY_END_TIMESTAMP_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 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 STUCK_PENALTY_DELAY_POSITION = keccak256("lido.NodeOperatorsRegistry.stuckPenaltyDelay"); bytes32 internal constant STUCK_PENALTY_DELAY_POSITION = 0x8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e; // // 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; // // METHODS // function initialize(address _locator, bytes32 _type, uint256 _stuckPenaltyDelay) public onlyInit { // Initializations for v1 --> v2 _initialize_v2(_locator, _type, _stuckPenaltyDelay); initialized(); } /// @notice A function to finalize upgrade to v2 (from v1). Can be called only once /// For more details see https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-10.md function finalizeUpgrade_v2(address _locator, bytes32 _type, uint256 _stuckPenaltyDelay) external { require(hasInitialized(), "CONTRACT_NOT_INITIALIZED"); _checkContractVersion(0); _initialize_v2(_locator, _type, _stuckPenaltyDelay); uint256 totalOperators = getNodeOperatorsCount(); Packed64x4.Packed memory signingKeysStats; Packed64x4.Packed memory operatorTargetStats; Packed64x4.Packed memory summarySigningKeysStats = Packed64x4.Packed(0); uint256 vettedSigningKeysCountBefore; uint256 totalSigningKeysCount; uint256 depositedSigningKeysCount; for (uint256 nodeOperatorId; nodeOperatorId < totalOperators; ++nodeOperatorId) { signingKeysStats = _loadOperatorSigningKeysStats(nodeOperatorId); vettedSigningKeysCountBefore = signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET); totalSigningKeysCount = signingKeysStats.get(TOTAL_KEYS_COUNT_OFFSET); depositedSigningKeysCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); uint256 vettedSigningKeysCountAfter; if (!_nodeOperators[nodeOperatorId].active) { // trim vetted signing keys count when node operator is not active vettedSigningKeysCountAfter = depositedSigningKeysCount; } else { vettedSigningKeysCountAfter = Math256.min( totalSigningKeysCount, Math256.max(depositedSigningKeysCount, vettedSigningKeysCountBefore) ); } if (vettedSigningKeysCountBefore != vettedSigningKeysCountAfter) { signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, vettedSigningKeysCountAfter); _saveOperatorSigningKeysStats(nodeOperatorId, signingKeysStats); emit VettedSigningKeysCountChanged(nodeOperatorId, vettedSigningKeysCountAfter); } operatorTargetStats = _loadOperatorTargetValidatorsStats(nodeOperatorId); operatorTargetStats.set(MAX_VALIDATORS_COUNT_OFFSET, vettedSigningKeysCountAfter); _saveOperatorTargetValidatorsStats(nodeOperatorId, operatorTargetStats); summarySigningKeysStats.add(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET, vettedSigningKeysCountAfter); summarySigningKeysStats.add(SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET, depositedSigningKeysCount); summarySigningKeysStats.add( SUMMARY_EXITED_KEYS_COUNT_OFFSET, signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET) ); summarySigningKeysStats.add(SUMMARY_TOTAL_KEYS_COUNT_OFFSET, totalSigningKeysCount); } _saveSummarySigningKeysStats(summarySigningKeysStats); _increaseValidatorsKeysNonce(); } function _initialize_v2(address _locator, bytes32 _type, uint256 _stuckPenaltyDelay) internal { _onlyNonZeroAddress(_locator); LIDO_LOCATOR_POSITION.setStorageAddress(_locator); TYPE_POSITION.setStorageBytes32(_type); _setContractVersion(2); _setStuckPenaltyDelay(_stuckPenaltyDelay); // set unlimited allowance for burner from staking router // to burn stuck keys penalized shares IStETH(getLocator().lido()).approve(getLocator().burner(), ~uint256(0)); emit LocatorContractSet(_locator); emit StakingModuleTypeSet(_type); } /// @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)); 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; } signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, vettedSigningKeysCountAfter); _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats); emit VettedSigningKeysCountChanged(_nodeOperatorId, vettedSigningKeysCountAfter); _updateSummaryMaxValidatorsCount(_nodeOperatorId); _increaseValidatorsKeysNonce(); } /// @notice Called by StakingRouter to signal that stETH rewards were minted for this module. function onRewardsMinted(uint256 /* _totalShares */) external view { _auth(STAKING_ROUTER_ROLE); // since we're pushing rewards to operators after exited validators counts are // updated (as opposed to pulling by node ops), we don't need any handling here // see `onExitedAndStuckValidatorsCountsUpdated()` } 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 of the given node /// operator that were requested to exit but failed to do so in the max allowed time /// /// @param _nodeOperatorIds bytes packed array of the node operators id /// @param _stuckValidatorsCounts bytes packed array of the new number of stuck validators for the node operators function updateStuckValidatorsCount(bytes _nodeOperatorIds, bytes _stuckValidatorsCounts) external { _auth(STAKING_ROUTER_ROLE); uint256 nodeOperatorsCount = _checkReportPayload(_nodeOperatorIds.length, _stuckValidatorsCounts.length); uint256 totalNodeOperatorsCount = getNodeOperatorsCount(); uint256 nodeOperatorId; uint256 validatorsCount; uint256 _nodeOperatorIdsOffset; uint256 _stuckValidatorsCountsOffset; /// @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) _stuckValidatorsCountsOffset := 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(_stuckValidatorsCountsOffset, mul(i, 16)))) i := add(i, 1) } _requireValidRange(nodeOperatorId < totalNodeOperatorsCount); _updateStuckValidatorsCount(nodeOperatorId, validatorsCount); } _increaseValidatorsKeysNonce(); } /// @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; /// @dev see comments for `updateStuckValidatorsCount` 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 Updates the number of the refunded validators for node operator with the given id /// @param _nodeOperatorId Id of the node operator /// @param _refundedValidatorsCount New number of refunded validators of the node operator function updateRefundedValidatorsCount(uint256 _nodeOperatorId, uint256 _refundedValidatorsCount) external { _onlyExistedNodeOperator(_nodeOperatorId); _auth(STAKING_ROUTER_ROLE); _updateRefundValidatorsKeysCount(_nodeOperatorId, _refundedValidatorsCount); } /// @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); // for the permissioned module, we're distributing rewards within oracle operation // since the number of node ops won't be high and thus gas costs are limited _distributeRewards(); } /// @notice Unsafely updates the number of validators in the EXITED/STUCK states for node operator with given id /// 'unsafely' means that this method can both increase and decrease exited and stuck counters /// @param _nodeOperatorId Id of the node operator /// @param _exitedValidatorsCount New number of EXITED validators for the node operator /// @param _stuckValidatorsCount New number of STUCK validator for the node operator function unsafeUpdateValidatorsCount( uint256 _nodeOperatorId, uint256 _exitedValidatorsCount, uint256 _stuckValidatorsCount ) external { _onlyExistedNodeOperator(_nodeOperatorId); _auth(STAKING_ROUTER_ROLE); _updateStuckValidatorsCount(_nodeOperatorId, _stuckValidatorsCount); _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); uint256 stuckValidatorsCount = _loadOperatorStuckPenaltyStats(_nodeOperatorId).get(STUCK_VALIDATORS_COUNT_OFFSET); // sustain invariant exited + stuck <= deposited assert(depositedValidatorsCount >= stuckValidatorsCount); _requireValidRange(_exitedValidatorsCount <= depositedValidatorsCount - stuckValidatorsCount); 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 _targetLimit Target limit of the node operator /// @param _isTargetLimitActive active flag function updateTargetValidatorsLimits(uint256 _nodeOperatorId, bool _isTargetLimitActive, uint256 _targetLimit) external { _onlyExistedNodeOperator(_nodeOperatorId); _auth(STAKING_ROUTER_ROLE); _requireValidRange(_targetLimit <= UINT64_MAX); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); operatorTargetStats.set(IS_TARGET_LIMIT_ACTIVE_OFFSET, _isTargetLimitActive ? 1 : 0); operatorTargetStats.set(TARGET_VALIDATORS_COUNT_OFFSET, _isTargetLimitActive ? _targetLimit : 0); _saveOperatorTargetValidatorsStats(_nodeOperatorId, operatorTargetStats); emit TargetValidatorsCountChanged(_nodeOperatorId, _targetLimit); _updateSummaryMaxValidatorsCount(_nodeOperatorId); _increaseValidatorsKeysNonce(); } /** * @notice Set the stuck signings keys count */ function _updateStuckValidatorsCount(uint256 _nodeOperatorId, uint256 _stuckValidatorsCount) internal { Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); uint256 curStuckValidatorsCount = stuckPenaltyStats.get(STUCK_VALIDATORS_COUNT_OFFSET); if (_stuckValidatorsCount == curStuckValidatorsCount) return; Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); uint256 exitedValidatorsCount = signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET); uint256 depositedValidatorsCount = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET); // sustain invariant exited + stuck <= deposited assert(depositedValidatorsCount >= exitedValidatorsCount); _requireValidRange(_stuckValidatorsCount <= depositedValidatorsCount - exitedValidatorsCount); uint256 curRefundedValidatorsCount = stuckPenaltyStats.get(REFUNDED_VALIDATORS_COUNT_OFFSET); if (_stuckValidatorsCount <= curRefundedValidatorsCount && curStuckValidatorsCount > curRefundedValidatorsCount) { stuckPenaltyStats.set(STUCK_PENALTY_END_TIMESTAMP_OFFSET, block.timestamp + getStuckPenaltyDelay()); } stuckPenaltyStats.set(STUCK_VALIDATORS_COUNT_OFFSET, _stuckValidatorsCount); _saveOperatorStuckPenaltyStats(_nodeOperatorId, stuckPenaltyStats); emit StuckPenaltyStateChanged( _nodeOperatorId, _stuckValidatorsCount, curRefundedValidatorsCount, stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET) ); _updateSummaryMaxValidatorsCount(_nodeOperatorId); } function _updateRefundValidatorsKeysCount(uint256 _nodeOperatorId, uint256 _refundedValidatorsCount) internal { Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); uint256 curRefundedValidatorsCount = stuckPenaltyStats.get(REFUNDED_VALIDATORS_COUNT_OFFSET); if (_refundedValidatorsCount == curRefundedValidatorsCount) return; Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId); _requireValidRange(_refundedValidatorsCount <= signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET)); uint256 curStuckValidatorsCount = stuckPenaltyStats.get(STUCK_VALIDATORS_COUNT_OFFSET); if (_refundedValidatorsCount >= curStuckValidatorsCount && curRefundedValidatorsCount < curStuckValidatorsCount) { stuckPenaltyStats.set(STUCK_PENALTY_END_TIMESTAMP_OFFSET, block.timestamp + getStuckPenaltyDelay()); } stuckPenaltyStats.set(REFUNDED_VALIDATORS_COUNT_OFFSET, _refundedValidatorsCount); _saveOperatorStuckPenaltyStats(_nodeOperatorId, stuckPenaltyStats); emit StuckPenaltyStateChanged( _nodeOperatorId, curStuckValidatorsCount, _refundedValidatorsCount, stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET) ); _updateSummaryMaxValidatorsCount(_nodeOperatorId); } // @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) { Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); summarySigningKeysStats.sub(SUMMARY_TOTAL_KEYS_COUNT_OFFSET, totalTrimmedKeysCount); _saveSummarySigningKeysStats(summarySigningKeysStats); _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 (!isOperatorPenaltyCleared(_nodeOperatorId)) { // when the node operator is penalized zeroing its depositable validators count newMaxSigningKeysCount = depositedSigningKeysCount; } else if (operatorTargetStats.get(IS_TARGET_LIMIT_ACTIVE_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 = 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. /// @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; penalized[idx] = isOperatorPenalized(operatorId); ++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); // upd totals Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); summarySigningKeysStats.add(SUMMARY_TOTAL_KEYS_COUNT_OFFSET, _keysCount); _saveSummarySigningKeysStats(summarySigningKeysStats); _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); } 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); // upd totals Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats(); summarySigningKeysStats.sub(SUMMARY_TOTAL_KEYS_COUNT_OFFSET, _keysCount); _saveSummarySigningKeysStats(summarySigningKeysStats); _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 ( bool isTargetLimitActive, uint256 targetValidatorsCount, uint256 stuckValidatorsCount, uint256 refundedValidatorsCount, uint256 stuckPenaltyEndTimestamp, uint256 totalExitedValidators, uint256 totalDepositedValidators, uint256 depositableValidatorsCount ) { _onlyExistedNodeOperator(_nodeOperatorId); Packed64x4.Packed memory operatorTargetStats = _loadOperatorTargetValidatorsStats(_nodeOperatorId); Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); isTargetLimitActive = operatorTargetStats.get(IS_TARGET_LIMIT_ACTIVE_OFFSET) != 0; targetValidatorsCount = operatorTargetStats.get(TARGET_VALIDATORS_COUNT_OFFSET); stuckValidatorsCount = stuckPenaltyStats.get(STUCK_VALIDATORS_COUNT_OFFSET); refundedValidatorsCount = stuckPenaltyStats.get(REFUNDED_VALIDATORS_COUNT_OFFSET); stuckPenaltyEndTimestamp = stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET); (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; } function _isOperatorPenalized(Packed64x4.Packed memory stuckPenaltyStats) internal view returns (bool) { return stuckPenaltyStats.get(REFUNDED_VALIDATORS_COUNT_OFFSET) < stuckPenaltyStats.get(STUCK_VALIDATORS_COUNT_OFFSET) || block.timestamp <= stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET); } function isOperatorPenalized(uint256 _nodeOperatorId) public view returns (bool) { Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); return _isOperatorPenalized(stuckPenaltyStats); } function isOperatorPenaltyCleared(uint256 _nodeOperatorId) public view returns (bool) { Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); return !_isOperatorPenalized(stuckPenaltyStats) && stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET) == 0; } function clearNodeOperatorPenalty(uint256 _nodeOperatorId) external returns (bool) { Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(_nodeOperatorId); require( !_isOperatorPenalized(stuckPenaltyStats) && stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET) != 0, "CANT_CLEAR_PENALTY" ); stuckPenaltyStats.set(STUCK_PENALTY_END_TIMESTAMP_OFFSET, 0); _saveOperatorStuckPenaltyStats(_nodeOperatorId, stuckPenaltyStats); _updateSummaryMaxValidatorsCount(_nodeOperatorId); _increaseValidatorsKeysNonce(); } /// @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, bool[] memory penalized) = getRewardsDistribution(sharesToDistribute); uint256 toBurn; 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; if (penalized[idx]) { /// @dev half reward punishment /// @dev ignore remainder since it accumulated on contract balance shares[idx] >>= 1; toBurn = toBurn.add(shares[idx]); emit NodeOperatorPenalized(recipients[idx], shares[idx]); } stETH.transferShares(recipients[idx], shares[idx]); distributed = distributed.add(shares[idx]); emit RewardsDistributed(recipients[idx], shares[idx]); } if (toBurn > 0) { IBurner(getLocator().burner()).requestBurnShares(address(this), toBurn); } } function getLocator() public view returns (ILidoLocator) { return ILidoLocator(LIDO_LOCATOR_POSITION.getStorageAddress()); } function getStuckPenaltyDelay() public view returns (uint256) { return STUCK_PENALTY_DELAY_POSITION.getStorageUint256(); } function setStuckPenaltyDelay(uint256 _delay) external { _auth(MANAGE_NODE_OPERATOR_ROLE); _setStuckPenaltyDelay(_delay); } /// @dev set new stuck penalty delay, duration in sec function _setStuckPenaltyDelay(uint256 _delay) internal { _requireValidRange(_delay <= MAX_STUCK_PENALTY_DELAY); STUCK_PENALTY_DELAY_POSITION.setStorageUint256(_delay); emit StuckPenaltyDelayChanged(_delay); } 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 _loadOperatorStuckPenaltyStats(uint256 _nodeOperatorId) internal view returns (Packed64x4.Packed memory) { return _nodeOperators[_nodeOperatorId].stuckPenaltyStats; } function _saveOperatorStuckPenaltyStats(uint256 _nodeOperatorId, Packed64x4.Packed memory _val) internal { _nodeOperators[_nodeOperatorId].stuckPenaltyStats = _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: 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; interface IBurner { /** * Commit cover/non-cover burning requests and logs cover/non-cover shares amount just burnt. * * NB: The real burn enactment to be invoked after the call (via internal Lido._burnShares()) */ function commitSharesToBurn(uint256 _stETHSharesToBurn) external; /** * Request burn shares */ function requestBurnShares(address _from, uint256 _sharesAmount) external; /** * Returns the current amount of shares locked on the contract to be burnt. */ function getSharesRequestedToBurn() external view returns (uint256 coverShares, uint256 nonCoverShares); /** * Returns the total cover shares ever burnt. */ function getCoverSharesBurnt() external view returns (uint256); /** * Returns the total non-cover shares ever burnt. */ function getNonCoverSharesBurnt() external view returns (uint256); }
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> // SPDX-License-Identifier: GPL-3.0 // See contracts/COMPILERS.md // solhint-disable-next-line pragma solidity >=0.4.24 <0.9.0; interface ILidoLocator { function accountingOracle() external view returns(address); function depositSecurityModule() external view returns(address); function elRewardsVault() external view returns(address); function legacyOracle() external view returns(address); function lido() external view returns(address); function oracleReportSanityChecker() external view returns(address); function burner() external view returns(address); function stakingRouter() external view returns(address); function treasury() external view returns(address); function validatorsExitBusOracle() external view returns(address); function withdrawalQueue() external view returns(address); function withdrawalVault() external view returns(address); function postTokenRebaseReceiver() external view returns(address); function oracleDaemonConfig() external view returns(address); function coreComponents() external view returns( address elRewardsVault, address oracleReportSanityChecker, address stakingRouter, address treasury, address withdrawalQueue, address withdrawalVault ); function oracleReportComponentsForLido() external view returns( address accountingOracle, address elRewardsVault, address oracleReportSanityChecker, address burner, address withdrawalQueue, address withdrawalVault, address postTokenRebaseReceiver ); }
// SPDX-FileCopyrightText: 2023 Lido <[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 ) internal pure returns (uint256 allocated) { uint256 allocatedToBestCandidate = 0; while (allocated < allocationSize) { allocatedToBestCandidate = allocateToBestCandidate(buckets, capacities, allocationSize - allocated); if (allocatedToBestCandidate == 0) { break; } allocated += allocatedToBestCandidate; } } /// @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": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"constant":true,"inputs":[],"name":"hasInitialized","outputs":[{"name":"","type":"bool"}],"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":"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":"_script","type":"bytes"}],"name":"getEVMScriptExecutor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"}],"name":"clearNodeOperatorPenalty","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","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":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":"_stuckPenaltyDelay","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_delay","type":"uint256"}],"name":"setStuckPenaltyDelay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getStuckPenaltyDelay","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_index","type":"uint256"}],"name":"removeSigningKey","outputs":[],"payable":false,"stateMutability":"nonpayable","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":"_nodeOperatorId","type":"uint256"}],"name":"isOperatorPenalized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","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":"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":true,"inputs":[{"name":"","type":"uint256"}],"name":"onRewardsMinted","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MANAGE_NODE_OPERATOR_ROLE","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","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":"_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":false,"inputs":[{"name":"_locator","type":"address"},{"name":"_type","type":"bytes32"},{"name":"_stuckPenaltyDelay","type":"uint256"}],"name":"finalizeUpgrade_v2","outputs":[],"payable":false,"stateMutability":"nonpayable","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":"_nodeOperatorIds","type":"bytes"},{"name":"_stuckValidatorsCounts","type":"bytes"}],"name":"updateStuckValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","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":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_refundedValidatorsCount","type":"uint256"}],"name":"updateRefundedValidatorsCount","outputs":[],"payable":false,"stateMutability":"nonpayable","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":"isTargetLimitActive","type":"bool"},{"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":"_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":"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":false,"inputs":[{"name":"_nodeOperatorId","type":"uint256"},{"name":"_exitedValidatorsCount","type":"uint256"},{"name":"_stuckValidatorsCount","type":"uint256"}],"name":"unsafeUpdateValidatorsCount","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":"_nodeOperatorId","type":"uint256"}],"name":"isOperatorPenaltyCleared","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","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":"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":false,"name":"stuckPenaltyDelay","type":"uint256"}],"name":"StuckPenaltyDelayChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"stuckValidatorsCount","type":"uint256"},{"indexed":false,"name":"refundedValidatorsCount","type":"uint256"},{"indexed":false,"name":"stuckPenaltyEndTimestamp","type":"uint256"}],"name":"StuckPenaltyStateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"nodeOperatorId","type":"uint256"},{"indexed":false,"name":"targetValidatorsCount","type":"uint256"}],"name":"TargetValidatorsCountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"recipientAddress","type":"address"},{"indexed":false,"name":"sharesPenalizedAmount","type":"uint256"}],"name":"NodeOperatorPenalized","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
6080604052620000146200005460201b60201c565b6200004e7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a660001962000156602090811b62003d6d17901c565b6200026b565b620000646200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a454400000000000000006020820152901562000140576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101562000104578181015183820152602001620000ea565b50505050905090810190601f168015620001325780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50620001546000196200018d60201b60201c565b565b9055565b60006200018860008051602062005de083398151915260001b600019166200026760201b62002f7c1760201c565b905090565b6200019d6200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156200023c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360008381101562000104578181015183820152602001620000ea565b506200026460008051602062005de08339815191528262000156602090811b62003d6d17901c565b50565b5490565b615b65806200027b6000396000f3006080604052600436106102d95760003560e01c63ffffffff1680630803fac0146102de578063096b7b351461030757806315dae03e1461033d5780632914b9bd1461036457806330a90f01146103d957806332f0a3b5146103f15780634febc81b1461040657806359e25c12146104715780635ddde810146105ae5780635e2fb908146105cc5780635e57d742146105e457806362dcfda11461060857806365cc369a146106e5578063684560a2146107005780636ccc7562146107275780636da7d0a71461073f5780636ef355f1146107545780637038b141146105ae57806375049ad81461076f57806375a080d5146107875780637e7db6e11461079f57806380231f15146107c0578063805911ae1461030757806380afdea8146107d55780638469cbd3146107ea57806385fa63d7146107ff5780638aa104351461082d5780638b3dd749146108425780638ca7c052146108575780638d7e40171461086f5780638ece99951461088757806390c09bdb1461089c57806391dcd6b2146108b1578063973e9328146108c95780639a56983c146108ed5780639a7c2ade146109c95780639abddf09146109f05780639b00c14614610a235780639b3d190014610a4f5780639d4941d814610a7b578063a1658fad14610a9c578063a2e080f114610b03578063a479e50814610b1e578063a70c70e414610b33578063a9e7a84614610b48578063ae962acf14610b68578063b3076c3c14610b8d578063b449402a14610be8578063b497183314610cec578063bee41b5814610d01578063d07442f114610e03578063d087d28814610e03578063d4aae0c414610e18578063d8343dcb14610e2d578063d8e71cd114610e42578063db9887ea14610e57578063de4796ed14610e6f578063e204d09b14610e84578063e864299e14610e99578063ec5af3a414610eae578063ed5cfa4114610754578063f2e2ca6314610ec3578063f31bd9c114610ee1578063fbc77ef114610ef6575b600080fd5b3480156102ea57600080fd5b506102f3610f0e565b604080519115158252519081900360200190f35b34801561031357600080fd5b5061033b60048035906024803591604435808301929082013591606435918201910135610f38565b005b34801561034957600080fd5b50610352610fa9565b60408051918252519081900360200190f35b34801561037057600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526103bd943694929360249392840191908190840183828082843750949750610fda9650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156103e557600080fd5b506102f36004356110bd565b3480156103fd57600080fd5b506103bd611181565b34801561041257600080fd5b506104216004356024356111f6565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561045d578181015183820152602001610445565b505050509050019250505060405180910390f35b34801561047d57600080fd5b5061048f60043560243560443561128d565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156104d45781810151838201526020016104bc565b50505050905090810190601f1680156105015780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b8381101561053457818101518382015260200161051c565b50505050905090810190601f1680156105615780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561059657818101518382015260200161057e565b50505050905001965050505050505060405180910390f35b3480156105ba57600080fd5b5061033b60043560243560443561138b565b3480156105d857600080fd5b506102f360043561139b565b3480156105f057600080fd5b5061033b6004803590602480359081019101356113b0565b34801561061457600080fd5b50610620600435611528565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610668578181015183820152602001610650565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156106a757818101518382015260200161068f565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561059657818101518382015260200161057e565b3480156106f157600080fd5b5061033b600435602435611759565b34801561070c57600080fd5b5061033b600160a060020a036004351660243560443561177e565b34801561073357600080fd5b5061033b60043561185b565b34801561074b57600080fd5b5061035261187e565b34801561076057600080fd5b5061033b6004356024356118a9565b34801561077b57600080fd5b506102f36004356118b5565b34801561079357600080fd5b5061033b6004356118da565b3480156107ab57600080fd5b506102f3600160a060020a0360043516611a2a565b3480156107cc57600080fd5b50610352611a30565b3480156107e157600080fd5b50610352611a42565b3480156107f657600080fd5b50610352611a6d565b34801561080b57600080fd5b506103526024600480358281019291013590600160a060020a03903516611a86565b34801561083957600080fd5b50610352611c77565b34801561084e57600080fd5b50610352611ca2565b34801561086357600080fd5b50610352600435611ccd565b34801561087b57600080fd5b5061033b600435611d1c565b34801561089357600080fd5b50610352611d33565b3480156108a857600080fd5b5061033b611d45565b3480156108bd57600080fd5b5061033b600435611d7f565b3480156108d557600080fd5b5061033b600435600160a060020a0360243516611e32565b3480156108f957600080fd5b5061090a6004356024351515611efe565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610988578181015183820152602001610970565b50505050905090810190601f1680156109b55780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b3480156109d557600080fd5b5061033b600160a060020a0360043516602435604435612051565b3480156109fc57600080fd5b50610a05612278565b60408051938452602084019290925282820152519081900360600190f35b348015610a2f57600080fd5b5061033b60246004803582810192908201359181359182019101356122d2565b348015610a5b57600080fd5b5061033b602460048035828101929082013591813591820191013561236b565b348015610a8757600080fd5b5061033b600160a060020a03600435166123ed565b348015610aa857600080fd5b5060408051602060046044358181013583810280860185019096528085526102f3958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061267c9650505050505050565b348015610b0f57600080fd5b5061033b6004356024356127c9565b348015610b2a57600080fd5b506103bd6127f3565b348015610b3f57600080fd5b506103526128a8565b348015610b5457600080fd5b5061033b60043560243515156044356128d3565b348015610b7457600080fd5b5061033b60043567ffffffffffffffff602435166129b7565b348015610b9957600080fd5b50610ba5600435612adc565b6040805198151589526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610bf457600080fd5b50610c03600435602435612b9a565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610c4e578181015183820152602001610c36565b50505050905090810190601f168015610c7b5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610cae578181015183820152602001610c96565b50505050905090810190601f168015610cdb5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610cf857600080fd5b50610352612bd8565b348015610d0d57600080fd5b50610d25600480359060248035908101910135612bdd565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610d66578181015183820152602001610d4e565b50505050905090810190601f168015610d935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610dc6578181015183820152602001610dae565b50505050905090810190601f168015610df35780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610e0f57600080fd5b50610352612cab565b348015610e2457600080fd5b506103bd612cd6565b348015610e3957600080fd5b506103bd612d01565b348015610e4e57600080fd5b50610352612d2c565b348015610e6357600080fd5b50610352600435612d50565b348015610e7b57600080fd5b506102f3612d7f565b348015610e9057600080fd5b50610352612d92565b348015610ea557600080fd5b5061033b612d9a565b348015610eba57600080fd5b50610352612db9565b348015610ecf57600080fd5b5061033b600435602435604435612dbe565b348015610eed57600080fd5b50610352612dfc565b348015610f0257600080fd5b506102f3600435612e20565b600080610f19611ca2565b90508015801590610f31575080610f2e612e5f565b10155b91505b5090565b610fa1868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750612e63945050505050565b505050505050565b6000610fd47fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0612f7c565b90505b90565b6000610fe46127f3565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561103f578181015183820152602001611027565b50505050905090810190601f16801561106c5780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b15801561108b57600080fd5b505af115801561109f573d6000803e3d6000fd5b505050506040513d60208110156110b557600080fd5b505192915050565b60006110c76159f9565b6110d083612f80565b90506110db81612fac565b1580156110f757506110f481600263ffffffff612ff116565b15155b151561114d576040805160e560020a62461bcd02815260206004820152601260248201527f43414e545f434c4541525f50454e414c54590000000000000000000000000000604482015290519081900360640190fd5b611160816002600063ffffffff61300916565b61116a8382613090565b611173836130a9565b61117b613120565b50919050565b600061118b612cd6565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111c557600080fd5b505af11580156111d9573d6000803e3d6000fd5b505050506040513d60208110156111ef57600080fd5b5051905090565b60606000806112036128a8565b91508185101580611212575083155b1561121c57611285565b611228848684036131e9565b604051908082528060200260200182016040528015611251578160200160208202803883390190505b509250600090505b825181101561128557808501838281518110151561127357fe5b60209081029091010152600101611259565b505092915050565b606080606061129a6159f9565b6000806112a6896131ff565b6112af89613211565b92506112dc6112c584600263ffffffff612ff116565b6112d58a8a63ffffffff61323d16565b11156132d7565b6112ed83600363ffffffff612ff116565b91506112f88761332e565b604080518a81526020808c028201019091529197509550878015611326578160200160208202803883390190505b50935061134d600080516020615b1a8339815191528a8a8a8a8a600063ffffffff6133b916565b8681101561137f578181890110848281518110151561136857fe5b91151560209283029091019091015260010161134d565b50505093509350939050565b611396838383613430565b505050565b60009081526020819052604090205460ff1690565b6113e982828080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b6113f2836131ff565b611409600080516020615a9a83398151915261360d565b6114b5828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561149d5780601f1061147b57610100808354040283529182019161149d565b820191906000526020600020905b815481529060010190602001808311611489575b5050915050604051809103902060001916141561364b565b60008381526020819052604090206114d1906001018383615a0b565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b606080606060008060008061153b6159f9565b6000806000806115496128a8565b9850611553611a6d565b97508760405190808252806020026020018201604052801561157f578160200160208202803883390190505b509b50876040519080825280602002602001820160405280156115ac578160200160208202803883390190505b509a50876040519080825280602002602001820160405280156115d9578160200160208202803883390190505b50995060009650600095505b888410156116da576115f68461139b565b1515611601576116cf565b61160a84613211565b945061161d85600163ffffffff612ff116565b925061163085600363ffffffff612ff116565b91508282101561163c57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d908990811061167157fe5b600160a060020a039092166020928302909101909101528a5181908c908990811061169857fe5b602090810290910101526116ab846118b5565b8a888151811015156116b957fe5b9115156020928302909101909101526001909601955b8360010193506115e5565b8515156116e657611749565b600096505b87871015611749578561171c8e8d8a81518110151561170657fe5b602090810290910101519063ffffffff6136a216565b81151561172557fe5b048b8881518110151561173457fe5b602090810290910101526001909601956116eb565b5050505050505050509193909250565b611770600080516020615a9a83398151915261360d565b61177a828261374d565b5050565b611786611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156118475760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561180c5781810151838201526020016117f4565b50505050905090810190601f1680156118395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506118538383836138f0565b611396613b40565b611872600080516020615a9a83398151915261360d565b61187b81613c08565b50565b6000610fd47f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e612f7c565b61177a82826001613430565b60006118bf6159f9565b6118c883612f80565b90506118d381612fac565b9392505050565b60006118e46159f9565b6000806118f0856131ff565b611907600080516020615a9a83398151915261360d565b6119186119138661139b565b613c82565b611920611a6d565b935061195161193685600163ffffffff613cd916565b600080516020615aba8339815191529063ffffffff613d6d16565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a26119a285613211565b92506119b583600063ffffffff612ff116565b91506119c883600363ffffffff612ff116565b905080821115611a1b576119e48360008363ffffffff61300916565b6119ee8584613d71565b6040805182815290518691600080516020615afa833981519152919081900360200190a2611a1b856130a9565b611a23613120565b5050505050565b50600190565b600080516020615ada83398151915281565b6000610fd47fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b612f7c565b6000610fd4600080516020615aba833981519152612f7c565b6000806000611ac486868080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b611acd84613d8a565b611ae4600080516020615a9a83398151915261360d565b611aec6128a8565b925060c88310611b46576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611b797fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613d6d16565b60008381526020819052604090209150611b91611a6d565b9050611bb4600080516020615aba8339815191526001830163ffffffff613d6d16565b815460ff191660019081178355611bce9083018787615a0b565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b6000610fd47f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6612f7c565b6000610fd47febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e612f7c565b6000611cd76159f9565b611ce0836131ff565b611ce983613211565b90506118d3611cff82600363ffffffff612ff116565b611d1083600263ffffffff612ff116565b9063ffffffff613cd916565b61187b600080516020615ada83398151915261360d565b600080516020615a9a83398151915281565b6000611d5e600080516020615ada83398151915261360d565b611d666128a8565b9050600081111561187b5761187b60006001830361374d565b611d88816131ff565b611d9f600080516020615a9a83398151915261360d565b611db1611dab8261139b565b15613c82565b611dda611dbc611a6d565b600080516020615aba8339815191529060010163ffffffff613d6d16565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261187b613120565b611e3b81613d8a565b611e44826131ff565b611e5b600080516020615a9a83398151915261360d565b600082815260208190526040902054611e8790600160a060020a0383811661010090920416141561364b565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b60006060600080600080600080611f136159f9565b611f1c8b6131ff565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089611f5d57604080516020810190915260008152611fea565b60018281018054604080516020600295841615610100026000190190931694909404601f810183900483028501830190915280845290830182828015611fe45780601f10611fb957610100808354040283529160200191611fe4565b820191906000526020600020905b815481529060010190602001808311611fc757829003601f168201915b50505050505b9750611ff58b613211565b905061200881600063ffffffff612ff116565b955061201b81600163ffffffff612ff116565b945061202e81600263ffffffff612ff116565b935061204181600363ffffffff612ff116565b9250505092959891949750929550565b600061205b6159f9565b6120636159f9565b61206b6159f9565b600080600080600061207b610f0e565b15156120d1576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6120db6000613e64565b6120e68c8c8c6138f0565b6120ee6128a8565b9850602060405190810160405280600081525095505b888210156122595761211582613211565b975061212888600063ffffffff612ff116565b945061213b88600263ffffffff612ff116565b935061214e88600363ffffffff612ff116565b60008381526020819052604090205490935060ff161515612170575081612186565b6121838461217e8588613ec2565b6131e9565b90505b8481146121ce5761219f8860008363ffffffff61300916565b6121a98289613d71565b6040805182815290518391600080516020615afa833981519152919081900360200190a25b6121d782613ed1565b96506121eb8760028363ffffffff61300916565b6121f58288613efd565b6122078660008363ffffffff613f1616565b6122198660038563ffffffff613f1616565b61223c600161222e8a8263ffffffff612ff116565b88919063ffffffff613f1616565b61224e8660028663ffffffff613f1616565b816001019150612104565b61226286613f3a565b61226a613120565b505050505050505050505050565b60008060006122856159f9565b61228d613f40565b90506122a081600163ffffffff612ff116565b93506122b381600363ffffffff612ff116565b92506122ca83611d1083600063ffffffff612ff116565b915050909192565b60008080808080806122f1600080516020615ada83398151915261360d565b6122fb8a89613f5c565b96506123056128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123458686106132d7565b61235185856000613fd6565b612317565b61235e613120565b5050505050505050505050565b600080808080808061238a600080516020615ada83398151915261360d565b6123948a89613f5c565b965061239e6128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123de8686106132d7565b6123e88585614191565b6123b0565b60008060006123fb84611a2a565b60408051808201909152601281527f5245434f5645525f444953414c4c4f574544000000000000000000000000000060208201529015156124815760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5061248a611181565b9250612495836142e1565b60408051808201909152601a81527f5245434f5645525f5641554c545f4e4f545f434f4e5452414354000000000000602082015290151561251b5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50600160a060020a038416151561256c5760405130319250600160a060020a0384169083156108fc029084906000818181858888f19350505050158015612566573d6000803e3d6000fd5b5061262b565b5082612587600160a060020a0382163063ffffffff61430716565b91506125a3600160a060020a038216848463ffffffff61441c16565b60408051808201909152601d81527f5245434f5645525f544f4b454e5f5452414e534645525f4641494c454400000060208201529015156126295760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505b83600160a060020a031683600160a060020a03167f596caf56044b55fb8c4ca640089bbc2b63cae3e978b851f5745cbb7c5b288e02846040518082815260200191505060405180910390a350505050565b600080612687610f0e565b151561269657600091506127c1565b61269e612cd6565b9050600160a060020a03811615156126b957600091506127c1565b80600160a060020a031663fdef91068630876126d4886144a7565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561274357818101518382015260200161272b565b50505050905090810190601f1680156127705780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561279257600080fd5b505af11580156127a6573d6000803e3d6000fd5b505050506040513d60208110156127bc57600080fd5b505191505b509392505050565b6127d2826131ff565b6127e9600080516020615ada83398151915261360d565b61177a82826144b1565b6000806127fe612cd6565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b15801561108b57600080fd5b6000610fd47fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8612f7c565b6128db6159f9565b6128e4846131ff565b6128fb600080516020615ada83398151915261360d565b61290f67ffffffffffffffff8311156132d7565b61291884613ed1565b905061293f60008461292b57600061292e565b60015b83919060ff1663ffffffff61300916565b612960600184612950576000612952565b835b83919063ffffffff61300916565b61296a8482613efd565b60408051838152905185917fd50ea115db6f0b433ef9cc4b71110dbd9202364a00488be90718990be5bf16a6919081900360200190a26129a9846130a9565b6129b1613120565b50505050565b6129bf6159f9565b6000806000806129ce876131ff565b612a0b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612a068967ffffffffffffffff8a166145d2565b61462d565b612a176119138861139b565b612a2087613211565b9450612a3385600063ffffffff612ff116565b9350612a4685600363ffffffff612ff116565b9250612a5985600263ffffffff612ff116565b9150612a738261217e8867ffffffffffffffff1686613ec2565b905083811415612a8257612ad3565b612a948560008363ffffffff61300916565b612a9e8786613d71565b6040805182815290518891600080516020615afa833981519152919081900360200190a2612acb876130a9565b612ad3613120565b50505050505050565b600080600080600080600080612af06159f9565b612af86159f9565b612b018b6131ff565b612b0a8b613ed1565b9150612b158b612f80565b9050612b2882600063ffffffff612ff116565b15159950612b3d82600163ffffffff612ff116565b9850612b5081600063ffffffff612ff116565b9750612b6381600163ffffffff612ff116565b9650612b7681600263ffffffff612ff116565b9550612b818b61463b565b8095508196508297505050505050919395975091939597565b60608060006060612bad8686600161128d565b8051929650909450915081906000908110612bc457fe5b906020019060200201519150509250925092565b60ff81565b60608060008180612bfb600080516020615ada83398151915261360d565b871515612c21576040805160008082526020820190815281830190925295509350612ca0565b612c2a8861465b565b91945092509050878314612c88576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612c938383836147ed565b9095509350612ca0613120565b505050935093915050565b6000610fd47fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b6000610fd47f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b612f7c565b6000610fd47ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537612f7c565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612d5a6159f9565b612d63836131ff565b612d6c83613211565b90506118d381600263ffffffff612ff116565b6000600019612d8c611ca2565b14905090565b6301e1338081565b612db1600080516020615ada83398151915261360d565b61187b6149c9565b60c881565b612dc7836131ff565b612dde600080516020615ada83398151915261360d565b612de88382614191565b612df483836001613fd6565b611396613120565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b6000612e2a6159f9565b612e3383612f80565b9050612e3e81612fac565b1580156118d35750612e5781600263ffffffff612ff116565b159392505050565b4390565b612e6b6159f9565b6000612e756159f9565b612e7e876131ff565b612e883388614e59565b612ea78615801590612ea2575067ffffffffffffffff8711155b6132d7565b612eb087613211565b9250612ec383600263ffffffff612ff116565b9150612ee167ffffffffffffffff6112d5848963ffffffff61323d16565b612f03600080516020615b1a833981519152888489898963ffffffff614ec616565b60408051828152905191935088917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a2612f4d8360028463ffffffff61300916565b612f578784613d71565b612f5f613f40565b9050612f738160028863ffffffff613f1616565b612acb81613f3a565b5490565b612f886159f9565b50600090815260208181526040918290208251918201909252600390910154815290565b6000612fbe828263ffffffff612ff116565b612fcf83600163ffffffff612ff116565b1080612feb5750612fe782600263ffffffff612ff116565b4211155b92915050565b905167ffffffffffffffff604090920260ff161c1690565b67ffffffffffffffff811115613069576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600390910155565b6000806130b46159f9565b60006130bf85615179565b93509350838314156130d057611a23565b6130d8613f40565b91506130e4838561526d565b905083831115613105576131008260008363ffffffff613f1616565b613117565b6131178260008363ffffffff61528416565b611a2382613f3a565b600061314b7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b60010190506131807fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613d6d16565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b60008183106131f857816118d3565b5090919050565b61187b61320a6128a8565b82106132d7565b6132196159f9565b50600090815260208181526040918290208251918201909252600290910154815290565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b508091505b5092915050565b80151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b60608061334283603063ffffffff6136a216565b6040519080825280601f01601f19166020018201604052801561336f578160200160208202803883390190505b5061338184606063ffffffff6136a216565b6040519080825280601f01601f1916602001820160405280156133ae578160200160208202803883390190505b509092509050915091565b6000805b85811015613425576133d8898989840163ffffffff61529716565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016133bd565b505050505050505050565b6134386159f9565b6000806134436159f9565b61344c876131ff565b6134563388614e59565b84151561346257612ad3565b61346b87613211565b935061347e84600263ffffffff612ff116565b92506134af61349485600363ffffffff612ff116565b8710158015612ea25750836112d5888863ffffffff61323d16565b6134d0600080516020615b1a8339815191528888888763ffffffff61532a16565b92506134e48460028563ffffffff61300916565b60408051848152905188917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261352b84600063ffffffff612ff116565b91508186101561356c576135478460008863ffffffff61300916565b6040805187815290518891600080516020615afa833981519152919081900360200190a25b6135768785613d71565b61357e613f40565b90506135928160028763ffffffff61528416565b61359b81613f3a565b612acb876130a9565b600081511180156135b7575060ff815111155b151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b61187b61364633836000604051908082528060200260200182016040528015613640578160200160208202803883390190505b5061267c565b615533565b80151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156136b557600091506132d0565b508282028284828115156136c557fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b60008060008061375b6159f9565b60006137656159f9565b613782888a11158015612ea2575061377b6128a8565b89106132d7565b8891505b8782116138ba5761379682613211565b92506137a983600263ffffffff612ff116565b94506137bc83600363ffffffff612ff116565b9350838514156137cb576138af565b8385116137d457fe5b8385039650948601946137ef8360028663ffffffff61300916565b6138018360008663ffffffff61300916565b61380b8284613d71565b613814826130a9565b60408051858152905183917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a26040805185815290518391600080516020615afa833981519152919081900360200190a26040805167ffffffffffffffff89168152905183917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b816001019150613786565b6000861115613425576138cb613f40565b90506138df8160028863ffffffff61528416565b6138e881613f3a565b613425613120565b6138f98361558a565b6139297ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378463ffffffff613d6d16565b6139597fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08363ffffffff613d6d16565b61396360026155ea565b61396c81613c08565b613974612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156139ae57600080fd5b505af11580156139c2573d6000803e3d6000fd5b505050506040513d60208110156139d857600080fd5b5051600160a060020a031663095ea7b36139f0612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613a2a57600080fd5b505af1158015613a3e573d6000803e3d6000fd5b505050506040513d6020811015613a5457600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260001960248301525160448083019260209291908290030181600087803b158015613aa057600080fd5b505af1158015613ab4573d6000803e3d6000fd5b505050506040513d6020811015613aca57600080fd5b505060408051600160a060020a038516815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805183815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a1505050565b613b48611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613bcd5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50613c06613bd9612e5f565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613d6d16565b565b613c186301e133808211156132d7565b613c487f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e8263ffffffff613d6d16565b6040805182815290517f4cccd9748bff0341d9852cc61d82652a3003dcebea088f05388c0be1f26b4c8a9181900360200190a150565b5490565b80151561187b576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613d655760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613d938161558a565b613d9b612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613dd557600080fd5b505af1158015613de9573d6000803e3d6000fd5b505050506040513d6020811015613dff57600080fd5b5051600160a060020a038281169116141561187b576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613e6c611c77565b811461187b576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008183116131f857816118d3565b613ed96159f9565b50600090815260208181526040918290208251918201909252600490910154815290565b6000918252602082905260409091209051600490910155565b6113968383613f3584613f298888612ff1565b9063ffffffff61323d16565b613009565b51600155565b613f486159f9565b506040805160208101909152600154815290565b600882046010820481148015613f73575060088306155b8015613f80575060108206155b1515612feb576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b613fde6159f9565b6000806000613feb6159f9565b6000613ff689613211565b955061400986600163ffffffff612ff116565b94508488141561401857613425565b868061402357508488115b151561409f576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6140b086600363ffffffff612ff116565b93506140cc60006140c08b612f80565b9063ffffffff612ff116565b9250828410156140d857fe5b6140e68385038911156132d7565b6140f88660018a63ffffffff61300916565b6141028987613d71565b6040805189815290518a917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a2614140613f40565b915061414c888661526d565b90508488111561416d576141688260018363ffffffff613f1616565b61417f565b61417f8260018363ffffffff61528416565b61418882613f3a565b613425896130a9565b6141996159f9565b60006141a36159f9565b60008060006141b188612f80565b95506141c486600063ffffffff612ff116565b9450848714156141d3576142d7565b6141dc88613211565b93506141ef84600163ffffffff612ff116565b925061420284600363ffffffff612ff116565b91508282101561420e57fe5b61421c8383038811156132d7565b61422d86600163ffffffff612ff116565b905080871115801561423e57508085115b1561426057614260600261425061187e565b889190420163ffffffff61300916565b6142728660008963ffffffff61300916565b61427c8887613090565b877f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb88836142b18a600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a26142d7886130a9565b5050505050505050565b600080600160a060020a03831615156142fd576000915061117b565b50506000903b1190565b60408051600160a060020a0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905260009081806143878684615650565b60408051808201909152601c81527f534146455f4552435f32305f42414c414e43455f524556455254454400000000602082015291935091508215156144125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5095945050505050565b60408051600160a060020a038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260009061449e8582615681565b95945050505050565b8051602002815290565b6144b96159f9565b60006144c36159f9565b60006144ce86612f80565b93506144e184600163ffffffff612ff116565b9250828514156144f057610fa1565b6144f986613211565b915061451761450f83600363ffffffff612ff116565b8611156132d7565b61452884600063ffffffff612ff116565b905080851015801561453957508083105b1561455b5761455b600261454b61187e565b869190420163ffffffff61300916565b61456d8460018763ffffffff61300916565b6145778685613090565b857f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb82876145ac88600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a2610fa1866130a9565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561460257fe5b60209081029091010152805182908290600190811061461d57fe5b6020908102909101015292915050565b61177a61364633848461267c565b60008060008061464a856156cf565b919790965090869003945092505050565b600060608060006060600080600080600080614675611a6d565b9750876040519080825280602002602001820160405280156146a1578160200160208202803883390190505b509950876040519080825280602002602001820160405280156146ce578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146fb578160200160208202803883390190505b5096506147066128a8565b94505b8481101561478d5761471a816156cf565b9550935091508284141561472d57614785565b808a8781518110151561473c57fe5b602090810290910101528851828403908a908890811061475857fe5b6020908102909101015286518285039088908890811061477457fe5b602090810290910101526001909501945b600101614709565b8515156147b557604080516000808252602082018181528284019093529c509a5098506147de565b878610156147c757858a528589528587525b6147d289888e615754565b9a508a8c10156147de57fe5b50505050505050509193909250565b6060806000806000806147fe6159f9565b60006148086159f9565b6148118c61332e565b9099509750600096505b8a5182101561498c576148448b8381518110151561483557fe5b90602001906020020151613211565b925061485783600363ffffffff612ff116565b9550898281518110151561486757fe5b6020908102909101015161488284600163ffffffff612ff116565b0194508585141561489257614981565b85851161489b57fe5b85850393506148db8b838151811015156148b157fe5b60209081029091010151600080516020615b1a8339815191529088878d8d8d63ffffffff6133b916565b8a51968401968b90839081106148ed57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a26149408360038763ffffffff61300916565b6149618b8381518110151561495157fe5b9060200190602002015184613d71565b6149818b8381518110151561497257fe5b906020019060200201516130a9565b81600101915061481b565b868c1461499557fe5b61499d613f40565b90506149b18160038963ffffffff613f1616565b6149ba81613f3a565b50505050505050935093915050565b600080600060608060606000806149de612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614a1857600080fd5b505af1158015614a2c573d6000803e3d6000fd5b505050506040513d6020811015614a4257600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919850600160a060020a0389169163f5eb42dc916024808201926020929091908290030181600087803b158015614aa957600080fd5b505af1158015614abd573d6000803e3d6000fd5b505050506040513d6020811015614ad357600080fd5b50519550851515614ae357614e4f565b614aec86611528565b9450945094505b8451811015614d565760028482815181101515614b0c57fe5b906020019060200201511015614b2157614d4e565b8281815181101515614b2f57fe5b9060200190602002015115614bf95760018482815181101515614b4e57fe5b602090810290910101805190911c90528351614b8790859083908110614b7057fe5b60209081029091010151839063ffffffff61323d16565b91508481815181101515614b9757fe5b90602001906020020151600160a060020a03167fe915a473fc2ef8e0231da98380f853b2aeea117a4392c67e753c54186bfbbd128583815181101515614bd957fe5b906020019060200201516040518082815260200191505060405180910390a25b86600160a060020a0316638fcb4e5b8683815181101515614c1657fe5b906020019060200201518684815181101515614c2e57fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015614c8757600080fd5b505af1158015614c9b573d6000803e3d6000fd5b505050506040513d6020811015614cb157600080fd5b50508351614cdc90859083908110614cc557fe5b60209081029091010151899063ffffffff61323d16565b97508481815181101515614cec57fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece0868583815181101515614d2e57fe5b906020019060200201516040518082815260200191505060405180910390a25b600101614af3565b6000821115614e4f57614d67612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614da157600080fd5b505af1158015614db5573d6000803e3d6000fd5b505050506040513d6020811015614dcb57600080fd5b5051604080517f46114928000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a039092169163461149289160448082019260009290919082900301818387803b158015614e3657600080fd5b505af1158015614e4a573d6000803e3d6000fd5b505050505b5050505050505090565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff166129b1828015614e8d5750815b806136465750613646857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ec187615784565b61267c565b6000806000606060008088118015614ef5575067ffffffffffffffff614ef28a8a63ffffffff61323d16565b11155b1515614f4b576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614f5c88603063ffffffff6136a216565b8751148015614f7b5750614f7788606063ffffffff6136a216565b8651145b1515614fd1576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b8781101561516a5761500d8b8b8b63ffffffff61529716565b60308281028901602081810151918301519286018390528501819052919550171592508215615086576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561512b578181015183820152602001615113565b50505050905090810190601f1680156151585780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614ff4565b50969998505050505050505050565b6000806151846159f9565b61518c6159f9565b600061519786613211565b92506151a286613ed1565b91506151b583600363ffffffff612ff116565b90506151c883600063ffffffff612ff116565b93506151d386612e20565b15156151e15780935061522f565b6151f282600063ffffffff612ff116565b1561522f5761522c816152278661521086600163ffffffff612ff116565b61522188600163ffffffff612ff116565b016131e9565b613ec2565b93505b61524082600263ffffffff612ff116565b94508385146152655761525b8260028663ffffffff61300916565b6152658683613efd565b505050915091565b600081831161527e578282036118d3565b50900390565b6113968383613f3584611d108888612ff1565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b602083106152f35780518252601f1990920191602091820191016152d4565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561535257508661534f8a8a63ffffffff61323d16565b11155b8015615366575067ffffffffffffffff8711155b15156153bc576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b88811115615524576154008b8b600019840163ffffffff61529716565b9450600185015460801c6030830152845460208301528681101561545a576154338b8b6000198a0163ffffffff61529716565b9350600092505b600583101561545657828401548386015560018301925061543a565b8394505b600092505b60058310156154795760008386015560018301925061545f565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156154e55781810151838201526020016154cd565b50505050905090810190601f1680156155125780820380516001836020036101000a031916815260200191505b509250505060405180910390a26153e3565b50949998505050505050505050565b80151561187b576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038116151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b61561a7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613d6d16565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000806000806040516020818751602089018a5afa9250600083111561567557805191505b50909590945092505050565b6000806040516020818551602087016000895af160008111156156c5573d80156156b257602081146156bb576156c3565b600193506156c3565b600183511493505b505b5090949350505050565b60008060006156dc6159f9565b6156e46159f9565b6156ed86613211565b91506156f886613ed1565b905061570b82600163ffffffff612ff116565b945061571e82600363ffffffff612ff116565b935061573181600263ffffffff612ff116565b92508383101580156157435750848410155b151561574b57fe5b50509193909250565b6000805b828210156127c15761576d85858486036157c5565b905080151561577b576127c1565b90810190615758565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156157b657fe5b60209081029091010152919050565b8251600090600019828080808715156157e157600096506159c4565b600092505b895183101561589f5788838151811015156157fd57fe5b906020019060200201518a8481518110151561581557fe5b602090810290910101511061582957615894565b898381518110151561583757fe5b9060200190602002015185111561586e5782955060019350898381518110151561585d57fe5b906020019060200201519450615894565b898381518110151561587c57fe5b90602001906020020151851415615894576001840193505b8260010192506157e6565b8315156158af57600096506159c4565b50600019905060005b895181101561595f5788818151811015156158cf57fe5b906020019060200201518a828151811015156158e757fe5b60209081029091010151106158fb57615957565b848a8281518110151561590a57fe5b906020019060200201511180156159375750818a8281518110151561592b57fe5b90602001906020020151105b1561595757898181518110151561594a57fe5b9060200190602002015191505b6001016158b8565b6159a260018511615970578861597a565b61597a89866159d1565b8661599c858d8b81518110151561598d57fe5b906020019060200201516131e9565b036131e9565b9650868a878151811015156159b357fe5b602090810290910101805190910190525b5050505050509392505050565b600082156159f05781600184038115156159e757fe5b046001016118d3565b50600092915050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615a4c5782800160ff19823516178555615a79565b82800160010185558215615a79579182015b82811115615a79578235825591602001919060010190615a5e565b50610f3492610fd79250905b80821115610f345760008155600101615a85560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a72305820c0673b4dec33ab43c022a0ca404b407602f0e9ac1e02e04efbe56d5c3734569c0029ebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e
Deployed Bytecode
0x6080604052600436106102d95760003560e01c63ffffffff1680630803fac0146102de578063096b7b351461030757806315dae03e1461033d5780632914b9bd1461036457806330a90f01146103d957806332f0a3b5146103f15780634febc81b1461040657806359e25c12146104715780635ddde810146105ae5780635e2fb908146105cc5780635e57d742146105e457806362dcfda11461060857806365cc369a146106e5578063684560a2146107005780636ccc7562146107275780636da7d0a71461073f5780636ef355f1146107545780637038b141146105ae57806375049ad81461076f57806375a080d5146107875780637e7db6e11461079f57806380231f15146107c0578063805911ae1461030757806380afdea8146107d55780638469cbd3146107ea57806385fa63d7146107ff5780638aa104351461082d5780638b3dd749146108425780638ca7c052146108575780638d7e40171461086f5780638ece99951461088757806390c09bdb1461089c57806391dcd6b2146108b1578063973e9328146108c95780639a56983c146108ed5780639a7c2ade146109c95780639abddf09146109f05780639b00c14614610a235780639b3d190014610a4f5780639d4941d814610a7b578063a1658fad14610a9c578063a2e080f114610b03578063a479e50814610b1e578063a70c70e414610b33578063a9e7a84614610b48578063ae962acf14610b68578063b3076c3c14610b8d578063b449402a14610be8578063b497183314610cec578063bee41b5814610d01578063d07442f114610e03578063d087d28814610e03578063d4aae0c414610e18578063d8343dcb14610e2d578063d8e71cd114610e42578063db9887ea14610e57578063de4796ed14610e6f578063e204d09b14610e84578063e864299e14610e99578063ec5af3a414610eae578063ed5cfa4114610754578063f2e2ca6314610ec3578063f31bd9c114610ee1578063fbc77ef114610ef6575b600080fd5b3480156102ea57600080fd5b506102f3610f0e565b604080519115158252519081900360200190f35b34801561031357600080fd5b5061033b60048035906024803591604435808301929082013591606435918201910135610f38565b005b34801561034957600080fd5b50610352610fa9565b60408051918252519081900360200190f35b34801561037057600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526103bd943694929360249392840191908190840183828082843750949750610fda9650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156103e557600080fd5b506102f36004356110bd565b3480156103fd57600080fd5b506103bd611181565b34801561041257600080fd5b506104216004356024356111f6565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561045d578181015183820152602001610445565b505050509050019250505060405180910390f35b34801561047d57600080fd5b5061048f60043560243560443561128d565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156104d45781810151838201526020016104bc565b50505050905090810190601f1680156105015780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b8381101561053457818101518382015260200161051c565b50505050905090810190601f1680156105615780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561059657818101518382015260200161057e565b50505050905001965050505050505060405180910390f35b3480156105ba57600080fd5b5061033b60043560243560443561138b565b3480156105d857600080fd5b506102f360043561139b565b3480156105f057600080fd5b5061033b6004803590602480359081019101356113b0565b34801561061457600080fd5b50610620600435611528565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610668578181015183820152602001610650565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156106a757818101518382015260200161068f565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561059657818101518382015260200161057e565b3480156106f157600080fd5b5061033b600435602435611759565b34801561070c57600080fd5b5061033b600160a060020a036004351660243560443561177e565b34801561073357600080fd5b5061033b60043561185b565b34801561074b57600080fd5b5061035261187e565b34801561076057600080fd5b5061033b6004356024356118a9565b34801561077b57600080fd5b506102f36004356118b5565b34801561079357600080fd5b5061033b6004356118da565b3480156107ab57600080fd5b506102f3600160a060020a0360043516611a2a565b3480156107cc57600080fd5b50610352611a30565b3480156107e157600080fd5b50610352611a42565b3480156107f657600080fd5b50610352611a6d565b34801561080b57600080fd5b506103526024600480358281019291013590600160a060020a03903516611a86565b34801561083957600080fd5b50610352611c77565b34801561084e57600080fd5b50610352611ca2565b34801561086357600080fd5b50610352600435611ccd565b34801561087b57600080fd5b5061033b600435611d1c565b34801561089357600080fd5b50610352611d33565b3480156108a857600080fd5b5061033b611d45565b3480156108bd57600080fd5b5061033b600435611d7f565b3480156108d557600080fd5b5061033b600435600160a060020a0360243516611e32565b3480156108f957600080fd5b5061090a6004356024351515611efe565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610988578181015183820152602001610970565b50505050905090810190601f1680156109b55780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b3480156109d557600080fd5b5061033b600160a060020a0360043516602435604435612051565b3480156109fc57600080fd5b50610a05612278565b60408051938452602084019290925282820152519081900360600190f35b348015610a2f57600080fd5b5061033b60246004803582810192908201359181359182019101356122d2565b348015610a5b57600080fd5b5061033b602460048035828101929082013591813591820191013561236b565b348015610a8757600080fd5b5061033b600160a060020a03600435166123ed565b348015610aa857600080fd5b5060408051602060046044358181013583810280860185019096528085526102f3958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061267c9650505050505050565b348015610b0f57600080fd5b5061033b6004356024356127c9565b348015610b2a57600080fd5b506103bd6127f3565b348015610b3f57600080fd5b506103526128a8565b348015610b5457600080fd5b5061033b60043560243515156044356128d3565b348015610b7457600080fd5b5061033b60043567ffffffffffffffff602435166129b7565b348015610b9957600080fd5b50610ba5600435612adc565b6040805198151589526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610bf457600080fd5b50610c03600435602435612b9a565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610c4e578181015183820152602001610c36565b50505050905090810190601f168015610c7b5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610cae578181015183820152602001610c96565b50505050905090810190601f168015610cdb5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610cf857600080fd5b50610352612bd8565b348015610d0d57600080fd5b50610d25600480359060248035908101910135612bdd565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610d66578181015183820152602001610d4e565b50505050905090810190601f168015610d935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610dc6578181015183820152602001610dae565b50505050905090810190601f168015610df35780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610e0f57600080fd5b50610352612cab565b348015610e2457600080fd5b506103bd612cd6565b348015610e3957600080fd5b506103bd612d01565b348015610e4e57600080fd5b50610352612d2c565b348015610e6357600080fd5b50610352600435612d50565b348015610e7b57600080fd5b506102f3612d7f565b348015610e9057600080fd5b50610352612d92565b348015610ea557600080fd5b5061033b612d9a565b348015610eba57600080fd5b50610352612db9565b348015610ecf57600080fd5b5061033b600435602435604435612dbe565b348015610eed57600080fd5b50610352612dfc565b348015610f0257600080fd5b506102f3600435612e20565b600080610f19611ca2565b90508015801590610f31575080610f2e612e5f565b10155b91505b5090565b610fa1868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750612e63945050505050565b505050505050565b6000610fd47fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0612f7c565b90505b90565b6000610fe46127f3565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561103f578181015183820152602001611027565b50505050905090810190601f16801561106c5780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b15801561108b57600080fd5b505af115801561109f573d6000803e3d6000fd5b505050506040513d60208110156110b557600080fd5b505192915050565b60006110c76159f9565b6110d083612f80565b90506110db81612fac565b1580156110f757506110f481600263ffffffff612ff116565b15155b151561114d576040805160e560020a62461bcd02815260206004820152601260248201527f43414e545f434c4541525f50454e414c54590000000000000000000000000000604482015290519081900360640190fd5b611160816002600063ffffffff61300916565b61116a8382613090565b611173836130a9565b61117b613120565b50919050565b600061118b612cd6565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111c557600080fd5b505af11580156111d9573d6000803e3d6000fd5b505050506040513d60208110156111ef57600080fd5b5051905090565b60606000806112036128a8565b91508185101580611212575083155b1561121c57611285565b611228848684036131e9565b604051908082528060200260200182016040528015611251578160200160208202803883390190505b509250600090505b825181101561128557808501838281518110151561127357fe5b60209081029091010152600101611259565b505092915050565b606080606061129a6159f9565b6000806112a6896131ff565b6112af89613211565b92506112dc6112c584600263ffffffff612ff116565b6112d58a8a63ffffffff61323d16565b11156132d7565b6112ed83600363ffffffff612ff116565b91506112f88761332e565b604080518a81526020808c028201019091529197509550878015611326578160200160208202803883390190505b50935061134d600080516020615b1a8339815191528a8a8a8a8a600063ffffffff6133b916565b8681101561137f578181890110848281518110151561136857fe5b91151560209283029091019091015260010161134d565b50505093509350939050565b611396838383613430565b505050565b60009081526020819052604090205460ff1690565b6113e982828080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b6113f2836131ff565b611409600080516020615a9a83398151915261360d565b6114b5828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561149d5780601f1061147b57610100808354040283529182019161149d565b820191906000526020600020905b815481529060010190602001808311611489575b5050915050604051809103902060001916141561364b565b60008381526020819052604090206114d1906001018383615a0b565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b606080606060008060008061153b6159f9565b6000806000806115496128a8565b9850611553611a6d565b97508760405190808252806020026020018201604052801561157f578160200160208202803883390190505b509b50876040519080825280602002602001820160405280156115ac578160200160208202803883390190505b509a50876040519080825280602002602001820160405280156115d9578160200160208202803883390190505b50995060009650600095505b888410156116da576115f68461139b565b1515611601576116cf565b61160a84613211565b945061161d85600163ffffffff612ff116565b925061163085600363ffffffff612ff116565b91508282101561163c57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d908990811061167157fe5b600160a060020a039092166020928302909101909101528a5181908c908990811061169857fe5b602090810290910101526116ab846118b5565b8a888151811015156116b957fe5b9115156020928302909101909101526001909601955b8360010193506115e5565b8515156116e657611749565b600096505b87871015611749578561171c8e8d8a81518110151561170657fe5b602090810290910101519063ffffffff6136a216565b81151561172557fe5b048b8881518110151561173457fe5b602090810290910101526001909601956116eb565b5050505050505050509193909250565b611770600080516020615a9a83398151915261360d565b61177a828261374d565b5050565b611786611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156118475760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561180c5781810151838201526020016117f4565b50505050905090810190601f1680156118395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506118538383836138f0565b611396613b40565b611872600080516020615a9a83398151915261360d565b61187b81613c08565b50565b6000610fd47f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e612f7c565b61177a82826001613430565b60006118bf6159f9565b6118c883612f80565b90506118d381612fac565b9392505050565b60006118e46159f9565b6000806118f0856131ff565b611907600080516020615a9a83398151915261360d565b6119186119138661139b565b613c82565b611920611a6d565b935061195161193685600163ffffffff613cd916565b600080516020615aba8339815191529063ffffffff613d6d16565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a26119a285613211565b92506119b583600063ffffffff612ff116565b91506119c883600363ffffffff612ff116565b905080821115611a1b576119e48360008363ffffffff61300916565b6119ee8584613d71565b6040805182815290518691600080516020615afa833981519152919081900360200190a2611a1b856130a9565b611a23613120565b5050505050565b50600190565b600080516020615ada83398151915281565b6000610fd47fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b612f7c565b6000610fd4600080516020615aba833981519152612f7c565b6000806000611ac486868080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b611acd84613d8a565b611ae4600080516020615a9a83398151915261360d565b611aec6128a8565b925060c88310611b46576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611b797fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613d6d16565b60008381526020819052604090209150611b91611a6d565b9050611bb4600080516020615aba8339815191526001830163ffffffff613d6d16565b815460ff191660019081178355611bce9083018787615a0b565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b6000610fd47f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6612f7c565b6000610fd47febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e612f7c565b6000611cd76159f9565b611ce0836131ff565b611ce983613211565b90506118d3611cff82600363ffffffff612ff116565b611d1083600263ffffffff612ff116565b9063ffffffff613cd916565b61187b600080516020615ada83398151915261360d565b600080516020615a9a83398151915281565b6000611d5e600080516020615ada83398151915261360d565b611d666128a8565b9050600081111561187b5761187b60006001830361374d565b611d88816131ff565b611d9f600080516020615a9a83398151915261360d565b611db1611dab8261139b565b15613c82565b611dda611dbc611a6d565b600080516020615aba8339815191529060010163ffffffff613d6d16565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261187b613120565b611e3b81613d8a565b611e44826131ff565b611e5b600080516020615a9a83398151915261360d565b600082815260208190526040902054611e8790600160a060020a0383811661010090920416141561364b565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b60006060600080600080600080611f136159f9565b611f1c8b6131ff565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089611f5d57604080516020810190915260008152611fea565b60018281018054604080516020600295841615610100026000190190931694909404601f810183900483028501830190915280845290830182828015611fe45780601f10611fb957610100808354040283529160200191611fe4565b820191906000526020600020905b815481529060010190602001808311611fc757829003601f168201915b50505050505b9750611ff58b613211565b905061200881600063ffffffff612ff116565b955061201b81600163ffffffff612ff116565b945061202e81600263ffffffff612ff116565b935061204181600363ffffffff612ff116565b9250505092959891949750929550565b600061205b6159f9565b6120636159f9565b61206b6159f9565b600080600080600061207b610f0e565b15156120d1576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6120db6000613e64565b6120e68c8c8c6138f0565b6120ee6128a8565b9850602060405190810160405280600081525095505b888210156122595761211582613211565b975061212888600063ffffffff612ff116565b945061213b88600263ffffffff612ff116565b935061214e88600363ffffffff612ff116565b60008381526020819052604090205490935060ff161515612170575081612186565b6121838461217e8588613ec2565b6131e9565b90505b8481146121ce5761219f8860008363ffffffff61300916565b6121a98289613d71565b6040805182815290518391600080516020615afa833981519152919081900360200190a25b6121d782613ed1565b96506121eb8760028363ffffffff61300916565b6121f58288613efd565b6122078660008363ffffffff613f1616565b6122198660038563ffffffff613f1616565b61223c600161222e8a8263ffffffff612ff116565b88919063ffffffff613f1616565b61224e8660028663ffffffff613f1616565b816001019150612104565b61226286613f3a565b61226a613120565b505050505050505050505050565b60008060006122856159f9565b61228d613f40565b90506122a081600163ffffffff612ff116565b93506122b381600363ffffffff612ff116565b92506122ca83611d1083600063ffffffff612ff116565b915050909192565b60008080808080806122f1600080516020615ada83398151915261360d565b6122fb8a89613f5c565b96506123056128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123458686106132d7565b61235185856000613fd6565b612317565b61235e613120565b5050505050505050505050565b600080808080808061238a600080516020615ada83398151915261360d565b6123948a89613f5c565b965061239e6128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123de8686106132d7565b6123e88585614191565b6123b0565b60008060006123fb84611a2a565b60408051808201909152601281527f5245434f5645525f444953414c4c4f574544000000000000000000000000000060208201529015156124815760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5061248a611181565b9250612495836142e1565b60408051808201909152601a81527f5245434f5645525f5641554c545f4e4f545f434f4e5452414354000000000000602082015290151561251b5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50600160a060020a038416151561256c5760405130319250600160a060020a0384169083156108fc029084906000818181858888f19350505050158015612566573d6000803e3d6000fd5b5061262b565b5082612587600160a060020a0382163063ffffffff61430716565b91506125a3600160a060020a038216848463ffffffff61441c16565b60408051808201909152601d81527f5245434f5645525f544f4b454e5f5452414e534645525f4641494c454400000060208201529015156126295760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505b83600160a060020a031683600160a060020a03167f596caf56044b55fb8c4ca640089bbc2b63cae3e978b851f5745cbb7c5b288e02846040518082815260200191505060405180910390a350505050565b600080612687610f0e565b151561269657600091506127c1565b61269e612cd6565b9050600160a060020a03811615156126b957600091506127c1565b80600160a060020a031663fdef91068630876126d4886144a7565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561274357818101518382015260200161272b565b50505050905090810190601f1680156127705780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561279257600080fd5b505af11580156127a6573d6000803e3d6000fd5b505050506040513d60208110156127bc57600080fd5b505191505b509392505050565b6127d2826131ff565b6127e9600080516020615ada83398151915261360d565b61177a82826144b1565b6000806127fe612cd6565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b15801561108b57600080fd5b6000610fd47fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8612f7c565b6128db6159f9565b6128e4846131ff565b6128fb600080516020615ada83398151915261360d565b61290f67ffffffffffffffff8311156132d7565b61291884613ed1565b905061293f60008461292b57600061292e565b60015b83919060ff1663ffffffff61300916565b612960600184612950576000612952565b835b83919063ffffffff61300916565b61296a8482613efd565b60408051838152905185917fd50ea115db6f0b433ef9cc4b71110dbd9202364a00488be90718990be5bf16a6919081900360200190a26129a9846130a9565b6129b1613120565b50505050565b6129bf6159f9565b6000806000806129ce876131ff565b612a0b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612a068967ffffffffffffffff8a166145d2565b61462d565b612a176119138861139b565b612a2087613211565b9450612a3385600063ffffffff612ff116565b9350612a4685600363ffffffff612ff116565b9250612a5985600263ffffffff612ff116565b9150612a738261217e8867ffffffffffffffff1686613ec2565b905083811415612a8257612ad3565b612a948560008363ffffffff61300916565b612a9e8786613d71565b6040805182815290518891600080516020615afa833981519152919081900360200190a2612acb876130a9565b612ad3613120565b50505050505050565b600080600080600080600080612af06159f9565b612af86159f9565b612b018b6131ff565b612b0a8b613ed1565b9150612b158b612f80565b9050612b2882600063ffffffff612ff116565b15159950612b3d82600163ffffffff612ff116565b9850612b5081600063ffffffff612ff116565b9750612b6381600163ffffffff612ff116565b9650612b7681600263ffffffff612ff116565b9550612b818b61463b565b8095508196508297505050505050919395975091939597565b60608060006060612bad8686600161128d565b8051929650909450915081906000908110612bc457fe5b906020019060200201519150509250925092565b60ff81565b60608060008180612bfb600080516020615ada83398151915261360d565b871515612c21576040805160008082526020820190815281830190925295509350612ca0565b612c2a8861465b565b91945092509050878314612c88576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612c938383836147ed565b9095509350612ca0613120565b505050935093915050565b6000610fd47fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b6000610fd47f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b612f7c565b6000610fd47ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537612f7c565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612d5a6159f9565b612d63836131ff565b612d6c83613211565b90506118d381600263ffffffff612ff116565b6000600019612d8c611ca2565b14905090565b6301e1338081565b612db1600080516020615ada83398151915261360d565b61187b6149c9565b60c881565b612dc7836131ff565b612dde600080516020615ada83398151915261360d565b612de88382614191565b612df483836001613fd6565b611396613120565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b6000612e2a6159f9565b612e3383612f80565b9050612e3e81612fac565b1580156118d35750612e5781600263ffffffff612ff116565b159392505050565b4390565b612e6b6159f9565b6000612e756159f9565b612e7e876131ff565b612e883388614e59565b612ea78615801590612ea2575067ffffffffffffffff8711155b6132d7565b612eb087613211565b9250612ec383600263ffffffff612ff116565b9150612ee167ffffffffffffffff6112d5848963ffffffff61323d16565b612f03600080516020615b1a833981519152888489898963ffffffff614ec616565b60408051828152905191935088917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a2612f4d8360028463ffffffff61300916565b612f578784613d71565b612f5f613f40565b9050612f738160028863ffffffff613f1616565b612acb81613f3a565b5490565b612f886159f9565b50600090815260208181526040918290208251918201909252600390910154815290565b6000612fbe828263ffffffff612ff116565b612fcf83600163ffffffff612ff116565b1080612feb5750612fe782600263ffffffff612ff116565b4211155b92915050565b905167ffffffffffffffff604090920260ff161c1690565b67ffffffffffffffff811115613069576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600390910155565b6000806130b46159f9565b60006130bf85615179565b93509350838314156130d057611a23565b6130d8613f40565b91506130e4838561526d565b905083831115613105576131008260008363ffffffff613f1616565b613117565b6131178260008363ffffffff61528416565b611a2382613f3a565b600061314b7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b60010190506131807fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613d6d16565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b60008183106131f857816118d3565b5090919050565b61187b61320a6128a8565b82106132d7565b6132196159f9565b50600090815260208181526040918290208251918201909252600290910154815290565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b508091505b5092915050565b80151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b60608061334283603063ffffffff6136a216565b6040519080825280601f01601f19166020018201604052801561336f578160200160208202803883390190505b5061338184606063ffffffff6136a216565b6040519080825280601f01601f1916602001820160405280156133ae578160200160208202803883390190505b509092509050915091565b6000805b85811015613425576133d8898989840163ffffffff61529716565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016133bd565b505050505050505050565b6134386159f9565b6000806134436159f9565b61344c876131ff565b6134563388614e59565b84151561346257612ad3565b61346b87613211565b935061347e84600263ffffffff612ff116565b92506134af61349485600363ffffffff612ff116565b8710158015612ea25750836112d5888863ffffffff61323d16565b6134d0600080516020615b1a8339815191528888888763ffffffff61532a16565b92506134e48460028563ffffffff61300916565b60408051848152905188917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261352b84600063ffffffff612ff116565b91508186101561356c576135478460008863ffffffff61300916565b6040805187815290518891600080516020615afa833981519152919081900360200190a25b6135768785613d71565b61357e613f40565b90506135928160028763ffffffff61528416565b61359b81613f3a565b612acb876130a9565b600081511180156135b7575060ff815111155b151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b61187b61364633836000604051908082528060200260200182016040528015613640578160200160208202803883390190505b5061267c565b615533565b80151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156136b557600091506132d0565b508282028284828115156136c557fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b60008060008061375b6159f9565b60006137656159f9565b613782888a11158015612ea2575061377b6128a8565b89106132d7565b8891505b8782116138ba5761379682613211565b92506137a983600263ffffffff612ff116565b94506137bc83600363ffffffff612ff116565b9350838514156137cb576138af565b8385116137d457fe5b8385039650948601946137ef8360028663ffffffff61300916565b6138018360008663ffffffff61300916565b61380b8284613d71565b613814826130a9565b60408051858152905183917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a26040805185815290518391600080516020615afa833981519152919081900360200190a26040805167ffffffffffffffff89168152905183917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b816001019150613786565b6000861115613425576138cb613f40565b90506138df8160028863ffffffff61528416565b6138e881613f3a565b613425613120565b6138f98361558a565b6139297ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378463ffffffff613d6d16565b6139597fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08363ffffffff613d6d16565b61396360026155ea565b61396c81613c08565b613974612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156139ae57600080fd5b505af11580156139c2573d6000803e3d6000fd5b505050506040513d60208110156139d857600080fd5b5051600160a060020a031663095ea7b36139f0612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613a2a57600080fd5b505af1158015613a3e573d6000803e3d6000fd5b505050506040513d6020811015613a5457600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260001960248301525160448083019260209291908290030181600087803b158015613aa057600080fd5b505af1158015613ab4573d6000803e3d6000fd5b505050506040513d6020811015613aca57600080fd5b505060408051600160a060020a038516815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805183815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a1505050565b613b48611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613bcd5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50613c06613bd9612e5f565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613d6d16565b565b613c186301e133808211156132d7565b613c487f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e8263ffffffff613d6d16565b6040805182815290517f4cccd9748bff0341d9852cc61d82652a3003dcebea088f05388c0be1f26b4c8a9181900360200190a150565b5490565b80151561187b576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613d655760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613d938161558a565b613d9b612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613dd557600080fd5b505af1158015613de9573d6000803e3d6000fd5b505050506040513d6020811015613dff57600080fd5b5051600160a060020a038281169116141561187b576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613e6c611c77565b811461187b576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008183116131f857816118d3565b613ed96159f9565b50600090815260208181526040918290208251918201909252600490910154815290565b6000918252602082905260409091209051600490910155565b6113968383613f3584613f298888612ff1565b9063ffffffff61323d16565b613009565b51600155565b613f486159f9565b506040805160208101909152600154815290565b600882046010820481148015613f73575060088306155b8015613f80575060108206155b1515612feb576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b613fde6159f9565b6000806000613feb6159f9565b6000613ff689613211565b955061400986600163ffffffff612ff116565b94508488141561401857613425565b868061402357508488115b151561409f576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6140b086600363ffffffff612ff116565b93506140cc60006140c08b612f80565b9063ffffffff612ff116565b9250828410156140d857fe5b6140e68385038911156132d7565b6140f88660018a63ffffffff61300916565b6141028987613d71565b6040805189815290518a917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a2614140613f40565b915061414c888661526d565b90508488111561416d576141688260018363ffffffff613f1616565b61417f565b61417f8260018363ffffffff61528416565b61418882613f3a565b613425896130a9565b6141996159f9565b60006141a36159f9565b60008060006141b188612f80565b95506141c486600063ffffffff612ff116565b9450848714156141d3576142d7565b6141dc88613211565b93506141ef84600163ffffffff612ff116565b925061420284600363ffffffff612ff116565b91508282101561420e57fe5b61421c8383038811156132d7565b61422d86600163ffffffff612ff116565b905080871115801561423e57508085115b1561426057614260600261425061187e565b889190420163ffffffff61300916565b6142728660008963ffffffff61300916565b61427c8887613090565b877f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb88836142b18a600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a26142d7886130a9565b5050505050505050565b600080600160a060020a03831615156142fd576000915061117b565b50506000903b1190565b60408051600160a060020a0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905260009081806143878684615650565b60408051808201909152601c81527f534146455f4552435f32305f42414c414e43455f524556455254454400000000602082015291935091508215156144125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5095945050505050565b60408051600160a060020a038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260009061449e8582615681565b95945050505050565b8051602002815290565b6144b96159f9565b60006144c36159f9565b60006144ce86612f80565b93506144e184600163ffffffff612ff116565b9250828514156144f057610fa1565b6144f986613211565b915061451761450f83600363ffffffff612ff116565b8611156132d7565b61452884600063ffffffff612ff116565b905080851015801561453957508083105b1561455b5761455b600261454b61187e565b869190420163ffffffff61300916565b61456d8460018763ffffffff61300916565b6145778685613090565b857f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb82876145ac88600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a2610fa1866130a9565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561460257fe5b60209081029091010152805182908290600190811061461d57fe5b6020908102909101015292915050565b61177a61364633848461267c565b60008060008061464a856156cf565b919790965090869003945092505050565b600060608060006060600080600080600080614675611a6d565b9750876040519080825280602002602001820160405280156146a1578160200160208202803883390190505b509950876040519080825280602002602001820160405280156146ce578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146fb578160200160208202803883390190505b5096506147066128a8565b94505b8481101561478d5761471a816156cf565b9550935091508284141561472d57614785565b808a8781518110151561473c57fe5b602090810290910101528851828403908a908890811061475857fe5b6020908102909101015286518285039088908890811061477457fe5b602090810290910101526001909501945b600101614709565b8515156147b557604080516000808252602082018181528284019093529c509a5098506147de565b878610156147c757858a528589528587525b6147d289888e615754565b9a508a8c10156147de57fe5b50505050505050509193909250565b6060806000806000806147fe6159f9565b60006148086159f9565b6148118c61332e565b9099509750600096505b8a5182101561498c576148448b8381518110151561483557fe5b90602001906020020151613211565b925061485783600363ffffffff612ff116565b9550898281518110151561486757fe5b6020908102909101015161488284600163ffffffff612ff116565b0194508585141561489257614981565b85851161489b57fe5b85850393506148db8b838151811015156148b157fe5b60209081029091010151600080516020615b1a8339815191529088878d8d8d63ffffffff6133b916565b8a51968401968b90839081106148ed57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a26149408360038763ffffffff61300916565b6149618b8381518110151561495157fe5b9060200190602002015184613d71565b6149818b8381518110151561497257fe5b906020019060200201516130a9565b81600101915061481b565b868c1461499557fe5b61499d613f40565b90506149b18160038963ffffffff613f1616565b6149ba81613f3a565b50505050505050935093915050565b600080600060608060606000806149de612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614a1857600080fd5b505af1158015614a2c573d6000803e3d6000fd5b505050506040513d6020811015614a4257600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919850600160a060020a0389169163f5eb42dc916024808201926020929091908290030181600087803b158015614aa957600080fd5b505af1158015614abd573d6000803e3d6000fd5b505050506040513d6020811015614ad357600080fd5b50519550851515614ae357614e4f565b614aec86611528565b9450945094505b8451811015614d565760028482815181101515614b0c57fe5b906020019060200201511015614b2157614d4e565b8281815181101515614b2f57fe5b9060200190602002015115614bf95760018482815181101515614b4e57fe5b602090810290910101805190911c90528351614b8790859083908110614b7057fe5b60209081029091010151839063ffffffff61323d16565b91508481815181101515614b9757fe5b90602001906020020151600160a060020a03167fe915a473fc2ef8e0231da98380f853b2aeea117a4392c67e753c54186bfbbd128583815181101515614bd957fe5b906020019060200201516040518082815260200191505060405180910390a25b86600160a060020a0316638fcb4e5b8683815181101515614c1657fe5b906020019060200201518684815181101515614c2e57fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015614c8757600080fd5b505af1158015614c9b573d6000803e3d6000fd5b505050506040513d6020811015614cb157600080fd5b50508351614cdc90859083908110614cc557fe5b60209081029091010151899063ffffffff61323d16565b97508481815181101515614cec57fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece0868583815181101515614d2e57fe5b906020019060200201516040518082815260200191505060405180910390a25b600101614af3565b6000821115614e4f57614d67612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614da157600080fd5b505af1158015614db5573d6000803e3d6000fd5b505050506040513d6020811015614dcb57600080fd5b5051604080517f46114928000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a039092169163461149289160448082019260009290919082900301818387803b158015614e3657600080fd5b505af1158015614e4a573d6000803e3d6000fd5b505050505b5050505050505090565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff166129b1828015614e8d5750815b806136465750613646857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ec187615784565b61267c565b6000806000606060008088118015614ef5575067ffffffffffffffff614ef28a8a63ffffffff61323d16565b11155b1515614f4b576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614f5c88603063ffffffff6136a216565b8751148015614f7b5750614f7788606063ffffffff6136a216565b8651145b1515614fd1576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b8781101561516a5761500d8b8b8b63ffffffff61529716565b60308281028901602081810151918301519286018390528501819052919550171592508215615086576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561512b578181015183820152602001615113565b50505050905090810190601f1680156151585780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614ff4565b50969998505050505050505050565b6000806151846159f9565b61518c6159f9565b600061519786613211565b92506151a286613ed1565b91506151b583600363ffffffff612ff116565b90506151c883600063ffffffff612ff116565b93506151d386612e20565b15156151e15780935061522f565b6151f282600063ffffffff612ff116565b1561522f5761522c816152278661521086600163ffffffff612ff116565b61522188600163ffffffff612ff116565b016131e9565b613ec2565b93505b61524082600263ffffffff612ff116565b94508385146152655761525b8260028663ffffffff61300916565b6152658683613efd565b505050915091565b600081831161527e578282036118d3565b50900390565b6113968383613f3584611d108888612ff1565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b602083106152f35780518252601f1990920191602091820191016152d4565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561535257508661534f8a8a63ffffffff61323d16565b11155b8015615366575067ffffffffffffffff8711155b15156153bc576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b88811115615524576154008b8b600019840163ffffffff61529716565b9450600185015460801c6030830152845460208301528681101561545a576154338b8b6000198a0163ffffffff61529716565b9350600092505b600583101561545657828401548386015560018301925061543a565b8394505b600092505b60058310156154795760008386015560018301925061545f565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156154e55781810151838201526020016154cd565b50505050905090810190601f1680156155125780820380516001836020036101000a031916815260200191505b509250505060405180910390a26153e3565b50949998505050505050505050565b80151561187b576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038116151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b61561a7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613d6d16565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000806000806040516020818751602089018a5afa9250600083111561567557805191505b50909590945092505050565b6000806040516020818551602087016000895af160008111156156c5573d80156156b257602081146156bb576156c3565b600193506156c3565b600183511493505b505b5090949350505050565b60008060006156dc6159f9565b6156e46159f9565b6156ed86613211565b91506156f886613ed1565b905061570b82600163ffffffff612ff116565b945061571e82600363ffffffff612ff116565b935061573181600263ffffffff612ff116565b92508383101580156157435750848410155b151561574b57fe5b50509193909250565b6000805b828210156127c15761576d85858486036157c5565b905080151561577b576127c1565b90810190615758565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156157b657fe5b60209081029091010152919050565b8251600090600019828080808715156157e157600096506159c4565b600092505b895183101561589f5788838151811015156157fd57fe5b906020019060200201518a8481518110151561581557fe5b602090810290910101511061582957615894565b898381518110151561583757fe5b9060200190602002015185111561586e5782955060019350898381518110151561585d57fe5b906020019060200201519450615894565b898381518110151561587c57fe5b90602001906020020151851415615894576001840193505b8260010192506157e6565b8315156158af57600096506159c4565b50600019905060005b895181101561595f5788818151811015156158cf57fe5b906020019060200201518a828151811015156158e757fe5b60209081029091010151106158fb57615957565b848a8281518110151561590a57fe5b906020019060200201511180156159375750818a8281518110151561592b57fe5b90602001906020020151105b1561595757898181518110151561594a57fe5b9060200190602002015191505b6001016158b8565b6159a260018511615970578861597a565b61597a89866159d1565b8661599c858d8b81518110151561598d57fe5b906020019060200201516131e9565b036131e9565b9650868a878151811015156159b357fe5b602090810290910101805190910190525b5050505050509392505050565b600082156159f05781600184038115156159e757fe5b046001016118d3565b50600092915050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615a4c5782800160ff19823516178555615a79565b82800160010185558215615a79579182015b82811115615a79578235825591602001919060010190615a5e565b50610f3492610fd79250905b80821115610f345760008155600101615a85560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a72305820c0673b4dec33ab43c022a0ca404b407602f0e9ac1e02e04efbe56d5c3734569c0029
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.