ETH Price: $3,389.42 (+4.87%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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
File 1 of 33 : NodeOperatorsRegistry.sol
// 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");
    }
}

File 2 of 33 : ACLSyntaxSugar.sol
/*
 * 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));
    }
}

File 3 of 33 : IACL.sol
/*
 * 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);
}

File 4 of 33 : AppStorage.sol
/*
 * 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);
    }
}

File 5 of 33 : AragonApp.sol
/*
 * 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
    }
}

File 6 of 33 : Autopetrified.sol
/*
 * 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();
    }
}

File 7 of 33 : ConversionHelpers.sol
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)
        }
    }
}

File 8 of 33 : EtherTokenConstant.sol
/*
 * 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);
}

File 9 of 33 : Initializable.sol
/*
 * 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);
    }
}

File 10 of 33 : IsContract.sol
/*
 * 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;
    }
}

File 11 of 33 : IVaultRecoverable.sol
/*
 * 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);
}

File 12 of 33 : Petrifiable.sol
/*
 * 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);
    }
}

File 13 of 33 : ReentrancyGuard.sol
/*
 * 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);
    }
}

File 14 of 33 : SafeERC20.sol
// 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;
    }
}

File 15 of 33 : TimeHelpers.sol
/*
 * 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();
    }
}

File 16 of 33 : Uint256Helpers.sol
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);
    }
}

File 17 of 33 : UnstructuredStorage.sol
/*
 * 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) }
    }
}

File 18 of 33 : VaultRecoverable.sol
/*
 * 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);
}

File 19 of 33 : EVMScriptRunner.sol
/*
 * 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);
    }
}

File 20 of 33 : IEVMScriptExecutor.sol
/*
 * 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);
}

File 21 of 33 : IEVMScriptRegistry.sol
/*
 * 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);
}

File 22 of 33 : IKernel.sol
/*
 * 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);
}

File 23 of 33 : KernelConstants.sol
/*
 * 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;
}

File 24 of 33 : SafeMath.sol
// 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;
    }
}

File 25 of 33 : SafeMath64.sol
// 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;
    }
}

File 26 of 33 : ERC20.sol
// 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
    );
}

File 27 of 33 : Packed64x4.sol
// 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));
    }
}

File 28 of 33 : SigningKeys.sol
// 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)));
    }
}

File 29 of 33 : Versioned.sol
// 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);
    }
}

File 30 of 33 : IBurner.sol
// 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);
}

File 31 of 33 : ILidoLocator.sol
// 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
    );
}

File 32 of 33 : Math256.sol
// 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;
    }
}

File 33 of 33 : MinFirstAllocationStrategy.sol
// 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;
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "constantinople",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"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"}]

6080604052620000146200005460201b60201c565b6200004e7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a660001962000156602090811b62003d6d17901c565b6200026b565b620000646200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a454400000000000000006020820152901562000140576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101562000104578181015183820152602001620000ea565b50505050905090810190601f168015620001325780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50620001546000196200018d60201b60201c565b565b9055565b60006200018860008051602062005de083398151915260001b600019166200026760201b62002f7c1760201c565b905090565b6200019d6200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156200023c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360008381101562000104578181015183820152602001620000ea565b506200026460008051602062005de08339815191528262000156602090811b62003d6d17901c565b50565b5490565b615b65806200027b6000396000f3006080604052600436106102d95760003560e01c63ffffffff1680630803fac0146102de578063096b7b351461030757806315dae03e1461033d5780632914b9bd1461036457806330a90f01146103d957806332f0a3b5146103f15780634febc81b1461040657806359e25c12146104715780635ddde810146105ae5780635e2fb908146105cc5780635e57d742146105e457806362dcfda11461060857806365cc369a146106e5578063684560a2146107005780636ccc7562146107275780636da7d0a71461073f5780636ef355f1146107545780637038b141146105ae57806375049ad81461076f57806375a080d5146107875780637e7db6e11461079f57806380231f15146107c0578063805911ae1461030757806380afdea8146107d55780638469cbd3146107ea57806385fa63d7146107ff5780638aa104351461082d5780638b3dd749146108425780638ca7c052146108575780638d7e40171461086f5780638ece99951461088757806390c09bdb1461089c57806391dcd6b2146108b1578063973e9328146108c95780639a56983c146108ed5780639a7c2ade146109c95780639abddf09146109f05780639b00c14614610a235780639b3d190014610a4f5780639d4941d814610a7b578063a1658fad14610a9c578063a2e080f114610b03578063a479e50814610b1e578063a70c70e414610b33578063a9e7a84614610b48578063ae962acf14610b68578063b3076c3c14610b8d578063b449402a14610be8578063b497183314610cec578063bee41b5814610d01578063d07442f114610e03578063d087d28814610e03578063d4aae0c414610e18578063d8343dcb14610e2d578063d8e71cd114610e42578063db9887ea14610e57578063de4796ed14610e6f578063e204d09b14610e84578063e864299e14610e99578063ec5af3a414610eae578063ed5cfa4114610754578063f2e2ca6314610ec3578063f31bd9c114610ee1578063fbc77ef114610ef6575b600080fd5b3480156102ea57600080fd5b506102f3610f0e565b604080519115158252519081900360200190f35b34801561031357600080fd5b5061033b60048035906024803591604435808301929082013591606435918201910135610f38565b005b34801561034957600080fd5b50610352610fa9565b60408051918252519081900360200190f35b34801561037057600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526103bd943694929360249392840191908190840183828082843750949750610fda9650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156103e557600080fd5b506102f36004356110bd565b3480156103fd57600080fd5b506103bd611181565b34801561041257600080fd5b506104216004356024356111f6565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561045d578181015183820152602001610445565b505050509050019250505060405180910390f35b34801561047d57600080fd5b5061048f60043560243560443561128d565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156104d45781810151838201526020016104bc565b50505050905090810190601f1680156105015780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b8381101561053457818101518382015260200161051c565b50505050905090810190601f1680156105615780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561059657818101518382015260200161057e565b50505050905001965050505050505060405180910390f35b3480156105ba57600080fd5b5061033b60043560243560443561138b565b3480156105d857600080fd5b506102f360043561139b565b3480156105f057600080fd5b5061033b6004803590602480359081019101356113b0565b34801561061457600080fd5b50610620600435611528565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610668578181015183820152602001610650565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156106a757818101518382015260200161068f565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561059657818101518382015260200161057e565b3480156106f157600080fd5b5061033b600435602435611759565b34801561070c57600080fd5b5061033b600160a060020a036004351660243560443561177e565b34801561073357600080fd5b5061033b60043561185b565b34801561074b57600080fd5b5061035261187e565b34801561076057600080fd5b5061033b6004356024356118a9565b34801561077b57600080fd5b506102f36004356118b5565b34801561079357600080fd5b5061033b6004356118da565b3480156107ab57600080fd5b506102f3600160a060020a0360043516611a2a565b3480156107cc57600080fd5b50610352611a30565b3480156107e157600080fd5b50610352611a42565b3480156107f657600080fd5b50610352611a6d565b34801561080b57600080fd5b506103526024600480358281019291013590600160a060020a03903516611a86565b34801561083957600080fd5b50610352611c77565b34801561084e57600080fd5b50610352611ca2565b34801561086357600080fd5b50610352600435611ccd565b34801561087b57600080fd5b5061033b600435611d1c565b34801561089357600080fd5b50610352611d33565b3480156108a857600080fd5b5061033b611d45565b3480156108bd57600080fd5b5061033b600435611d7f565b3480156108d557600080fd5b5061033b600435600160a060020a0360243516611e32565b3480156108f957600080fd5b5061090a6004356024351515611efe565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610988578181015183820152602001610970565b50505050905090810190601f1680156109b55780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b3480156109d557600080fd5b5061033b600160a060020a0360043516602435604435612051565b3480156109fc57600080fd5b50610a05612278565b60408051938452602084019290925282820152519081900360600190f35b348015610a2f57600080fd5b5061033b60246004803582810192908201359181359182019101356122d2565b348015610a5b57600080fd5b5061033b602460048035828101929082013591813591820191013561236b565b348015610a8757600080fd5b5061033b600160a060020a03600435166123ed565b348015610aa857600080fd5b5060408051602060046044358181013583810280860185019096528085526102f3958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061267c9650505050505050565b348015610b0f57600080fd5b5061033b6004356024356127c9565b348015610b2a57600080fd5b506103bd6127f3565b348015610b3f57600080fd5b506103526128a8565b348015610b5457600080fd5b5061033b60043560243515156044356128d3565b348015610b7457600080fd5b5061033b60043567ffffffffffffffff602435166129b7565b348015610b9957600080fd5b50610ba5600435612adc565b6040805198151589526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610bf457600080fd5b50610c03600435602435612b9a565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610c4e578181015183820152602001610c36565b50505050905090810190601f168015610c7b5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610cae578181015183820152602001610c96565b50505050905090810190601f168015610cdb5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610cf857600080fd5b50610352612bd8565b348015610d0d57600080fd5b50610d25600480359060248035908101910135612bdd565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610d66578181015183820152602001610d4e565b50505050905090810190601f168015610d935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610dc6578181015183820152602001610dae565b50505050905090810190601f168015610df35780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610e0f57600080fd5b50610352612cab565b348015610e2457600080fd5b506103bd612cd6565b348015610e3957600080fd5b506103bd612d01565b348015610e4e57600080fd5b50610352612d2c565b348015610e6357600080fd5b50610352600435612d50565b348015610e7b57600080fd5b506102f3612d7f565b348015610e9057600080fd5b50610352612d92565b348015610ea557600080fd5b5061033b612d9a565b348015610eba57600080fd5b50610352612db9565b348015610ecf57600080fd5b5061033b600435602435604435612dbe565b348015610eed57600080fd5b50610352612dfc565b348015610f0257600080fd5b506102f3600435612e20565b600080610f19611ca2565b90508015801590610f31575080610f2e612e5f565b10155b91505b5090565b610fa1868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750612e63945050505050565b505050505050565b6000610fd47fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0612f7c565b90505b90565b6000610fe46127f3565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561103f578181015183820152602001611027565b50505050905090810190601f16801561106c5780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b15801561108b57600080fd5b505af115801561109f573d6000803e3d6000fd5b505050506040513d60208110156110b557600080fd5b505192915050565b60006110c76159f9565b6110d083612f80565b90506110db81612fac565b1580156110f757506110f481600263ffffffff612ff116565b15155b151561114d576040805160e560020a62461bcd02815260206004820152601260248201527f43414e545f434c4541525f50454e414c54590000000000000000000000000000604482015290519081900360640190fd5b611160816002600063ffffffff61300916565b61116a8382613090565b611173836130a9565b61117b613120565b50919050565b600061118b612cd6565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111c557600080fd5b505af11580156111d9573d6000803e3d6000fd5b505050506040513d60208110156111ef57600080fd5b5051905090565b60606000806112036128a8565b91508185101580611212575083155b1561121c57611285565b611228848684036131e9565b604051908082528060200260200182016040528015611251578160200160208202803883390190505b509250600090505b825181101561128557808501838281518110151561127357fe5b60209081029091010152600101611259565b505092915050565b606080606061129a6159f9565b6000806112a6896131ff565b6112af89613211565b92506112dc6112c584600263ffffffff612ff116565b6112d58a8a63ffffffff61323d16565b11156132d7565b6112ed83600363ffffffff612ff116565b91506112f88761332e565b604080518a81526020808c028201019091529197509550878015611326578160200160208202803883390190505b50935061134d600080516020615b1a8339815191528a8a8a8a8a600063ffffffff6133b916565b8681101561137f578181890110848281518110151561136857fe5b91151560209283029091019091015260010161134d565b50505093509350939050565b611396838383613430565b505050565b60009081526020819052604090205460ff1690565b6113e982828080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b6113f2836131ff565b611409600080516020615a9a83398151915261360d565b6114b5828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561149d5780601f1061147b57610100808354040283529182019161149d565b820191906000526020600020905b815481529060010190602001808311611489575b5050915050604051809103902060001916141561364b565b60008381526020819052604090206114d1906001018383615a0b565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b606080606060008060008061153b6159f9565b6000806000806115496128a8565b9850611553611a6d565b97508760405190808252806020026020018201604052801561157f578160200160208202803883390190505b509b50876040519080825280602002602001820160405280156115ac578160200160208202803883390190505b509a50876040519080825280602002602001820160405280156115d9578160200160208202803883390190505b50995060009650600095505b888410156116da576115f68461139b565b1515611601576116cf565b61160a84613211565b945061161d85600163ffffffff612ff116565b925061163085600363ffffffff612ff116565b91508282101561163c57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d908990811061167157fe5b600160a060020a039092166020928302909101909101528a5181908c908990811061169857fe5b602090810290910101526116ab846118b5565b8a888151811015156116b957fe5b9115156020928302909101909101526001909601955b8360010193506115e5565b8515156116e657611749565b600096505b87871015611749578561171c8e8d8a81518110151561170657fe5b602090810290910101519063ffffffff6136a216565b81151561172557fe5b048b8881518110151561173457fe5b602090810290910101526001909601956116eb565b5050505050505050509193909250565b611770600080516020615a9a83398151915261360d565b61177a828261374d565b5050565b611786611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156118475760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561180c5781810151838201526020016117f4565b50505050905090810190601f1680156118395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506118538383836138f0565b611396613b40565b611872600080516020615a9a83398151915261360d565b61187b81613c08565b50565b6000610fd47f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e612f7c565b61177a82826001613430565b60006118bf6159f9565b6118c883612f80565b90506118d381612fac565b9392505050565b60006118e46159f9565b6000806118f0856131ff565b611907600080516020615a9a83398151915261360d565b6119186119138661139b565b613c82565b611920611a6d565b935061195161193685600163ffffffff613cd916565b600080516020615aba8339815191529063ffffffff613d6d16565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a26119a285613211565b92506119b583600063ffffffff612ff116565b91506119c883600363ffffffff612ff116565b905080821115611a1b576119e48360008363ffffffff61300916565b6119ee8584613d71565b6040805182815290518691600080516020615afa833981519152919081900360200190a2611a1b856130a9565b611a23613120565b5050505050565b50600190565b600080516020615ada83398151915281565b6000610fd47fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b612f7c565b6000610fd4600080516020615aba833981519152612f7c565b6000806000611ac486868080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b611acd84613d8a565b611ae4600080516020615a9a83398151915261360d565b611aec6128a8565b925060c88310611b46576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611b797fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613d6d16565b60008381526020819052604090209150611b91611a6d565b9050611bb4600080516020615aba8339815191526001830163ffffffff613d6d16565b815460ff191660019081178355611bce9083018787615a0b565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b6000610fd47f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6612f7c565b6000610fd47febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e612f7c565b6000611cd76159f9565b611ce0836131ff565b611ce983613211565b90506118d3611cff82600363ffffffff612ff116565b611d1083600263ffffffff612ff116565b9063ffffffff613cd916565b61187b600080516020615ada83398151915261360d565b600080516020615a9a83398151915281565b6000611d5e600080516020615ada83398151915261360d565b611d666128a8565b9050600081111561187b5761187b60006001830361374d565b611d88816131ff565b611d9f600080516020615a9a83398151915261360d565b611db1611dab8261139b565b15613c82565b611dda611dbc611a6d565b600080516020615aba8339815191529060010163ffffffff613d6d16565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261187b613120565b611e3b81613d8a565b611e44826131ff565b611e5b600080516020615a9a83398151915261360d565b600082815260208190526040902054611e8790600160a060020a0383811661010090920416141561364b565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b60006060600080600080600080611f136159f9565b611f1c8b6131ff565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089611f5d57604080516020810190915260008152611fea565b60018281018054604080516020600295841615610100026000190190931694909404601f810183900483028501830190915280845290830182828015611fe45780601f10611fb957610100808354040283529160200191611fe4565b820191906000526020600020905b815481529060010190602001808311611fc757829003601f168201915b50505050505b9750611ff58b613211565b905061200881600063ffffffff612ff116565b955061201b81600163ffffffff612ff116565b945061202e81600263ffffffff612ff116565b935061204181600363ffffffff612ff116565b9250505092959891949750929550565b600061205b6159f9565b6120636159f9565b61206b6159f9565b600080600080600061207b610f0e565b15156120d1576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6120db6000613e64565b6120e68c8c8c6138f0565b6120ee6128a8565b9850602060405190810160405280600081525095505b888210156122595761211582613211565b975061212888600063ffffffff612ff116565b945061213b88600263ffffffff612ff116565b935061214e88600363ffffffff612ff116565b60008381526020819052604090205490935060ff161515612170575081612186565b6121838461217e8588613ec2565b6131e9565b90505b8481146121ce5761219f8860008363ffffffff61300916565b6121a98289613d71565b6040805182815290518391600080516020615afa833981519152919081900360200190a25b6121d782613ed1565b96506121eb8760028363ffffffff61300916565b6121f58288613efd565b6122078660008363ffffffff613f1616565b6122198660038563ffffffff613f1616565b61223c600161222e8a8263ffffffff612ff116565b88919063ffffffff613f1616565b61224e8660028663ffffffff613f1616565b816001019150612104565b61226286613f3a565b61226a613120565b505050505050505050505050565b60008060006122856159f9565b61228d613f40565b90506122a081600163ffffffff612ff116565b93506122b381600363ffffffff612ff116565b92506122ca83611d1083600063ffffffff612ff116565b915050909192565b60008080808080806122f1600080516020615ada83398151915261360d565b6122fb8a89613f5c565b96506123056128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123458686106132d7565b61235185856000613fd6565b612317565b61235e613120565b5050505050505050505050565b600080808080808061238a600080516020615ada83398151915261360d565b6123948a89613f5c565b965061239e6128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123de8686106132d7565b6123e88585614191565b6123b0565b60008060006123fb84611a2a565b60408051808201909152601281527f5245434f5645525f444953414c4c4f574544000000000000000000000000000060208201529015156124815760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5061248a611181565b9250612495836142e1565b60408051808201909152601a81527f5245434f5645525f5641554c545f4e4f545f434f4e5452414354000000000000602082015290151561251b5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50600160a060020a038416151561256c5760405130319250600160a060020a0384169083156108fc029084906000818181858888f19350505050158015612566573d6000803e3d6000fd5b5061262b565b5082612587600160a060020a0382163063ffffffff61430716565b91506125a3600160a060020a038216848463ffffffff61441c16565b60408051808201909152601d81527f5245434f5645525f544f4b454e5f5452414e534645525f4641494c454400000060208201529015156126295760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505b83600160a060020a031683600160a060020a03167f596caf56044b55fb8c4ca640089bbc2b63cae3e978b851f5745cbb7c5b288e02846040518082815260200191505060405180910390a350505050565b600080612687610f0e565b151561269657600091506127c1565b61269e612cd6565b9050600160a060020a03811615156126b957600091506127c1565b80600160a060020a031663fdef91068630876126d4886144a7565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561274357818101518382015260200161272b565b50505050905090810190601f1680156127705780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561279257600080fd5b505af11580156127a6573d6000803e3d6000fd5b505050506040513d60208110156127bc57600080fd5b505191505b509392505050565b6127d2826131ff565b6127e9600080516020615ada83398151915261360d565b61177a82826144b1565b6000806127fe612cd6565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b15801561108b57600080fd5b6000610fd47fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8612f7c565b6128db6159f9565b6128e4846131ff565b6128fb600080516020615ada83398151915261360d565b61290f67ffffffffffffffff8311156132d7565b61291884613ed1565b905061293f60008461292b57600061292e565b60015b83919060ff1663ffffffff61300916565b612960600184612950576000612952565b835b83919063ffffffff61300916565b61296a8482613efd565b60408051838152905185917fd50ea115db6f0b433ef9cc4b71110dbd9202364a00488be90718990be5bf16a6919081900360200190a26129a9846130a9565b6129b1613120565b50505050565b6129bf6159f9565b6000806000806129ce876131ff565b612a0b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612a068967ffffffffffffffff8a166145d2565b61462d565b612a176119138861139b565b612a2087613211565b9450612a3385600063ffffffff612ff116565b9350612a4685600363ffffffff612ff116565b9250612a5985600263ffffffff612ff116565b9150612a738261217e8867ffffffffffffffff1686613ec2565b905083811415612a8257612ad3565b612a948560008363ffffffff61300916565b612a9e8786613d71565b6040805182815290518891600080516020615afa833981519152919081900360200190a2612acb876130a9565b612ad3613120565b50505050505050565b600080600080600080600080612af06159f9565b612af86159f9565b612b018b6131ff565b612b0a8b613ed1565b9150612b158b612f80565b9050612b2882600063ffffffff612ff116565b15159950612b3d82600163ffffffff612ff116565b9850612b5081600063ffffffff612ff116565b9750612b6381600163ffffffff612ff116565b9650612b7681600263ffffffff612ff116565b9550612b818b61463b565b8095508196508297505050505050919395975091939597565b60608060006060612bad8686600161128d565b8051929650909450915081906000908110612bc457fe5b906020019060200201519150509250925092565b60ff81565b60608060008180612bfb600080516020615ada83398151915261360d565b871515612c21576040805160008082526020820190815281830190925295509350612ca0565b612c2a8861465b565b91945092509050878314612c88576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612c938383836147ed565b9095509350612ca0613120565b505050935093915050565b6000610fd47fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b6000610fd47f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b612f7c565b6000610fd47ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537612f7c565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612d5a6159f9565b612d63836131ff565b612d6c83613211565b90506118d381600263ffffffff612ff116565b6000600019612d8c611ca2565b14905090565b6301e1338081565b612db1600080516020615ada83398151915261360d565b61187b6149c9565b60c881565b612dc7836131ff565b612dde600080516020615ada83398151915261360d565b612de88382614191565b612df483836001613fd6565b611396613120565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b6000612e2a6159f9565b612e3383612f80565b9050612e3e81612fac565b1580156118d35750612e5781600263ffffffff612ff116565b159392505050565b4390565b612e6b6159f9565b6000612e756159f9565b612e7e876131ff565b612e883388614e59565b612ea78615801590612ea2575067ffffffffffffffff8711155b6132d7565b612eb087613211565b9250612ec383600263ffffffff612ff116565b9150612ee167ffffffffffffffff6112d5848963ffffffff61323d16565b612f03600080516020615b1a833981519152888489898963ffffffff614ec616565b60408051828152905191935088917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a2612f4d8360028463ffffffff61300916565b612f578784613d71565b612f5f613f40565b9050612f738160028863ffffffff613f1616565b612acb81613f3a565b5490565b612f886159f9565b50600090815260208181526040918290208251918201909252600390910154815290565b6000612fbe828263ffffffff612ff116565b612fcf83600163ffffffff612ff116565b1080612feb5750612fe782600263ffffffff612ff116565b4211155b92915050565b905167ffffffffffffffff604090920260ff161c1690565b67ffffffffffffffff811115613069576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600390910155565b6000806130b46159f9565b60006130bf85615179565b93509350838314156130d057611a23565b6130d8613f40565b91506130e4838561526d565b905083831115613105576131008260008363ffffffff613f1616565b613117565b6131178260008363ffffffff61528416565b611a2382613f3a565b600061314b7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b60010190506131807fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613d6d16565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b60008183106131f857816118d3565b5090919050565b61187b61320a6128a8565b82106132d7565b6132196159f9565b50600090815260208181526040918290208251918201909252600290910154815290565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b508091505b5092915050565b80151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b60608061334283603063ffffffff6136a216565b6040519080825280601f01601f19166020018201604052801561336f578160200160208202803883390190505b5061338184606063ffffffff6136a216565b6040519080825280601f01601f1916602001820160405280156133ae578160200160208202803883390190505b509092509050915091565b6000805b85811015613425576133d8898989840163ffffffff61529716565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016133bd565b505050505050505050565b6134386159f9565b6000806134436159f9565b61344c876131ff565b6134563388614e59565b84151561346257612ad3565b61346b87613211565b935061347e84600263ffffffff612ff116565b92506134af61349485600363ffffffff612ff116565b8710158015612ea25750836112d5888863ffffffff61323d16565b6134d0600080516020615b1a8339815191528888888763ffffffff61532a16565b92506134e48460028563ffffffff61300916565b60408051848152905188917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261352b84600063ffffffff612ff116565b91508186101561356c576135478460008863ffffffff61300916565b6040805187815290518891600080516020615afa833981519152919081900360200190a25b6135768785613d71565b61357e613f40565b90506135928160028763ffffffff61528416565b61359b81613f3a565b612acb876130a9565b600081511180156135b7575060ff815111155b151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b61187b61364633836000604051908082528060200260200182016040528015613640578160200160208202803883390190505b5061267c565b615533565b80151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156136b557600091506132d0565b508282028284828115156136c557fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b60008060008061375b6159f9565b60006137656159f9565b613782888a11158015612ea2575061377b6128a8565b89106132d7565b8891505b8782116138ba5761379682613211565b92506137a983600263ffffffff612ff116565b94506137bc83600363ffffffff612ff116565b9350838514156137cb576138af565b8385116137d457fe5b8385039650948601946137ef8360028663ffffffff61300916565b6138018360008663ffffffff61300916565b61380b8284613d71565b613814826130a9565b60408051858152905183917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a26040805185815290518391600080516020615afa833981519152919081900360200190a26040805167ffffffffffffffff89168152905183917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b816001019150613786565b6000861115613425576138cb613f40565b90506138df8160028863ffffffff61528416565b6138e881613f3a565b613425613120565b6138f98361558a565b6139297ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378463ffffffff613d6d16565b6139597fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08363ffffffff613d6d16565b61396360026155ea565b61396c81613c08565b613974612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156139ae57600080fd5b505af11580156139c2573d6000803e3d6000fd5b505050506040513d60208110156139d857600080fd5b5051600160a060020a031663095ea7b36139f0612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613a2a57600080fd5b505af1158015613a3e573d6000803e3d6000fd5b505050506040513d6020811015613a5457600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260001960248301525160448083019260209291908290030181600087803b158015613aa057600080fd5b505af1158015613ab4573d6000803e3d6000fd5b505050506040513d6020811015613aca57600080fd5b505060408051600160a060020a038516815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805183815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a1505050565b613b48611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613bcd5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50613c06613bd9612e5f565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613d6d16565b565b613c186301e133808211156132d7565b613c487f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e8263ffffffff613d6d16565b6040805182815290517f4cccd9748bff0341d9852cc61d82652a3003dcebea088f05388c0be1f26b4c8a9181900360200190a150565b5490565b80151561187b576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613d655760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613d938161558a565b613d9b612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613dd557600080fd5b505af1158015613de9573d6000803e3d6000fd5b505050506040513d6020811015613dff57600080fd5b5051600160a060020a038281169116141561187b576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613e6c611c77565b811461187b576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008183116131f857816118d3565b613ed96159f9565b50600090815260208181526040918290208251918201909252600490910154815290565b6000918252602082905260409091209051600490910155565b6113968383613f3584613f298888612ff1565b9063ffffffff61323d16565b613009565b51600155565b613f486159f9565b506040805160208101909152600154815290565b600882046010820481148015613f73575060088306155b8015613f80575060108206155b1515612feb576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b613fde6159f9565b6000806000613feb6159f9565b6000613ff689613211565b955061400986600163ffffffff612ff116565b94508488141561401857613425565b868061402357508488115b151561409f576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6140b086600363ffffffff612ff116565b93506140cc60006140c08b612f80565b9063ffffffff612ff116565b9250828410156140d857fe5b6140e68385038911156132d7565b6140f88660018a63ffffffff61300916565b6141028987613d71565b6040805189815290518a917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a2614140613f40565b915061414c888661526d565b90508488111561416d576141688260018363ffffffff613f1616565b61417f565b61417f8260018363ffffffff61528416565b61418882613f3a565b613425896130a9565b6141996159f9565b60006141a36159f9565b60008060006141b188612f80565b95506141c486600063ffffffff612ff116565b9450848714156141d3576142d7565b6141dc88613211565b93506141ef84600163ffffffff612ff116565b925061420284600363ffffffff612ff116565b91508282101561420e57fe5b61421c8383038811156132d7565b61422d86600163ffffffff612ff116565b905080871115801561423e57508085115b1561426057614260600261425061187e565b889190420163ffffffff61300916565b6142728660008963ffffffff61300916565b61427c8887613090565b877f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb88836142b18a600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a26142d7886130a9565b5050505050505050565b600080600160a060020a03831615156142fd576000915061117b565b50506000903b1190565b60408051600160a060020a0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905260009081806143878684615650565b60408051808201909152601c81527f534146455f4552435f32305f42414c414e43455f524556455254454400000000602082015291935091508215156144125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5095945050505050565b60408051600160a060020a038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260009061449e8582615681565b95945050505050565b8051602002815290565b6144b96159f9565b60006144c36159f9565b60006144ce86612f80565b93506144e184600163ffffffff612ff116565b9250828514156144f057610fa1565b6144f986613211565b915061451761450f83600363ffffffff612ff116565b8611156132d7565b61452884600063ffffffff612ff116565b905080851015801561453957508083105b1561455b5761455b600261454b61187e565b869190420163ffffffff61300916565b61456d8460018763ffffffff61300916565b6145778685613090565b857f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb82876145ac88600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a2610fa1866130a9565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561460257fe5b60209081029091010152805182908290600190811061461d57fe5b6020908102909101015292915050565b61177a61364633848461267c565b60008060008061464a856156cf565b919790965090869003945092505050565b600060608060006060600080600080600080614675611a6d565b9750876040519080825280602002602001820160405280156146a1578160200160208202803883390190505b509950876040519080825280602002602001820160405280156146ce578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146fb578160200160208202803883390190505b5096506147066128a8565b94505b8481101561478d5761471a816156cf565b9550935091508284141561472d57614785565b808a8781518110151561473c57fe5b602090810290910101528851828403908a908890811061475857fe5b6020908102909101015286518285039088908890811061477457fe5b602090810290910101526001909501945b600101614709565b8515156147b557604080516000808252602082018181528284019093529c509a5098506147de565b878610156147c757858a528589528587525b6147d289888e615754565b9a508a8c10156147de57fe5b50505050505050509193909250565b6060806000806000806147fe6159f9565b60006148086159f9565b6148118c61332e565b9099509750600096505b8a5182101561498c576148448b8381518110151561483557fe5b90602001906020020151613211565b925061485783600363ffffffff612ff116565b9550898281518110151561486757fe5b6020908102909101015161488284600163ffffffff612ff116565b0194508585141561489257614981565b85851161489b57fe5b85850393506148db8b838151811015156148b157fe5b60209081029091010151600080516020615b1a8339815191529088878d8d8d63ffffffff6133b916565b8a51968401968b90839081106148ed57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a26149408360038763ffffffff61300916565b6149618b8381518110151561495157fe5b9060200190602002015184613d71565b6149818b8381518110151561497257fe5b906020019060200201516130a9565b81600101915061481b565b868c1461499557fe5b61499d613f40565b90506149b18160038963ffffffff613f1616565b6149ba81613f3a565b50505050505050935093915050565b600080600060608060606000806149de612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614a1857600080fd5b505af1158015614a2c573d6000803e3d6000fd5b505050506040513d6020811015614a4257600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919850600160a060020a0389169163f5eb42dc916024808201926020929091908290030181600087803b158015614aa957600080fd5b505af1158015614abd573d6000803e3d6000fd5b505050506040513d6020811015614ad357600080fd5b50519550851515614ae357614e4f565b614aec86611528565b9450945094505b8451811015614d565760028482815181101515614b0c57fe5b906020019060200201511015614b2157614d4e565b8281815181101515614b2f57fe5b9060200190602002015115614bf95760018482815181101515614b4e57fe5b602090810290910101805190911c90528351614b8790859083908110614b7057fe5b60209081029091010151839063ffffffff61323d16565b91508481815181101515614b9757fe5b90602001906020020151600160a060020a03167fe915a473fc2ef8e0231da98380f853b2aeea117a4392c67e753c54186bfbbd128583815181101515614bd957fe5b906020019060200201516040518082815260200191505060405180910390a25b86600160a060020a0316638fcb4e5b8683815181101515614c1657fe5b906020019060200201518684815181101515614c2e57fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015614c8757600080fd5b505af1158015614c9b573d6000803e3d6000fd5b505050506040513d6020811015614cb157600080fd5b50508351614cdc90859083908110614cc557fe5b60209081029091010151899063ffffffff61323d16565b97508481815181101515614cec57fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece0868583815181101515614d2e57fe5b906020019060200201516040518082815260200191505060405180910390a25b600101614af3565b6000821115614e4f57614d67612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614da157600080fd5b505af1158015614db5573d6000803e3d6000fd5b505050506040513d6020811015614dcb57600080fd5b5051604080517f46114928000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a039092169163461149289160448082019260009290919082900301818387803b158015614e3657600080fd5b505af1158015614e4a573d6000803e3d6000fd5b505050505b5050505050505090565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff166129b1828015614e8d5750815b806136465750613646857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ec187615784565b61267c565b6000806000606060008088118015614ef5575067ffffffffffffffff614ef28a8a63ffffffff61323d16565b11155b1515614f4b576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614f5c88603063ffffffff6136a216565b8751148015614f7b5750614f7788606063ffffffff6136a216565b8651145b1515614fd1576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b8781101561516a5761500d8b8b8b63ffffffff61529716565b60308281028901602081810151918301519286018390528501819052919550171592508215615086576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561512b578181015183820152602001615113565b50505050905090810190601f1680156151585780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614ff4565b50969998505050505050505050565b6000806151846159f9565b61518c6159f9565b600061519786613211565b92506151a286613ed1565b91506151b583600363ffffffff612ff116565b90506151c883600063ffffffff612ff116565b93506151d386612e20565b15156151e15780935061522f565b6151f282600063ffffffff612ff116565b1561522f5761522c816152278661521086600163ffffffff612ff116565b61522188600163ffffffff612ff116565b016131e9565b613ec2565b93505b61524082600263ffffffff612ff116565b94508385146152655761525b8260028663ffffffff61300916565b6152658683613efd565b505050915091565b600081831161527e578282036118d3565b50900390565b6113968383613f3584611d108888612ff1565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b602083106152f35780518252601f1990920191602091820191016152d4565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561535257508661534f8a8a63ffffffff61323d16565b11155b8015615366575067ffffffffffffffff8711155b15156153bc576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b88811115615524576154008b8b600019840163ffffffff61529716565b9450600185015460801c6030830152845460208301528681101561545a576154338b8b6000198a0163ffffffff61529716565b9350600092505b600583101561545657828401548386015560018301925061543a565b8394505b600092505b60058310156154795760008386015560018301925061545f565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156154e55781810151838201526020016154cd565b50505050905090810190601f1680156155125780820380516001836020036101000a031916815260200191505b509250505060405180910390a26153e3565b50949998505050505050505050565b80151561187b576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038116151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b61561a7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613d6d16565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000806000806040516020818751602089018a5afa9250600083111561567557805191505b50909590945092505050565b6000806040516020818551602087016000895af160008111156156c5573d80156156b257602081146156bb576156c3565b600193506156c3565b600183511493505b505b5090949350505050565b60008060006156dc6159f9565b6156e46159f9565b6156ed86613211565b91506156f886613ed1565b905061570b82600163ffffffff612ff116565b945061571e82600363ffffffff612ff116565b935061573181600263ffffffff612ff116565b92508383101580156157435750848410155b151561574b57fe5b50509193909250565b6000805b828210156127c15761576d85858486036157c5565b905080151561577b576127c1565b90810190615758565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156157b657fe5b60209081029091010152919050565b8251600090600019828080808715156157e157600096506159c4565b600092505b895183101561589f5788838151811015156157fd57fe5b906020019060200201518a8481518110151561581557fe5b602090810290910101511061582957615894565b898381518110151561583757fe5b9060200190602002015185111561586e5782955060019350898381518110151561585d57fe5b906020019060200201519450615894565b898381518110151561587c57fe5b90602001906020020151851415615894576001840193505b8260010192506157e6565b8315156158af57600096506159c4565b50600019905060005b895181101561595f5788818151811015156158cf57fe5b906020019060200201518a828151811015156158e757fe5b60209081029091010151106158fb57615957565b848a8281518110151561590a57fe5b906020019060200201511180156159375750818a8281518110151561592b57fe5b90602001906020020151105b1561595757898181518110151561594a57fe5b9060200190602002015191505b6001016158b8565b6159a260018511615970578861597a565b61597a89866159d1565b8661599c858d8b81518110151561598d57fe5b906020019060200201516131e9565b036131e9565b9650868a878151811015156159b357fe5b602090810290910101805190910190525b5050505050509392505050565b600082156159f05781600184038115156159e757fe5b046001016118d3565b50600092915050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615a4c5782800160ff19823516178555615a79565b82800160010185558215615a79579182015b82811115615a79578235825591602001919060010190615a5e565b50610f3492610fd79250905b80821115610f345760008155600101615a85560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a72305820c0673b4dec33ab43c022a0ca404b407602f0e9ac1e02e04efbe56d5c3734569c0029ebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e

Deployed Bytecode

0x6080604052600436106102d95760003560e01c63ffffffff1680630803fac0146102de578063096b7b351461030757806315dae03e1461033d5780632914b9bd1461036457806330a90f01146103d957806332f0a3b5146103f15780634febc81b1461040657806359e25c12146104715780635ddde810146105ae5780635e2fb908146105cc5780635e57d742146105e457806362dcfda11461060857806365cc369a146106e5578063684560a2146107005780636ccc7562146107275780636da7d0a71461073f5780636ef355f1146107545780637038b141146105ae57806375049ad81461076f57806375a080d5146107875780637e7db6e11461079f57806380231f15146107c0578063805911ae1461030757806380afdea8146107d55780638469cbd3146107ea57806385fa63d7146107ff5780638aa104351461082d5780638b3dd749146108425780638ca7c052146108575780638d7e40171461086f5780638ece99951461088757806390c09bdb1461089c57806391dcd6b2146108b1578063973e9328146108c95780639a56983c146108ed5780639a7c2ade146109c95780639abddf09146109f05780639b00c14614610a235780639b3d190014610a4f5780639d4941d814610a7b578063a1658fad14610a9c578063a2e080f114610b03578063a479e50814610b1e578063a70c70e414610b33578063a9e7a84614610b48578063ae962acf14610b68578063b3076c3c14610b8d578063b449402a14610be8578063b497183314610cec578063bee41b5814610d01578063d07442f114610e03578063d087d28814610e03578063d4aae0c414610e18578063d8343dcb14610e2d578063d8e71cd114610e42578063db9887ea14610e57578063de4796ed14610e6f578063e204d09b14610e84578063e864299e14610e99578063ec5af3a414610eae578063ed5cfa4114610754578063f2e2ca6314610ec3578063f31bd9c114610ee1578063fbc77ef114610ef6575b600080fd5b3480156102ea57600080fd5b506102f3610f0e565b604080519115158252519081900360200190f35b34801561031357600080fd5b5061033b60048035906024803591604435808301929082013591606435918201910135610f38565b005b34801561034957600080fd5b50610352610fa9565b60408051918252519081900360200190f35b34801561037057600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526103bd943694929360249392840191908190840183828082843750949750610fda9650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156103e557600080fd5b506102f36004356110bd565b3480156103fd57600080fd5b506103bd611181565b34801561041257600080fd5b506104216004356024356111f6565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561045d578181015183820152602001610445565b505050509050019250505060405180910390f35b34801561047d57600080fd5b5061048f60043560243560443561128d565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156104d45781810151838201526020016104bc565b50505050905090810190601f1680156105015780820380516001836020036101000a031916815260200191505b50848103835286518152865160209182019188019080838360005b8381101561053457818101518382015260200161051c565b50505050905090810190601f1680156105615780820380516001836020036101000a031916815260200191505b508481038252855181528551602091820191808801910280838360005b8381101561059657818101518382015260200161057e565b50505050905001965050505050505060405180910390f35b3480156105ba57600080fd5b5061033b60043560243560443561138b565b3480156105d857600080fd5b506102f360043561139b565b3480156105f057600080fd5b5061033b6004803590602480359081019101356113b0565b34801561061457600080fd5b50610620600435611528565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015610668578181015183820152602001610650565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156106a757818101518382015260200161068f565b50505050905001848103825285818151815260200191508051906020019060200280838360008381101561059657818101518382015260200161057e565b3480156106f157600080fd5b5061033b600435602435611759565b34801561070c57600080fd5b5061033b600160a060020a036004351660243560443561177e565b34801561073357600080fd5b5061033b60043561185b565b34801561074b57600080fd5b5061035261187e565b34801561076057600080fd5b5061033b6004356024356118a9565b34801561077b57600080fd5b506102f36004356118b5565b34801561079357600080fd5b5061033b6004356118da565b3480156107ab57600080fd5b506102f3600160a060020a0360043516611a2a565b3480156107cc57600080fd5b50610352611a30565b3480156107e157600080fd5b50610352611a42565b3480156107f657600080fd5b50610352611a6d565b34801561080b57600080fd5b506103526024600480358281019291013590600160a060020a03903516611a86565b34801561083957600080fd5b50610352611c77565b34801561084e57600080fd5b50610352611ca2565b34801561086357600080fd5b50610352600435611ccd565b34801561087b57600080fd5b5061033b600435611d1c565b34801561089357600080fd5b50610352611d33565b3480156108a857600080fd5b5061033b611d45565b3480156108bd57600080fd5b5061033b600435611d7f565b3480156108d557600080fd5b5061033b600435600160a060020a0360243516611e32565b3480156108f957600080fd5b5061090a6004356024351515611efe565b604080518815158152600160a060020a0387169181019190915267ffffffffffffffff8086166060830152848116608083015283811660a0830152821660c082015260e0602080830182815289519284019290925288516101008401918a019080838360005b83811015610988578181015183820152602001610970565b50505050905090810190601f1680156109b55780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b3480156109d557600080fd5b5061033b600160a060020a0360043516602435604435612051565b3480156109fc57600080fd5b50610a05612278565b60408051938452602084019290925282820152519081900360600190f35b348015610a2f57600080fd5b5061033b60246004803582810192908201359181359182019101356122d2565b348015610a5b57600080fd5b5061033b602460048035828101929082013591813591820191013561236b565b348015610a8757600080fd5b5061033b600160a060020a03600435166123ed565b348015610aa857600080fd5b5060408051602060046044358181013583810280860185019096528085526102f3958335600160a060020a031695602480359636969560649593949201929182918501908490808284375094975061267c9650505050505050565b348015610b0f57600080fd5b5061033b6004356024356127c9565b348015610b2a57600080fd5b506103bd6127f3565b348015610b3f57600080fd5b506103526128a8565b348015610b5457600080fd5b5061033b60043560243515156044356128d3565b348015610b7457600080fd5b5061033b60043567ffffffffffffffff602435166129b7565b348015610b9957600080fd5b50610ba5600435612adc565b6040805198151589526020890197909752878701959095526060870193909352608086019190915260a085015260c084015260e083015251908190036101000190f35b348015610bf457600080fd5b50610c03600435602435612b9a565b60405180806020018060200184151515158152602001838103835286818151815260200191508051906020019080838360005b83811015610c4e578181015183820152602001610c36565b50505050905090810190601f168015610c7b5780820380516001836020036101000a031916815260200191505b50838103825285518152855160209182019187019080838360005b83811015610cae578181015183820152602001610c96565b50505050905090810190601f168015610cdb5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b348015610cf857600080fd5b50610352612bd8565b348015610d0d57600080fd5b50610d25600480359060248035908101910135612bdd565b604051808060200180602001838103835285818151815260200191508051906020019080838360005b83811015610d66578181015183820152602001610d4e565b50505050905090810190601f168015610d935780820380516001836020036101000a031916815260200191505b50838103825284518152845160209182019186019080838360005b83811015610dc6578181015183820152602001610dae565b50505050905090810190601f168015610df35780820380516001836020036101000a031916815260200191505b5094505050505060405180910390f35b348015610e0f57600080fd5b50610352612cab565b348015610e2457600080fd5b506103bd612cd6565b348015610e3957600080fd5b506103bd612d01565b348015610e4e57600080fd5b50610352612d2c565b348015610e6357600080fd5b50610352600435612d50565b348015610e7b57600080fd5b506102f3612d7f565b348015610e9057600080fd5b50610352612d92565b348015610ea557600080fd5b5061033b612d9a565b348015610eba57600080fd5b50610352612db9565b348015610ecf57600080fd5b5061033b600435602435604435612dbe565b348015610eed57600080fd5b50610352612dfc565b348015610f0257600080fd5b506102f3600435612e20565b600080610f19611ca2565b90508015801590610f31575080610f2e612e5f565b10155b91505b5090565b610fa1868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375050604080516020601f8c018190048102820181019092528a815294508a9350899250829150840183828082843750612e63945050505050565b505050505050565b6000610fd47fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0612f7c565b90505b90565b6000610fe46127f3565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561103f578181015183820152602001611027565b50505050905090810190601f16801561106c5780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b15801561108b57600080fd5b505af115801561109f573d6000803e3d6000fd5b505050506040513d60208110156110b557600080fd5b505192915050565b60006110c76159f9565b6110d083612f80565b90506110db81612fac565b1580156110f757506110f481600263ffffffff612ff116565b15155b151561114d576040805160e560020a62461bcd02815260206004820152601260248201527f43414e545f434c4541525f50454e414c54590000000000000000000000000000604482015290519081900360640190fd5b611160816002600063ffffffff61300916565b61116a8382613090565b611173836130a9565b61117b613120565b50919050565b600061118b612cd6565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156111c557600080fd5b505af11580156111d9573d6000803e3d6000fd5b505050506040513d60208110156111ef57600080fd5b5051905090565b60606000806112036128a8565b91508185101580611212575083155b1561121c57611285565b611228848684036131e9565b604051908082528060200260200182016040528015611251578160200160208202803883390190505b509250600090505b825181101561128557808501838281518110151561127357fe5b60209081029091010152600101611259565b505092915050565b606080606061129a6159f9565b6000806112a6896131ff565b6112af89613211565b92506112dc6112c584600263ffffffff612ff116565b6112d58a8a63ffffffff61323d16565b11156132d7565b6112ed83600363ffffffff612ff116565b91506112f88761332e565b604080518a81526020808c028201019091529197509550878015611326578160200160208202803883390190505b50935061134d600080516020615b1a8339815191528a8a8a8a8a600063ffffffff6133b916565b8681101561137f578181890110848281518110151561136857fe5b91151560209283029091019091015260010161134d565b50505093509350939050565b611396838383613430565b505050565b60009081526020819052604090205460ff1690565b6113e982828080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b6113f2836131ff565b611409600080516020615a9a83398151915261360d565b6114b5828260405180838380828437820191505092505050604051809103902060001916600080868152602001908152602001600020600101604051808280546001816001161561010002031660029004801561149d5780601f1061147b57610100808354040283529182019161149d565b820191906000526020600020905b815481529060010190602001808311611489575b5050915050604051809103902060001916141561364b565b60008381526020819052604090206114d1906001018383615a0b565b50827fcb16868f4831cc58a28d413f658752a2958bd1f50e94ed6391716b936c48093b83836040518080602001828103825284848281815260200192508082843760405192018290039550909350505050a2505050565b606080606060008060008061153b6159f9565b6000806000806115496128a8565b9850611553611a6d565b97508760405190808252806020026020018201604052801561157f578160200160208202803883390190505b509b50876040519080825280602002602001820160405280156115ac578160200160208202803883390190505b509a50876040519080825280602002602001820160405280156115d9578160200160208202803883390190505b50995060009650600095505b888410156116da576115f68461139b565b1515611601576116cf565b61160a84613211565b945061161d85600163ffffffff612ff116565b925061163085600363ffffffff612ff116565b91508282101561163c57fe5b506000838152602081905260409020548b5183830396870196916101009004600160a060020a0316908d908990811061167157fe5b600160a060020a039092166020928302909101909101528a5181908c908990811061169857fe5b602090810290910101526116ab846118b5565b8a888151811015156116b957fe5b9115156020928302909101909101526001909601955b8360010193506115e5565b8515156116e657611749565b600096505b87871015611749578561171c8e8d8a81518110151561170657fe5b602090810290910101519063ffffffff6136a216565b81151561172557fe5b048b8881518110151561173457fe5b602090810290910101526001909601956116eb565b5050505050505050509193909250565b611770600080516020615a9a83398151915261360d565b61177a828261374d565b5050565b611786611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156118475760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561180c5781810151838201526020016117f4565b50505050905090810190601f1680156118395780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506118538383836138f0565b611396613b40565b611872600080516020615a9a83398151915261360d565b61187b81613c08565b50565b6000610fd47f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e612f7c565b61177a82826001613430565b60006118bf6159f9565b6118c883612f80565b90506118d381612fac565b9392505050565b60006118e46159f9565b6000806118f0856131ff565b611907600080516020615a9a83398151915261360d565b6119186119138661139b565b613c82565b611920611a6d565b935061195161193685600163ffffffff613cd916565b600080516020615aba8339815191529063ffffffff613d6d16565b600085815260208181526040808320805460ff1916905580519283525187927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a26119a285613211565b92506119b583600063ffffffff612ff116565b91506119c883600363ffffffff612ff116565b905080821115611a1b576119e48360008363ffffffff61300916565b6119ee8584613d71565b6040805182815290518691600080516020615afa833981519152919081900360200190a2611a1b856130a9565b611a23613120565b5050505050565b50600190565b600080516020615ada83398151915281565b6000610fd47fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b612f7c565b6000610fd4600080516020615aba833981519152612f7c565b6000806000611ac486868080601f016020809104026020016040519081016040528093929190818152602001838380828437506135a4945050505050565b611acd84613d8a565b611ae4600080516020615a9a83398151915261360d565b611aec6128a8565b925060c88310611b46576040805160e560020a62461bcd02815260206004820152601c60248201527f4d41585f4f50455241544f52535f434f554e545f455843454544454400000000604482015290519081900360640190fd5b611b797fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e86001850163ffffffff613d6d16565b60008381526020819052604090209150611b91611a6d565b9050611bb4600080516020615aba8339815191526001830163ffffffff613d6d16565b815460ff191660019081178355611bce9083018787615a0b565b50815474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a03861690810291909117835560408051858152908101919091526000606082018190526080602083018181529083018890527fc52ec0ad7872dae440d886040390c13677df7bf3cca136d8d81e5e5e7dd62ff19286928a928a928a929160a0820186868082843760405192018290039850909650505050505050a150509392505050565b6000610fd47f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a6612f7c565b6000610fd47febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e612f7c565b6000611cd76159f9565b611ce0836131ff565b611ce983613211565b90506118d3611cff82600363ffffffff612ff116565b611d1083600263ffffffff612ff116565b9063ffffffff613cd916565b61187b600080516020615ada83398151915261360d565b600080516020615a9a83398151915281565b6000611d5e600080516020615ada83398151915261360d565b611d666128a8565b9050600081111561187b5761187b60006001830361374d565b611d88816131ff565b611d9f600080516020615a9a83398151915261360d565b611db1611dab8261139b565b15613c82565b611dda611dbc611a6d565b600080516020615aba8339815191529060010163ffffffff613d6d16565b60008181526020818152604091829020805460ff191660019081179091558251908152915183927fecdf08e8a6c4493efb460f6abc7d14532074fa339c3a6410623a1d3ee0fb2cac92908290030190a261187b613120565b611e3b81613d8a565b611e44826131ff565b611e5b600080516020615a9a83398151915261360d565b600082815260208190526040902054611e8790600160a060020a0383811661010090920416141561364b565b60008281526020818152604091829020805474ffffffffffffffffffffffffffffffffffffffff001916610100600160a060020a038616908102919091179091558251908152915184927f9a52205165d510fc1e428886d52108725dc01ed544da1702dc7bd3fdb3f243b292908290030190a25050565b60006060600080600080600080611f136159f9565b611f1c8b6131ff565b60008b8152602081905260409020805460ff81169a506101009004600160a060020a03169750915089611f5d57604080516020810190915260008152611fea565b60018281018054604080516020600295841615610100026000190190931694909404601f810183900483028501830190915280845290830182828015611fe45780601f10611fb957610100808354040283529160200191611fe4565b820191906000526020600020905b815481529060010190602001808311611fc757829003601f168201915b50505050505b9750611ff58b613211565b905061200881600063ffffffff612ff116565b955061201b81600163ffffffff612ff116565b945061202e81600263ffffffff612ff116565b935061204181600363ffffffff612ff116565b9250505092959891949750929550565b600061205b6159f9565b6120636159f9565b61206b6159f9565b600080600080600061207b610f0e565b15156120d1576040805160e560020a62461bcd02815260206004820152601860248201527f434f4e54524143545f4e4f545f494e495449414c495a45440000000000000000604482015290519081900360640190fd5b6120db6000613e64565b6120e68c8c8c6138f0565b6120ee6128a8565b9850602060405190810160405280600081525095505b888210156122595761211582613211565b975061212888600063ffffffff612ff116565b945061213b88600263ffffffff612ff116565b935061214e88600363ffffffff612ff116565b60008381526020819052604090205490935060ff161515612170575081612186565b6121838461217e8588613ec2565b6131e9565b90505b8481146121ce5761219f8860008363ffffffff61300916565b6121a98289613d71565b6040805182815290518391600080516020615afa833981519152919081900360200190a25b6121d782613ed1565b96506121eb8760028363ffffffff61300916565b6121f58288613efd565b6122078660008363ffffffff613f1616565b6122198660038563ffffffff613f1616565b61223c600161222e8a8263ffffffff612ff116565b88919063ffffffff613f1616565b61224e8660028663ffffffff613f1616565b816001019150612104565b61226286613f3a565b61226a613120565b505050505050505050505050565b60008060006122856159f9565b61228d613f40565b90506122a081600163ffffffff612ff116565b93506122b381600363ffffffff612ff116565b92506122ca83611d1083600063ffffffff612ff116565b915050909192565b60008080808080806122f1600080516020615ada83398151915261360d565b6122fb8a89613f5c565b96506123056128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123458686106132d7565b61235185856000613fd6565b612317565b61235e613120565b5050505050505050505050565b600080808080808061238a600080516020615ada83398151915261360d565b6123948a89613f5c565b965061239e6128a8565b95506024600435019250602480350191505b86811015612356576008810283013560c01c94506010810282013560801c93506001016123de8686106132d7565b6123e88585614191565b6123b0565b60008060006123fb84611a2a565b60408051808201909152601281527f5245434f5645525f444953414c4c4f574544000000000000000000000000000060208201529015156124815760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5061248a611181565b9250612495836142e1565b60408051808201909152601a81527f5245434f5645525f5641554c545f4e4f545f434f4e5452414354000000000000602082015290151561251b5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50600160a060020a038416151561256c5760405130319250600160a060020a0384169083156108fc029084906000818181858888f19350505050158015612566573d6000803e3d6000fd5b5061262b565b5082612587600160a060020a0382163063ffffffff61430716565b91506125a3600160a060020a038216848463ffffffff61441c16565b60408051808201909152601d81527f5245434f5645525f544f4b454e5f5452414e534645525f4641494c454400000060208201529015156126295760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505b83600160a060020a031683600160a060020a03167f596caf56044b55fb8c4ca640089bbc2b63cae3e978b851f5745cbb7c5b288e02846040518082815260200191505060405180910390a350505050565b600080612687610f0e565b151561269657600091506127c1565b61269e612cd6565b9050600160a060020a03811615156126b957600091506127c1565b80600160a060020a031663fdef91068630876126d4886144a7565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b8381101561274357818101518382015260200161272b565b50505050905090810190601f1680156127705780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561279257600080fd5b505af11580156127a6573d6000803e3d6000fd5b505050506040513d60208110156127bc57600080fd5b505191505b509392505050565b6127d2826131ff565b6127e9600080516020615ada83398151915261360d565b61177a82826144b1565b6000806127fe612cd6565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b15801561108b57600080fd5b6000610fd47fe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8612f7c565b6128db6159f9565b6128e4846131ff565b6128fb600080516020615ada83398151915261360d565b61290f67ffffffffffffffff8311156132d7565b61291884613ed1565b905061293f60008461292b57600061292e565b60015b83919060ff1663ffffffff61300916565b612960600184612950576000612952565b835b83919063ffffffff61300916565b61296a8482613efd565b60408051838152905185917fd50ea115db6f0b433ef9cc4b71110dbd9202364a00488be90718990be5bf16a6919081900360200190a26129a9846130a9565b6129b1613120565b50505050565b6129bf6159f9565b6000806000806129ce876131ff565b612a0b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d794754612a068967ffffffffffffffff8a166145d2565b61462d565b612a176119138861139b565b612a2087613211565b9450612a3385600063ffffffff612ff116565b9350612a4685600363ffffffff612ff116565b9250612a5985600263ffffffff612ff116565b9150612a738261217e8867ffffffffffffffff1686613ec2565b905083811415612a8257612ad3565b612a948560008363ffffffff61300916565b612a9e8786613d71565b6040805182815290518891600080516020615afa833981519152919081900360200190a2612acb876130a9565b612ad3613120565b50505050505050565b600080600080600080600080612af06159f9565b612af86159f9565b612b018b6131ff565b612b0a8b613ed1565b9150612b158b612f80565b9050612b2882600063ffffffff612ff116565b15159950612b3d82600163ffffffff612ff116565b9850612b5081600063ffffffff612ff116565b9750612b6381600163ffffffff612ff116565b9650612b7681600263ffffffff612ff116565b9550612b818b61463b565b8095508196508297505050505050919395975091939597565b60608060006060612bad8686600161128d565b8051929650909450915081906000908110612bc457fe5b906020019060200201519150509250925092565b60ff81565b60608060008180612bfb600080516020615ada83398151915261360d565b871515612c21576040805160008082526020820190815281830190925295509350612ca0565b612c2a8861465b565b91945092509050878314612c88576040805160e560020a62461bcd02815260206004820152601c60248201527f494e56414c49445f414c4c4f43415445445f4b4559535f434f554e5400000000604482015290519081900360640190fd5b612c938383836147ed565b9095509350612ca0613120565b505050935093915050565b6000610fd47fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b6000610fd47f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b612f7c565b6000610fd47ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537612f7c565b7f07b39e0faf2521001ae4e58cb9ffd3840a63e205d288dc9c93c3774f0d79475481565b6000612d5a6159f9565b612d63836131ff565b612d6c83613211565b90506118d381600263ffffffff612ff116565b6000600019612d8c611ca2565b14905090565b6301e1338081565b612db1600080516020615ada83398151915261360d565b61187b6149c9565b60c881565b612dc7836131ff565b612dde600080516020615ada83398151915261360d565b612de88382614191565b612df483836001613fd6565b611396613120565b7f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee81565b6000612e2a6159f9565b612e3383612f80565b9050612e3e81612fac565b1580156118d35750612e5781600263ffffffff612ff116565b159392505050565b4390565b612e6b6159f9565b6000612e756159f9565b612e7e876131ff565b612e883388614e59565b612ea78615801590612ea2575067ffffffffffffffff8711155b6132d7565b612eb087613211565b9250612ec383600263ffffffff612ff116565b9150612ee167ffffffffffffffff6112d5848963ffffffff61323d16565b612f03600080516020615b1a833981519152888489898963ffffffff614ec616565b60408051828152905191935088917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f09181900360200190a2612f4d8360028463ffffffff61300916565b612f578784613d71565b612f5f613f40565b9050612f738160028863ffffffff613f1616565b612acb81613f3a565b5490565b612f886159f9565b50600090815260208181526040918290208251918201909252600390910154815290565b6000612fbe828263ffffffff612ff116565b612fcf83600163ffffffff612ff116565b1080612feb5750612fe782600263ffffffff612ff116565b4211155b92915050565b905167ffffffffffffffff604090920260ff161c1690565b67ffffffffffffffff811115613069576040805160e560020a62461bcd02815260206004820152600f60248201527f5041434b45445f4f564552464c4f570000000000000000000000000000000000604482015290519081900360640190fd5b825167ffffffffffffffff91821660409390930260ff1692831b9190921b19909116179052565b6000918252602082905260409091209051600390910155565b6000806130b46159f9565b60006130bf85615179565b93509350838314156130d057611a23565b6130d8613f40565b91506130e4838561526d565b905083831115613105576131008260008363ffffffff613f1616565b613117565b6131178260008363ffffffff61528416565b611a2382613f3a565b600061314b7fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e612f7c565b60010190506131807fcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e8263ffffffff613d6d16565b6040805182815290517ffb992daec9d46d64898e3a9336d02811349df6cbea8b95d4deb2fa6c7b454f0d9181900360200190a16040805182815290517f7220970e1f1f12864ecccd8942690a837c7a8dd45d158cb891eb45a8a69134aa9181900360200190a150565b60008183106131f857816118d3565b5090919050565b61187b61320a6128a8565b82106132d7565b6132196159f9565b50600090815260208181526040918290208251918201909252600290910154815290565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b508091505b5092915050565b80151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f4f55545f4f465f52414e47450000000000000000000000000000000000000000604482015290519081900360640190fd5b60608061334283603063ffffffff6136a216565b6040519080825280601f01601f19166020018201604052801561336f578160200160208202803883390190505b5061338184606063ffffffff6136a216565b6040519080825280601f01601f1916602001820160405280156133ae578160200160208202803883390190505b509092509050915091565b6000805b85811015613425576133d8898989840163ffffffff61529716565b60018082015460801c85840160308181028a0190810192909252835460209283015260028401546060918202890192830152600384015460408301526004840154910152909250016133bd565b505050505050505050565b6134386159f9565b6000806134436159f9565b61344c876131ff565b6134563388614e59565b84151561346257612ad3565b61346b87613211565b935061347e84600263ffffffff612ff116565b92506134af61349485600363ffffffff612ff116565b8710158015612ea25750836112d5888863ffffffff61323d16565b6134d0600080516020615b1a8339815191528888888763ffffffff61532a16565b92506134e48460028563ffffffff61300916565b60408051848152905188917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a261352b84600063ffffffff612ff116565b91508186101561356c576135478460008863ffffffff61300916565b6040805187815290518891600080516020615afa833981519152919081900360200190a25b6135768785613d71565b61357e613f40565b90506135928160028763ffffffff61528416565b61359b81613f3a565b612acb876130a9565b600081511180156135b7575060ff815111155b151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f57524f4e475f4e414d455f4c454e475448000000000000000000000000000000604482015290519081900360640190fd5b61187b61364633836000604051908082528060200260200182016040528015613640578160200160208202803883390190505b5061267c565b615533565b80151561187b576040805160e560020a62461bcd02815260206004820152601160248201527f56414c55455f49535f5448455f53414d45000000000000000000000000000000604482015290519081900360640190fd5b6000808315156136b557600091506132d0565b508282028284828115156136c557fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146132cb5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b60008060008061375b6159f9565b60006137656159f9565b613782888a11158015612ea2575061377b6128a8565b89106132d7565b8891505b8782116138ba5761379682613211565b92506137a983600263ffffffff612ff116565b94506137bc83600363ffffffff612ff116565b9350838514156137cb576138af565b8385116137d457fe5b8385039650948601946137ef8360028663ffffffff61300916565b6138018360008663ffffffff61300916565b61380b8284613d71565b613814826130a9565b60408051858152905183917fdd01838a366ae4dc9a86e1922512c0716abebc9a440baae0e22d2dec578223f0919081900360200190a26040805185815290518391600080516020615afa833981519152919081900360200190a26040805167ffffffffffffffff89168152905183917f9824694569ba758f8872bb150515caaf8f1e2cc27e6805679c4ac8c3b9b83d87919081900360200190a25b816001019150613786565b6000861115613425576138cb613f40565b90506138df8160028863ffffffff61528416565b6138e881613f3a565b613425613120565b6138f98361558a565b6139297ffb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d035378463ffffffff613d6d16565b6139597fbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d08363ffffffff613d6d16565b61396360026155ea565b61396c81613c08565b613974612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156139ae57600080fd5b505af11580156139c2573d6000803e3d6000fd5b505050506040513d60208110156139d857600080fd5b5051600160a060020a031663095ea7b36139f0612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613a2a57600080fd5b505af1158015613a3e573d6000803e3d6000fd5b505050506040513d6020811015613a5457600080fd5b50516040805163ffffffff841660e01b8152600160a060020a03909216600483015260001960248301525160448083019260209291908290030181600087803b158015613aa057600080fd5b505af1158015613ab4573d6000803e3d6000fd5b505050506040513d6020811015613aca57600080fd5b505060408051600160a060020a038516815290517fa44aa4b7320163340e971b1f22f153bbb8a0151d783bd58377018ea5bc96d0c99181900360200190a16040805183815290517fdb042010b15d1321c99552200b350bba0a95dfa3d0b43869983ce74b44d644ee9181900360200190a1505050565b613b48611ca2565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a4544000000000000000060208201529015613bcd5760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b50613c06613bd9612e5f565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff613d6d16565b565b613c186301e133808211156132d7565b613c487f8e3a1f3826a82c1116044b334cae49f3c3d12c3866a1c4b18af461e12e58a18e8263ffffffff613d6d16565b6040805182815290517f4cccd9748bff0341d9852cc61d82652a3003dcebea088f05388c0be1f26b4c8a9181900360200190a150565b5490565b80151561187b576040805160e560020a62461bcd02815260206004820152601b60248201527f57524f4e475f4f50455241544f525f4143544956455f53544154450000000000604482015290519081900360640190fd5b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613d655760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b505050900390565b9055565b6000918252602082905260409091209051600290910155565b613d938161558a565b613d9b612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015613dd557600080fd5b505af1158015613de9573d6000803e3d6000fd5b505050506040513d6020811015613dff57600080fd5b5051600160a060020a038281169116141561187b576040805160e560020a62461bcd02815260206004820152601360248201527f4c49444f5f5245574152445f4144445245535300000000000000000000000000604482015290519081900360640190fd5b613e6c611c77565b811461187b576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b60008183116131f857816118d3565b613ed96159f9565b50600090815260208181526040918290208251918201909252600490910154815290565b6000918252602082905260409091209051600490910155565b6113968383613f3584613f298888612ff1565b9063ffffffff61323d16565b613009565b51600155565b613f486159f9565b506040805160208101909152600154815290565b600882046010820481148015613f73575060088306155b8015613f80575060108206155b1515612feb576040805160e560020a62461bcd02815260206004820152601360248201527f494e56414c49445f5245504f52545f4441544100000000000000000000000000604482015290519081900360640190fd5b613fde6159f9565b6000806000613feb6159f9565b6000613ff689613211565b955061400986600163ffffffff612ff116565b94508488141561401857613425565b868061402357508488115b151561409f576040805160e560020a62461bcd02815260206004820152602160248201527f4558495445445f56414c494441544f52535f434f554e545f444543524541534560448201527f4400000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b6140b086600363ffffffff612ff116565b93506140cc60006140c08b612f80565b9063ffffffff612ff116565b9250828410156140d857fe5b6140e68385038911156132d7565b6140f88660018a63ffffffff61300916565b6141028987613d71565b6040805189815290518a917f0f67960648751434ae86bf350db61194f387fda387e7f568b0ccd0ae0c220166919081900360200190a2614140613f40565b915061414c888661526d565b90508488111561416d576141688260018363ffffffff613f1616565b61417f565b61417f8260018363ffffffff61528416565b61418882613f3a565b613425896130a9565b6141996159f9565b60006141a36159f9565b60008060006141b188612f80565b95506141c486600063ffffffff612ff116565b9450848714156141d3576142d7565b6141dc88613211565b93506141ef84600163ffffffff612ff116565b925061420284600363ffffffff612ff116565b91508282101561420e57fe5b61421c8383038811156132d7565b61422d86600163ffffffff612ff116565b905080871115801561423e57508085115b1561426057614260600261425061187e565b889190420163ffffffff61300916565b6142728660008963ffffffff61300916565b61427c8887613090565b877f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb88836142b18a600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a26142d7886130a9565b5050505050505050565b600080600160a060020a03831615156142fd576000915061117b565b50506000903b1190565b60408051600160a060020a0383166024808301919091528251808303909101815260449091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f70a082310000000000000000000000000000000000000000000000000000000017905260009081806143878684615650565b60408051808201909152601c81527f534146455f4552435f32305f42414c414e43455f524556455254454400000000602082015291935091508215156144125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561180c5781810151838201526020016117f4565b5095945050505050565b60408051600160a060020a038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905260009061449e8582615681565b95945050505050565b8051602002815290565b6144b96159f9565b60006144c36159f9565b60006144ce86612f80565b93506144e184600163ffffffff612ff116565b9250828514156144f057610fa1565b6144f986613211565b915061451761450f83600363ffffffff612ff116565b8611156132d7565b61452884600063ffffffff612ff116565b905080851015801561453957508083105b1561455b5761455b600261454b61187e565b869190420163ffffffff61300916565b61456d8460018763ffffffff61300916565b6145778685613090565b857f0ee42dd52dd2b8feb0fc9cc054a08162a23e022c177319db981cf339e5b8ffdb82876145ac88600263ffffffff612ff116565b60408051938452602084019290925282820152519081900360600190a2610fa1866130a9565b60408051600280825260608083018452926020830190803883390190505090508281600081518110151561460257fe5b60209081029091010152805182908290600190811061461d57fe5b6020908102909101015292915050565b61177a61364633848461267c565b60008060008061464a856156cf565b919790965090869003945092505050565b600060608060006060600080600080600080614675611a6d565b9750876040519080825280602002602001820160405280156146a1578160200160208202803883390190505b509950876040519080825280602002602001820160405280156146ce578160200160208202803883390190505b509850876040519080825280602002602001820160405280156146fb578160200160208202803883390190505b5096506147066128a8565b94505b8481101561478d5761471a816156cf565b9550935091508284141561472d57614785565b808a8781518110151561473c57fe5b602090810290910101528851828403908a908890811061475857fe5b6020908102909101015286518285039088908890811061477457fe5b602090810290910101526001909501945b600101614709565b8515156147b557604080516000808252602082018181528284019093529c509a5098506147de565b878610156147c757858a528589528587525b6147d289888e615754565b9a508a8c10156147de57fe5b50505050505050509193909250565b6060806000806000806147fe6159f9565b60006148086159f9565b6148118c61332e565b9099509750600096505b8a5182101561498c576148448b8381518110151561483557fe5b90602001906020020151613211565b925061485783600363ffffffff612ff116565b9550898281518110151561486757fe5b6020908102909101015161488284600163ffffffff612ff116565b0194508585141561489257614981565b85851161489b57fe5b85850393506148db8b838151811015156148b157fe5b60209081029091010151600080516020615b1a8339815191529088878d8d8d63ffffffff6133b916565b8a51968401968b90839081106148ed57fe5b906020019060200201517f24eb1c9e765ba41accf9437300ea91ece5ed3f897ec3cdee0e9debd7fe309b78866040518082815260200191505060405180910390a26149408360038763ffffffff61300916565b6149618b8381518110151561495157fe5b9060200190602002015184613d71565b6149818b8381518110151561497257fe5b906020019060200201516130a9565b81600101915061481b565b868c1461499557fe5b61499d613f40565b90506149b18160038963ffffffff613f1616565b6149ba81613f3a565b50505050505050935093915050565b600080600060608060606000806149de612d01565b600160a060020a03166323509a2d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614a1857600080fd5b505af1158015614a2c573d6000803e3d6000fd5b505050506040513d6020811015614a4257600080fd5b5051604080517ff5eb42dc0000000000000000000000000000000000000000000000000000000081523060048201529051919850600160a060020a0389169163f5eb42dc916024808201926020929091908290030181600087803b158015614aa957600080fd5b505af1158015614abd573d6000803e3d6000fd5b505050506040513d6020811015614ad357600080fd5b50519550851515614ae357614e4f565b614aec86611528565b9450945094505b8451811015614d565760028482815181101515614b0c57fe5b906020019060200201511015614b2157614d4e565b8281815181101515614b2f57fe5b9060200190602002015115614bf95760018482815181101515614b4e57fe5b602090810290910101805190911c90528351614b8790859083908110614b7057fe5b60209081029091010151839063ffffffff61323d16565b91508481815181101515614b9757fe5b90602001906020020151600160a060020a03167fe915a473fc2ef8e0231da98380f853b2aeea117a4392c67e753c54186bfbbd128583815181101515614bd957fe5b906020019060200201516040518082815260200191505060405180910390a25b86600160a060020a0316638fcb4e5b8683815181101515614c1657fe5b906020019060200201518684815181101515614c2e57fe5b906020019060200201516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015614c8757600080fd5b505af1158015614c9b573d6000803e3d6000fd5b505050506040513d6020811015614cb157600080fd5b50508351614cdc90859083908110614cc557fe5b60209081029091010151899063ffffffff61323d16565b97508481815181101515614cec57fe5b90602001906020020151600160a060020a03167fdf29796aad820e4bb192f3a8d631b76519bcd2cbe77cc85af20e9df53cece0868583815181101515614d2e57fe5b906020019060200201516040518082815260200191505060405180910390a25b600101614af3565b6000821115614e4f57614d67612d01565b600160a060020a03166327810b6e6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614da157600080fd5b505af1158015614db5573d6000803e3d6000fd5b505050506040513d6020811015614dcb57600080fd5b5051604080517f46114928000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a039092169163461149289160448082019260009290919082900301818387803b158015614e3657600080fd5b505af1158015614e4a573d6000803e3d6000fd5b505050505b5050505050505090565b6000818152602081905260409020546101008104600160a060020a03908116908416149060ff166129b1828015614e8d5750815b806136465750613646857f75abc64490e17b40ea1e66691c3eb493647b24430b358bd87ec3e5127f1621ee614ec187615784565b61267c565b6000806000606060008088118015614ef5575067ffffffffffffffff614ef28a8a63ffffffff61323d16565b11155b1515614f4b576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b614f5c88603063ffffffff6136a216565b8751148015614f7b5750614f7788606063ffffffff6136a216565b8651145b1515614fd1576040805160e560020a62461bcd02815260206004820152600f60248201527f4c454e4754485f4d49534d415443480000000000000000000000000000000000604482015290519081900360640190fd5b604080516030808252606082019092529060208201610600803883390190505091505b8781101561516a5761500d8b8b8b63ffffffff61529716565b60308281028901602081810151918301519286018390528501819052919550171592508215615086576040805160e560020a62461bcd02815260206004820152600960248201527f454d5054595f4b45590000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60208201518455603082015160801b600185015560608102602087010180516002860155602081015160038601556040810151600486015560018201915060018a01995050897fc77a17d6b857abe6d6e6c37301621bc72c4dd52fa8830fb54dfa715c04911a89836040518080602001828103825283818151815260200191508051906020019080838360005b8381101561512b578181015183820152602001615113565b50505050905090810190601f1680156151585780820380516001836020036101000a031916815260200191505b509250505060405180910390a2614ff4565b50969998505050505050505050565b6000806151846159f9565b61518c6159f9565b600061519786613211565b92506151a286613ed1565b91506151b583600363ffffffff612ff116565b90506151c883600063ffffffff612ff116565b93506151d386612e20565b15156151e15780935061522f565b6151f282600063ffffffff612ff116565b1561522f5761522c816152278661521086600163ffffffff612ff116565b61522188600163ffffffff612ff116565b016131e9565b613ec2565b93505b61524082600263ffffffff612ff116565b94508385146152655761525b8260028663ffffffff61300916565b6152658683613efd565b505050915091565b600081831161527e578282036118d3565b50900390565b6113968383613f3584611d108888612ff1565b6040805160208082018690528183018590526060808301859052835180840390910181526080909201928390528151600093918291908401908083835b602083106152f35780518252601f1990920191602091820191016152d4565b5181516020939093036101000a6000190180199091169216919091179052604051920182900390912060001c979650505050505050565b60008060008060606000808811801561535257508661534f8a8a63ffffffff61323d16565b11155b8015615366575067ffffffffffffffff8711155b15156153bc576040805160e560020a62461bcd02815260206004820152601260248201527f494e56414c49445f4b4559535f434f554e540000000000000000000000000000604482015290519081900360640190fd5b60408051603080825260608201909252906020820161060080388339019050509150508787015b88811115615524576154008b8b600019840163ffffffff61529716565b9450600185015460801c6030830152845460208301528681101561545a576154338b8b6000198a0163ffffffff61529716565b9350600092505b600583101561545657828401548386015560018301925061543a565b8394505b600092505b60058310156154795760008386015560018301925061545f565b600187039650600181039050897fea4b75aaf57196f73d338cadf79ecd0a437902e2dd0d2c4c2cf3ea71b8ab27b9836040518080602001828103825283818151815260200191508051906020019080838360005b838110156154e55781810151838201526020016154cd565b50505050905090810190601f1680156155125780820380516001836020036101000a031916815260200191505b509250505060405180910390a26153e3565b50949998505050505050505050565b80151561187b576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038116151561187b576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f414444524553530000000000000000000000000000000000000000604482015290519081900360640190fd5b61561a7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff613d6d16565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b6000806000806040516020818751602089018a5afa9250600083111561567557805191505b50909590945092505050565b6000806040516020818551602087016000895af160008111156156c5573d80156156b257602081146156bb576156c3565b600193506156c3565b600183511493505b505b5090949350505050565b60008060006156dc6159f9565b6156e46159f9565b6156ed86613211565b91506156f886613ed1565b905061570b82600163ffffffff612ff116565b945061571e82600363ffffffff612ff116565b935061573181600263ffffffff612ff116565b92508383101580156157435750848410155b151561574b57fe5b50509193909250565b6000805b828210156127c15761576d85858486036157c5565b905080151561577b576127c1565b90810190615758565b60408051600180825281830190925260609160208083019080388339019050509050818160008151811015156157b657fe5b60209081029091010152919050565b8251600090600019828080808715156157e157600096506159c4565b600092505b895183101561589f5788838151811015156157fd57fe5b906020019060200201518a8481518110151561581557fe5b602090810290910101511061582957615894565b898381518110151561583757fe5b9060200190602002015185111561586e5782955060019350898381518110151561585d57fe5b906020019060200201519450615894565b898381518110151561587c57fe5b90602001906020020151851415615894576001840193505b8260010192506157e6565b8315156158af57600096506159c4565b50600019905060005b895181101561595f5788818151811015156158cf57fe5b906020019060200201518a828151811015156158e757fe5b60209081029091010151106158fb57615957565b848a8281518110151561590a57fe5b906020019060200201511180156159375750818a8281518110151561592b57fe5b90602001906020020151105b1561595757898181518110151561594a57fe5b9060200190602002015191505b6001016158b8565b6159a260018511615970578861597a565b61597a89866159d1565b8661599c858d8b81518110151561598d57fe5b906020019060200201516131e9565b036131e9565b9650868a878151811015156159b357fe5b602090810290910101805190910190525b5050505050509392505050565b600082156159f05781600184038115156159e757fe5b046001016118d3565b50600092915050565b60408051602081019091526000815290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10615a4c5782800160ff19823516178555615a79565b82800160010185558215615a79579182015b82811115615a79578235825591602001919060010190615a5e565b50610f3492610fd79250905b80821115610f345760008155600101615a85560078523850fdd761612f46e844cf5a16bda6b3151d6ae961fd7e8e7b92bfbca7f86f5220989faafdc182d508d697678366f4e831f5f56166ad69bfc253fc548fb1bb75b874360e0bfd87f964eadd8276d8efb7c942134fc329b513032d0803e0c6947f955eec7e1f626bee3afd2aa47b5de04ddcdd3fe78dc8838213015ef58dfdeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9a165627a7a72305820c0673b4dec33ab43c022a0ca404b407602f0e9ac1e02e04efbe56d5c3734569c0029

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.