false
false
0

Contract Address Details

0xD84c1623938d5bf763947d740cAdAc73c017d0A1

Contract Name
Lolik
Creator
0x0768fa–fcb6ac at 0xe24fe3–216155
Balance
0 FTN ( )
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
6915541
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
Lolik




Optimization enabled
true
Compiler version
v0.4.24+commit.e67f0147




Optimization runs
200
EVM Version
constantinople




Verified at
2025-08-15T09:27:25.389290Z

contracts/0.4.24/Lolik.sol

Sol2uml
new
// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
pragma solidity 0.4.24;

import "@aragon/os/contracts/apps/AragonApp.sol";
import "@aragon/os/contracts/lib/math/SafeMath.sol";

import "../common/interfaces/ILolikLocator.sol";
import "../common/interfaces/IBurner.sol";

import "./lib/StakeLimitUtils.sol";
import "../common/lib/Math256.sol";

import "./StFTNPermit.sol";

import "./utils/Versioned.sol";

interface IPostTokenRebaseReceiver {
    function handlePostTokenRebase(
        uint256 _reportTimestamp,
        uint256 _timeElapsed,
        uint256 _preTotalShares,
        uint256 _preTotalFtn,
        uint256 _postTotalShares,
        uint256 _postTotalFtn,
        uint256 _sharesMintedAsFees
    ) external;
}

interface IOracleReportSanityChecker {
    function checkAccountingOracleReport(
        uint256 _timeElapsed,
        uint256 _preCLBalance,
        uint256 _postCLBalance,
        uint256 _withdrawalVaultBalance,
        uint256 _elRewardsVaultBalance,
        uint256 _sharesRequestedToBurn,
        uint256 _preCLValidators,
        uint256 _postCLValidators
    ) external view;

    function smoothenTokenRebase(
        uint256 _preTotalPooledFtn,
        uint256 _preTotalShares,
        uint256 _preCLBalance,
        uint256 _postCLBalance,
        uint256 _withdrawalVaultBalance,
        uint256 _elRewardsVaultBalance,
        uint256 _sharesRequestedToBurn,
        uint256 _ftnToLockForWithdrawals,
        uint256 _newSharesToBurnForWithdrawals
    ) external view returns (
        uint256 withdrawals,
        uint256 elRewards,
        uint256 simulatedSharesToBurn,
        uint256 sharesToBurn
    );

    function checkWithdrawalQueueOracleReport(
        uint256 _lastFinalizableRequestId,
        uint256 _reportTimestamp
    ) external view;

    function checkSimulatedShareRate(
        uint256 _postTotalPooledFtn,
        uint256 _postTotalShares,
        uint256 _ftnLockedOnWithdrawalQueue,
        uint256 _sharesBurntDueToWithdrawals,
        uint256 _simulatedShareRate
    ) external view;
}

interface ILolikExecutionLayerRewardsVault {
    function withdrawRewards(uint256 _maxAmount) external returns (uint256 amount);
}

interface IWithdrawalVault {
    function withdrawWithdrawals(uint256 _amount) external;
}

interface IStakingRouter {
    function deposit(
        uint256 _depositsCount,
        uint256 _stakingModuleId,
        bytes _depositCalldata
    ) external payable;

    function getStakingRewardsDistribution()
        external
        view
        returns (
            address[] memory recipients,
            uint256[] memory stakingModuleIds,
            uint96[] memory stakingModuleFees,
            uint96 totalFee,
            uint256 precisionPoints
        );

    function getWithdrawalCredentials() external view returns (bytes32);

    function reportRewardsMinted(uint256[] _stakingModuleIds, uint256[] _totalShares) external;

    function getTotalFeeE4Precision() external view returns (uint16 totalFee);

    function getStakingFeeAggregateDistributionE4Precision() external view returns (
        uint16 modulesFee, uint16 treasuryFee
    );

    function getStakingModuleMaxDepositsCount(uint256 _stakingModuleId, uint256 _maxDepositsValue)
        external
        view
        returns (uint256);

    function TOTAL_BASIS_POINTS() external view returns (uint256);
}

interface IWithdrawalQueue {
    function prefinalize(uint256[] _batches, uint256 _maxShareRate)
        external
        view
        returns (uint256 ftnToLock, uint256 sharesToBurn);

    function finalize(uint256 _lastIdToFinalize, uint256 _maxShareRate) external payable;

    function isPaused() external view returns (bool);

    function unfinalizedStFTN() external view returns (uint256);

    function isBunkerModeActive() external view returns (bool);
}

/**
* @title Liquid staking pool implementation
*
* Lolik is an Bahamut liquid staking protocol solving the problem of frozen staked ftn on Consensus Layer
* being unavailable for transfers and DeFi on Execution Layer.
*
* Since balances of all token holders change when the amount of total pooled Ftn
* changes, this token cannot fully implement ERC20 standard: it only emits `Transfer`
* events upon explicit transfer between holders. In contrast, when Lolik oracle reports
* rewards, no Transfer events are generated: doing so would require emitting an event
* for each token holder and thus running an unbounded loop.
*
* ---
* NB: Order of inheritance must preserve the structured storage layout of the previous versions.
*
* @dev Lolik is derived from `StFTNPermit` that has a structured storage:
* SLOT 0: mapping (address => uint256) private shares (`StFTN`)
* SLOT 1: mapping (address => mapping (address => uint256)) private allowances (`StFTN`)
* SLOT 2: mapping(address => uint256) internal noncesByAddress (`StFTNPermit`)
*
* `Versioned` and `AragonApp` both don't have the pre-allocated structured storage.
*/
contract Lolik is Versioned, StFTNPermit, AragonApp {
    using SafeMath for uint256;
    using UnstructuredStorage for bytes32;
    using StakeLimitUnstructuredStorage for bytes32;
    using StakeLimitUtils for StakeLimitState.Data;

    /// ACL
    bytes32 public constant PAUSE_ROLE =
        0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d; // keccak256("PAUSE_ROLE");
    bytes32 public constant RESUME_ROLE =
        0x2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7; // keccak256("RESUME_ROLE");
    bytes32 public constant STAKING_PAUSE_ROLE =
        0x84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de8; // keccak256("STAKING_PAUSE_ROLE")
    bytes32 public constant STAKING_CONTROL_ROLE =
        0xa42eee1333c0758ba72be38e728b6dadb32ea767de5b4ddbaea1dae85b1b051f; // keccak256("STAKING_CONTROL_ROLE")
    bytes32 public constant UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE =
        0xe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c; // keccak256("UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE")

    // uint256 private constant DEPOSIT_SIZE = 32 ether;
    uint256 private constant DEPOSIT_SIZE = 8192 ether;

    /// @dev storage slot position for the Lolik protocol contracts locator
    bytes32 internal constant LOLIK_LOCATOR_POSITION =
        0x9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df7; // keccak256("lolik.Lolik.LolikLocator")
    /// @dev storage slot position of the staking rate limit structure
    bytes32 internal constant STAKING_STATE_POSITION =
        0xa3678de4a579be090bed1177e0a24f77cc29d181ac22fd7688aca344d8938015; // keccak256("lolik.Lolik.stakeLimit");
    /// @dev amount of Ftn (on the current Bahamut side) buffered on this smart contract balance
    bytes32 internal constant BUFFERED_FTN_POSITION =
        0xed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b0; // keccak256("lolik.Lolik.bufferedFtn");
    /// @dev number of deposited validators (incrementing counter of deposit operations).
    bytes32 internal constant DEPOSITED_VALIDATORS_POSITION =
        0xe6e35175eb53fc006520a2a9c3e9711a7c00de6ff2c32dd31df8c5a24cac1b5c; // keccak256("lolik.Lolik.depositedValidators");
    /// @dev total amount of ftn on Consensus Layer (sum of all the balances of Lolik validators)
    // "beacon" in the `keccak256()` parameter is staying here for compatibility reason
    bytes32 internal constant CL_BALANCE_POSITION =
        0xa66d35f054e68143c18f32c990ed5cb972bb68a68f500cd2dd3a16bbf3686483; // keccak256("lolik.Lolik.beaconBalance");
    /// @dev number of Lolik's validators available in the Consensus Layer state
    // "beacon" in the `keccak256()` parameter is staying here for compatibility reason
    bytes32 internal constant CL_VALIDATORS_POSITION =
        0x9f70001d82b6ef54e9d3725b46581c3eb9ee3aa02b941b6aa54d678a9ca35b10; // keccak256("lolik.Lolik.beaconValidators");
    /// @dev Just a counter of total amount of execution layer rewards received by Lolik contract. Not used in the logic.
    bytes32 internal constant TOTAL_EL_REWARDS_COLLECTED_POSITION =
        0xafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb; // keccak256("lolik.Lolik.totalELRewardsCollected");

    // Staking was paused (don't accept user's ftn submits)
    event StakingPaused();
    // Staking was resumed (accept user's ftn submits)
    event StakingResumed();
    // Staking limit was set (rate limits user's submits)
    event StakingLimitSet(uint256 maxStakeLimit, uint256 stakeLimitIncreasePerBlock);
    // Staking limit was removed
    event StakingLimitRemoved();

    // Emits when validators number delivered by the oracle
    event CLValidatorsUpdated(
        uint256 indexed reportTimestamp,
        uint256 preCLValidators,
        uint256 postCLValidators
    );

    // Emits when var at `DEPOSITED_VALIDATORS_POSITION` changed
    event DepositedValidatorsChanged(
        uint256 depositedValidators
    );

    // Emits when oracle accounting report processed
    event FTNDistributed(
        uint256 indexed reportTimestamp,
        uint256 preCLBalance,
        uint256 postCLBalance,
        uint256 withdrawalsWithdrawn,
        uint256 executionLayerRewardsWithdrawn,
        uint256 postBufferedFtn
    );

    // Emits when token rebased (total supply and/or total shares were changed)
    event TokenRebased(
        uint256 indexed reportTimestamp,
        uint256 timeElapsed,
        uint256 preTotalShares,
        uint256 preTotalFtn,
        uint256 postTotalShares,
        uint256 postTotalFtn,
        uint256 sharesMintedAsFees
    );

    // Lolik locator set
    event LolikLocatorSet(address LolikLocator);

    // The amount of FTN withdrawn from LolikExecutionLayerRewardsVault to Lolik
    event ELRewardsReceived(uint256 amount);

    // The amount of FTN withdrawn from WithdrawalVault to Lolik
    event WithdrawalsReceived(uint256 amount);

    // Records a deposit made by a user
    event Submitted(address indexed sender, uint256 amount, address referral);

    // The `amount` of ftn was sent to the deposit_contract.deposit function
    event Unbuffered(uint256 amount);

    /**
    * @dev As AragonApp, Lolik contract must be initialized with following variables:
    *      NB: by default, staking and the whole Lolik pool are in paused state
    *
    * The contract's balance must be non-zero to allow initial holder bootstrap.
    *
    * @param _lolikLocator lolik locator contract
    * @param _eip712StFTN eip712 helper contract for StFTN
    */
    function initialize(address _lolikLocator, address _eip712StFTN)
        public
        payable
        onlyInit
    {
        _bootstrapInitialHolder();
        _initialize_v2(_lolikLocator, _eip712StFTN);
        initialized();
    }

    /**
     * initializer for the Lolik version "2"
     */
    function _initialize_v2(address _lolikLocator, address _eip712StFTN) internal {
        _setContractVersion(2);

        LOLIK_LOCATOR_POSITION.setStorageAddress(_lolikLocator);
        _initializeEIP712StFTN(_eip712StFTN);

        // set infinite allowance for burner from withdrawal queue
        // to burn finalized requests' shares
        _approve(
            ILolikLocator(_lolikLocator).withdrawalQueue(),
            ILolikLocator(_lolikLocator).burner(),
            INFINITE_ALLOWANCE
        );

        emit LolikLocatorSet(_lolikLocator);
    }

    /**
     * @notice A function to finalize upgrade to v2 (from v1). Can be called only once
     * @dev Value "1" in CONTRACT_VERSION_POSITION is skipped due to change in numbering
     *
     * The initial protocol token holder must exist.
     *
     * For more details see https://github.com/lidofinance/lolik-improvement-proposals/blob/develop/LIPS/lip-10.md
     */
    function finalizeUpgrade_v2(address _lolikLocator, address _eip712StFTN) external {
        _checkContractVersion(0);
        require(hasInitialized(), "NOT_INITIALIZED");

        require(_lolikLocator != address(0), "LOLIK_LOCATOR_ZERO_ADDRESS");
        require(_eip712StFTN != address(0), "EIP712_STFTN_ZERO_ADDRESS");

        require(_sharesOf(INITIAL_TOKEN_HOLDER) != 0, "INITIAL_HOLDER_EXISTS");

        _initialize_v2(_lolikLocator, _eip712StFTN);
    }

    /**
     * @notice Stops accepting new Ftn to the protocol
     *
     * @dev While accepting new Ftn is stopped, calls to the `submit` function,
     * as well as to the default payable function, will revert.
     *
     * Emits `StakingPaused` event.
     */
    function pauseStaking() external {
        _auth(STAKING_PAUSE_ROLE);

        _pauseStaking();
    }

    /**
     * @notice Resumes accepting new Ftn to the protocol (if `pauseStaking` was called previously)
     * NB: Staking could be rate-limited by imposing a limit on the stake amount
     * at each moment in time, see `setStakingLimit()` and `removeStakingLimit()`
     *
     * @dev Preserves staking limit if it was set previously
     *
     * Emits `StakingResumed` event
     */
    function resumeStaking() external {
        _auth(STAKING_CONTROL_ROLE);
        require(hasInitialized(), "NOT_INITIALIZED");

        _resumeStaking();
    }

    /**
     * @notice Sets the staking rate limit
     *
     * ▲ Stake limit
     * │.....  .....   ........ ...            ....     ... Stake limit = max
     * │      .       .        .   .   .      .    . . .
     * │     .       .              . .  . . .      . .
     * │            .                .  . . .
     * │──────────────────────────────────────────────────> Time
     * │     ^      ^          ^   ^^^  ^ ^ ^     ^^^ ^     Stake events
     *
     * @dev Reverts if:
     * - `_maxStakeLimit` == 0
     * - `_maxStakeLimit` >= 2^96
     * - `_maxStakeLimit` < `_stakeLimitIncreasePerBlock`
     * - `_maxStakeLimit` / `_stakeLimitIncreasePerBlock` >= 2^32 (only if `_stakeLimitIncreasePerBlock` != 0)
     *
     * Emits `StakingLimitSet` event
     *
     * @param _maxStakeLimit max stake limit value
     * @param _stakeLimitIncreasePerBlock stake limit increase per single block
     */
    function setStakingLimit(uint256 _maxStakeLimit, uint256 _stakeLimitIncreasePerBlock) external {
        _auth(STAKING_CONTROL_ROLE);

        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakingLimit(_maxStakeLimit, _stakeLimitIncreasePerBlock)
        );

        emit StakingLimitSet(_maxStakeLimit, _stakeLimitIncreasePerBlock);
    }

    /**
     * @notice Removes the staking rate limit
     *
     * Emits `StakingLimitRemoved` event
     */
    function removeStakingLimit() external {
        _auth(STAKING_CONTROL_ROLE);

        STAKING_STATE_POSITION.setStorageStakeLimitStruct(STAKING_STATE_POSITION.getStorageStakeLimitStruct().removeStakingLimit());

        emit StakingLimitRemoved();
    }

    /**
     * @notice Check staking state: whether it's paused or not
     */
    function isStakingPaused() external view returns (bool) {
        return STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingPaused();
    }


    /**
     * @notice Returns how much Ftn can be staked in the current block
     * @dev Special return values:
     * - 2^256 - 1 if staking is unlimited;
     * - 0 if staking is paused or if limit is exhausted.
     */
    function getCurrentStakeLimit() external view returns (uint256) {
        return _getCurrentStakeLimit(STAKING_STATE_POSITION.getStorageStakeLimitStruct());
    }

    /**
     * @notice Returns full info about current stake limit params and state
     * @dev Might be used for the advanced integration requests.
     * @return isStakingPaused staking pause state (equivalent to return of isStakingPaused())
     * @return isStakingLimitSet whether the stake limit is set
     * @return currentStakeLimit current stake limit (equivalent to return of getCurrentStakeLimit())
     * @return maxStakeLimit max stake limit
     * @return maxStakeLimitGrowthBlocks blocks needed to restore max stake limit from the fully exhausted state
     * @return prevStakeLimit previously reached stake limit
     * @return prevStakeBlockNumber previously seen block number
     */
    function getStakeLimitFullInfo()
        external
        view
        returns (
            bool isStakingPaused,
            bool isStakingLimitSet,
            uint256 currentStakeLimit,
            uint256 maxStakeLimit,
            uint256 maxStakeLimitGrowthBlocks,
            uint256 prevStakeLimit,
            uint256 prevStakeBlockNumber
        )
    {
        StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct();

        isStakingPaused = stakeLimitData.isStakingPaused();
        isStakingLimitSet = stakeLimitData.isStakingLimitSet();

        currentStakeLimit = _getCurrentStakeLimit(stakeLimitData);

        maxStakeLimit = stakeLimitData.maxStakeLimit;
        maxStakeLimitGrowthBlocks = stakeLimitData.maxStakeLimitGrowthBlocks;
        prevStakeLimit = stakeLimitData.prevStakeLimit;
        prevStakeBlockNumber = stakeLimitData.prevStakeBlockNumber;
    }

    /**
    * @notice Send funds to the pool
    * @dev Users are able to submit their funds by transacting to the fallback function.
    * Unlike vanilla Bahamut Deposit contract, accepting only 8192-Ftn transactions, Lolik
    * accepts payments of any size. Submitted Ftns are stored in Buffer until someone calls
    * deposit() and pushes them to the Bahamut Deposit contract.
    */
    // solhint-disable-next-line no-complex-fallback
    function() external payable {
        // protection against accidental submissions by calling non-existent function
        require(msg.data.length == 0, "NON_EMPTY_DATA");
        _submit(0);
    }

    /**
     * @notice Send funds to the pool with optional _referral parameter
     * @dev This function is alternative way to submit funds. Supports optional referral address.
     * @return Amount of StFTN shares generated
     */
    function submit(address _referral) external payable returns (uint256) {
        return _submit(_referral);
    }

    /**
     * @notice A payable function for execution layer rewards. Can be called only by `ExecutionLayerRewardsVault`
     * @dev We need a dedicated function because funds received by the default payable function
     * are treated as a user deposit
     */
    function receiveELRewards() external payable {
        require(msg.sender == getLolikLocator().elRewardsVault());

        TOTAL_EL_REWARDS_COLLECTED_POSITION.setStorageUint256(getTotalELRewardsCollected().add(msg.value));

        emit ELRewardsReceived(msg.value);
    }

    /**
    * @notice A payable function for withdrawals acquisition. Can be called only by `WithdrawalVault`
    * @dev We need a dedicated function because funds received by the default payable function
    * are treated as a user deposit
    */
    function receiveWithdrawals() external payable {
        require(msg.sender == getLolikLocator().withdrawalVault());

        emit WithdrawalsReceived(msg.value);
    }

    /**
     * @notice Stop pool routine operations
     */
    function stop() external {
        _auth(PAUSE_ROLE);

        _stop();
        _pauseStaking();
    }

    /**
     * @notice Resume pool routine operations
     * @dev Staking is resumed after this call using the previously set limits (if any)
     */
    function resume() external {
        _auth(RESUME_ROLE);

        _resume();
        _resumeStaking();
    }

    /**
     * The structure is used to aggregate the `handleOracleReport` provided data.
     * @dev Using the in-memory structure addresses `stack too deep` issues.
     */
    struct OracleReportedData {
        // Oracle timings
        uint256 reportTimestamp;
        uint256 timeElapsed;
        // CL values
        uint256 clValidators;
        uint256 postCLBalance;
        // EL values
        uint256 withdrawalVaultBalance;
        uint256 elRewardsVaultBalance;
        uint256 sharesRequestedToBurn;
        // Decision about withdrawals processing
        uint256[] withdrawalFinalizationBatches;
        uint256 simulatedShareRate;
    }

    /**
     * The structure is used to preload the contract using `getLolikLocator()` via single call
     */
    struct OracleReportContracts {
        address accountingOracle;
        address elRewardsVault;
        address oracleReportSanityChecker;
        address burner;
        address withdrawalQueue;
        address withdrawalVault;
        address postTokenRebaseReceiver;
    }

    /**
    * @notice Updates accounting stats, collects EL rewards and distributes collected rewards
    *         if beacon balance increased, performs withdrawal requests finalization
    * @dev periodically called by the AccountingOracle contract
    *
    * @param _reportTimestamp the moment of the oracle report calculation
    * @param _timeElapsed seconds elapsed since the previous report calculation
    * @param _clValidators number of Lolik validators on Consensus Layer
    * @param _clBalance sum of all Lolik validators' balances on Consensus Layer
    * @param _withdrawalVaultBalance withdrawal vault balance on Execution Layer at `_reportTimestamp`
    * @param _elRewardsVaultBalance elRewards vault balance on Execution Layer at `_reportTimestamp`
    * @param _sharesRequestedToBurn shares requested to burn through Burner at `_reportTimestamp`
    * @param _withdrawalFinalizationBatches the ascendingly-sorted array of withdrawal request IDs obtained by calling
    * WithdrawalQueue.calculateFinalizationBatches. Empty array means that no withdrawal requests should be finalized
    * @param _simulatedShareRate share rate that was simulated by oracle when the report data created (1e27 precision)
    *
    * NB: `_simulatedShareRate` should be calculated off-chain by calling the method with `eth_call` JSON-RPC API
    * while passing empty `_withdrawalFinalizationBatches` and `_simulatedShareRate` == 0, plugging the returned values
    * to the following formula: `_simulatedShareRate = (postTotalPooledFtn * 1e27) / postTotalShares`
    *
    * @return postRebaseAmounts[0]: `postTotalPooledFtn` amount of ftn in the protocol after report
    * @return postRebaseAmounts[1]: `postTotalShares` amount of shares in the protocol after report
    * @return postRebaseAmounts[2]: `withdrawals` withdrawn from the withdrawals vault
    * @return postRebaseAmounts[3]: `elRewards` withdrawn from the execution layer rewards vault
    */
    function handleOracleReport(
        // Oracle timings
        uint256 _reportTimestamp,
        uint256 _timeElapsed,
        // CL values
        uint256 _clValidators,
        uint256 _clBalance,
        // EL values
        uint256 _withdrawalVaultBalance,
        uint256 _elRewardsVaultBalance,
        uint256 _sharesRequestedToBurn,
        // Decision about withdrawals processing
        uint256[] _withdrawalFinalizationBatches,
        uint256 _simulatedShareRate
    ) external returns (uint256[4] postRebaseAmounts) {
        _whenNotStopped();

        return _handleOracleReport(
            OracleReportedData(
                _reportTimestamp,
                _timeElapsed,
                _clValidators,
                _clBalance,
                _withdrawalVaultBalance,
                _elRewardsVaultBalance,
                _sharesRequestedToBurn,
                _withdrawalFinalizationBatches,
                _simulatedShareRate
            )
        );
    }

    /**
     * @notice Unsafely change deposited validators
     *
     * The method unsafely changes deposited validator counter.
     * Can be required when onboarding external validators to Lolik
     * (i.e., had deposited before and rotated their type-0x00 withdrawal credentials to Lolik)
     *
     * @param _newDepositedValidators new value
     */
    function unsafeChangeDepositedValidators(uint256 _newDepositedValidators) external {
        _auth(UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE);

        DEPOSITED_VALIDATORS_POSITION.setStorageUint256(_newDepositedValidators);

        emit DepositedValidatorsChanged(_newDepositedValidators);
    }

    /**
     * @notice Overrides default AragonApp behaviour to disallow recovery.
     */
    function transferToVault(address /* _token */) external {
        revert("NOT_SUPPORTED");
    }

    /**
    * @notice Get the amount of Ftn temporary buffered on this contract balance
    * @dev Buffered balance is kept on the contract from the moment the funds are received from user
    * until the moment they are actually sent to the official Deposit contract.
    * @return amount of buffered funds in wei
    */
    function getBufferedFtn() external view returns (uint256) {
        return _getBufferedFtn();
    }

    /**
     * @notice Get total amount of execution layer rewards collected to Lolik contract
     * @dev Ftn got through LolikExecutionLayerRewardsVault is kept on this contract's balance the same way
     * as other buffered Ftn is kept (until it gets deposited)
     * @return amount of funds received as execution layer rewards in wei
     */
    function getTotalELRewardsCollected() public view returns (uint256) {
        return TOTAL_EL_REWARDS_COLLECTED_POSITION.getStorageUint256();
    }

    /**
     * @notice Gets authorized oracle address
     * @return address of oracle contract
     */
    function getLolikLocator() public view returns (ILolikLocator) {
        return ILolikLocator(LOLIK_LOCATOR_POSITION.getStorageAddress());
    }

    /**
    * @notice Returns the key values related to Consensus Layer side of the contract. It historically contains beacon
    * @return depositedValidators - number of deposited validators from Lolik contract side
    * @return beaconValidators - number of Lolik validators visible on Consensus Layer, reported by oracle
    * @return beaconBalance - total amount of ftn on the Consensus Layer side (sum of all the balances of Lolik validators)
    *
    * @dev `beacon` in naming still here for historical reasons
    */
    function getBeaconStat() external view returns (uint256 depositedValidators, uint256 beaconValidators, uint256 beaconBalance) {
        depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
        beaconValidators = CL_VALIDATORS_POSITION.getStorageUint256();
        beaconBalance = CL_BALANCE_POSITION.getStorageUint256();
    }

    /**
     * @dev Check that Lolik allows depositing buffered ftn to the consensus layer
     * Depends on the bunker state and protocol's pause state
     */
    function canDeposit() public view returns (bool) {
        return !_withdrawalQueue().isBunkerModeActive() && !isStopped();
    }

    /**
     * @dev Returns depositable ftn amount.
     * Takes into account unfinalized stFTN required by WithdrawalQueue
     */
    function getDepositableFtn() public view returns (uint256) {
        uint256 bufferedFtn = _getBufferedFtn();
        uint256 withdrawalReserve = _withdrawalQueue().unfinalizedStFTN();
        return bufferedFtn > withdrawalReserve ? bufferedFtn - withdrawalReserve : 0;
    }

    /**
     * @dev Invokes a deposit call to the Staking Router contract and updates buffered counters
     * @param _maxDepositsCount max deposits count
     * @param _stakingModuleId id of the staking module to be deposited
     * @param _depositCalldata module calldata
     */
    function deposit(uint256 _maxDepositsCount, uint256 _stakingModuleId, bytes _depositCalldata) external {
        ILolikLocator locator = getLolikLocator();

        require(msg.sender == locator.depositSecurityModule(), "APP_AUTH_DSM_FAILED");
        require(canDeposit(), "CAN_NOT_DEPOSIT");

        IStakingRouter stakingRouter = _stakingRouter();
        uint256 depositsCount = Math256.min(
            _maxDepositsCount,
            stakingRouter.getStakingModuleMaxDepositsCount(_stakingModuleId, getDepositableFtn())
        );

        uint256 depositsValue;
        if (depositsCount > 0) { // 1 > 0
            depositsValue = depositsCount.mul(DEPOSIT_SIZE);
            /// @dev firstly update the local state of the contract to prevent a reentrancy attack,
            ///     even if the StakingRouter is a trusted contract.
            BUFFERED_FTN_POSITION.setStorageUint256(_getBufferedFtn().sub(depositsValue));
            emit Unbuffered(depositsValue);

            uint256 newDepositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256().add(depositsCount);
            DEPOSITED_VALIDATORS_POSITION.setStorageUint256(newDepositedValidators);
            emit DepositedValidatorsChanged(newDepositedValidators);
        }

        /// @dev transfer ftn to StakingRouter and make a deposit at the same time. All the ftn
        ///     sent to StakingRouter is counted as deposited. If StakingRouter can't deposit all
        ///     passed ftn it MUST revert the whole transaction (never happens in normal circumstances)
        stakingRouter.deposit.value(depositsValue)(depositsCount, _stakingModuleId, _depositCalldata);
    }

    /// DEPRECATED PUBLIC METHODS

    /**
     * @notice Returns current withdrawal credentials of deposited validators
     * @dev DEPRECATED: use StakingRouter.getWithdrawalCredentials() instead
     */
    function getWithdrawalCredentials() external view returns (bytes32) {
        return _stakingRouter().getWithdrawalCredentials();
    }

    /**
     * @notice Returns legacy oracle
     * @dev DEPRECATED: the `AccountingOracle` superseded the old one
     */
    function getOracle() external view returns (address) {
        return getLolikLocator().legacyOracle();
    }

    /**
     * @notice Returns the treasury address
     * @dev DEPRECATED: use LolikLocator.treasury()
     */
    function getTreasury() external view returns (address) {
        return _treasury();
    }

    /**
     * @notice Returns current staking rewards fee rate
     * @dev DEPRECATED: Now fees information is stored in StakingRouter and
     * with higher precision. Use StakingRouter.getStakingFeeAggregateDistribution() instead.
     * @return totalFee total rewards fee in 1e4 precision (10000 is 100%). The value might be
     * inaccurate because the actual value is truncated here to 1e4 precision.
     */
    function getFee() external view returns (uint16 totalFee) {
        totalFee = _stakingRouter().getTotalFeeE4Precision();
    }

    /**
     * @notice Returns current fee distribution, values relative to the total fee (getFee())
     * @dev DEPRECATED: Now fees information is stored in StakingRouter and
     * with higher precision. Use StakingRouter.getStakingFeeAggregateDistribution() instead.
     * @return treasuryFeeBasisPoints return treasury fee in TOTAL_BASIS_POINTS (10000 is 100% fee) precision
     * @return insuranceFeeBasisPoints always returns 0 because the capability to send fees to
     * insurance from Lolik contract is removed.
     * @return operatorsFeeBasisPoints return total fee for all operators of all staking modules in
     * TOTAL_BASIS_POINTS (10000 is 100% fee) precision.
     * Previously returned total fee of all node operators of NodeOperatorsRegistry (Curated staking module now)
     * The value might be inaccurate because the actual value is truncated here to 1e4 precision.
     */
    function getFeeDistribution()
        external view
        returns (
            uint16 treasuryFeeBasisPoints,
            uint16 insuranceFeeBasisPoints,
            uint16 operatorsFeeBasisPoints
        )
    {
        IStakingRouter stakingRouter = _stakingRouter();
        uint256 totalBasisPoints = stakingRouter.TOTAL_BASIS_POINTS();
        uint256 totalFee = stakingRouter.getTotalFeeE4Precision();
        (uint256 treasuryFeeBasisPointsAbs, uint256 operatorsFeeBasisPointsAbs) = stakingRouter
            .getStakingFeeAggregateDistributionE4Precision();

        insuranceFeeBasisPoints = 0;  // explicitly set to zero
        treasuryFeeBasisPoints = uint16((treasuryFeeBasisPointsAbs * totalBasisPoints) / totalFee);
        operatorsFeeBasisPoints = uint16((operatorsFeeBasisPointsAbs * totalBasisPoints) / totalFee);
    }

    /*
     * @dev updates Consensus Layer state snapshot according to the current report
     *
     * NB: conventions and assumptions
     *
     * `depositedValidators` are total amount of the **ever** deposited Lolik validators
     * `_postClValidators` are total amount of the **ever** appeared on the CL side Lolik validators
     *
     * i.e., exited Lolik validators persist in the state, just with a different status
     */
    function _processClStateUpdate(
        uint256 _reportTimestamp,
        uint256 _preClValidators,
        uint256 _postClValidators,
        uint256 _postClBalance
    ) internal returns (uint256 preCLBalance) {
        uint256 depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
        require(_postClValidators <= depositedValidators, "REPORTED_MORE_DEPOSITED");
        require(_postClValidators >= _preClValidators, "REPORTED_LESS_VALIDATORS");

        if (_postClValidators > _preClValidators) {
            CL_VALIDATORS_POSITION.setStorageUint256(_postClValidators);
        }

        uint256 appearedValidators = _postClValidators - _preClValidators;
        preCLBalance = CL_BALANCE_POSITION.getStorageUint256();
        // Take into account the balance of the newly appeared validators
        preCLBalance = preCLBalance.add(appearedValidators.mul(DEPOSIT_SIZE));

        // Save the current CL balance and validators to
        // calculate rewards on the next push
        CL_BALANCE_POSITION.setStorageUint256(_postClBalance);

        emit CLValidatorsUpdated(_reportTimestamp, _preClValidators, _postClValidators);
    }

    /**
     * @dev collect FTN from ELRewardsVault and WithdrawalVault, then send to WithdrawalQueue
     */
    function _collectRewardsAndProcessWithdrawals(
        OracleReportContracts memory _contracts,
        uint256 _withdrawalsToWithdraw,
        uint256 _elRewardsToWithdraw,
        uint256[] _withdrawalFinalizationBatches,
        uint256 _simulatedShareRate,
        uint256 _ftnToLockOnWithdrawalQueue
    ) internal {
        // withdraw execution layer rewards and put them to the buffer
        if (_elRewardsToWithdraw > 0) {
            ILolikExecutionLayerRewardsVault(_contracts.elRewardsVault).withdrawRewards(_elRewardsToWithdraw);
        }

        // withdraw withdrawals and put them to the buffer
        if (_withdrawalsToWithdraw > 0) {
            IWithdrawalVault(_contracts.withdrawalVault).withdrawWithdrawals(_withdrawalsToWithdraw);
        }

        // finalize withdrawals (send ftn, assign shares for burning)
        if (_ftnToLockOnWithdrawalQueue > 0) {
            IWithdrawalQueue withdrawalQueue = IWithdrawalQueue(_contracts.withdrawalQueue);
            withdrawalQueue.finalize.value(_ftnToLockOnWithdrawalQueue)(
                _withdrawalFinalizationBatches[_withdrawalFinalizationBatches.length - 1],
                _simulatedShareRate
            );
        }

        uint256 postBufferedFtn = _getBufferedFtn()
            .add(_elRewardsToWithdraw) // Collected from ELVault
            .add(_withdrawalsToWithdraw) // Collected from WithdrawalVault
            .sub(_ftnToLockOnWithdrawalQueue); // Sent to WithdrawalQueue

        _setBufferedFtn(postBufferedFtn);
    }

    /**
     * @dev return amount to lock on withdrawal queue and shares to burn
     * depending on the finalization batch parameters
     */
    function _calculateWithdrawals(
        OracleReportContracts memory _contracts,
        OracleReportedData memory _reportedData
    ) internal view returns (
        uint256 ftnToLock, uint256 sharesToBurn
    ) {
        IWithdrawalQueue withdrawalQueue = IWithdrawalQueue(_contracts.withdrawalQueue);

        if (!withdrawalQueue.isPaused()) {
            IOracleReportSanityChecker(_contracts.oracleReportSanityChecker).checkWithdrawalQueueOracleReport(
                _reportedData.withdrawalFinalizationBatches[_reportedData.withdrawalFinalizationBatches.length - 1],
                _reportedData.reportTimestamp
            );

            (ftnToLock, sharesToBurn) = withdrawalQueue.prefinalize(
                _reportedData.withdrawalFinalizationBatches,
                _reportedData.simulatedShareRate
            );
        }
    }

    /**
     * @dev calculate the amount of rewards and distribute it
     */
    function _processRewards(
        OracleReportContext memory _reportContext,
        uint256 _postCLBalance,
        uint256 _withdrawnWithdrawals,
        uint256 _withdrawnElRewards
    ) internal returns (uint256 sharesMintedAsFees) {
        uint256 postCLTotalBalance = _postCLBalance.add(_withdrawnWithdrawals);
        // Don’t mint/distribute any protocol fee on the non-profitable Lolik oracle report
        // (when consensus layer balance delta is zero or negative).
        // See LIP-12 for details:
        // https://research.lolik.fi/t/lip-12-on-chain-part-of-the-rewards-distribution-after-the-merge/1625
        if (postCLTotalBalance > _reportContext.preCLBalance) {
            uint256 consensusLayerRewards = postCLTotalBalance - _reportContext.preCLBalance;

            sharesMintedAsFees = _distributeFee(
                _reportContext.preTotalPooledFtn,
                _reportContext.preTotalShares,
                consensusLayerRewards.add(_withdrawnElRewards)
            );
        }
    }

    /**
     * @dev Process user deposit, mints liquid tokens and increase the pool buffer
     * @param _referral address of referral.
     * @return amount of StFTN shares generated
     */
    function _submit(address _referral) internal returns (uint256) {
        require(msg.value != 0, "ZERO_DEPOSIT");

        StakeLimitState.Data memory stakeLimitData = STAKING_STATE_POSITION.getStorageStakeLimitStruct();
        // There is an invariant that protocol pause also implies staking pause.
        // Thus, no need to check protocol pause explicitly.
        require(!stakeLimitData.isStakingPaused(), "STAKING_PAUSED");

        if (stakeLimitData.isStakingLimitSet()) {
            uint256 currentStakeLimit = stakeLimitData.calculateCurrentStakeLimit();

            require(msg.value <= currentStakeLimit, "STAKE_LIMIT");

            STAKING_STATE_POSITION.setStorageStakeLimitStruct(stakeLimitData.updatePrevStakeLimit(currentStakeLimit - msg.value));
        }

        uint256 sharesAmount = getSharesByPooledFtn(msg.value);

        _mintShares(msg.sender, sharesAmount);

        _setBufferedFtn(_getBufferedFtn().add(msg.value));
        emit Submitted(msg.sender, msg.value, _referral);

        _emitTransferAfterMintingShares(msg.sender, sharesAmount);
        return sharesAmount;
    }

    /**
     * @dev Staking router rewards distribution.
     *
     * Corresponds to the return value of `IStakingRouter.newTotalPooledFtnerForRewards()`
     * Prevents `stack too deep` issue.
     */
    struct StakingRewardsDistribution {
        address[] recipients;
        uint256[] moduleIds;
        uint96[] modulesFees;
        uint96 totalFee;
        uint256 precisionPoints;
    }

    /**
     * @dev Get staking rewards distribution from staking router.
     */
    function _getStakingRewardsDistribution() internal view returns (
        StakingRewardsDistribution memory ret,
        IStakingRouter router
    ) {
        router = _stakingRouter();

        (
            ret.recipients,
            ret.moduleIds,
            ret.modulesFees,
            ret.totalFee,
            ret.precisionPoints
        ) = router.getStakingRewardsDistribution();

        require(ret.recipients.length == ret.modulesFees.length, "WRONG_RECIPIENTS_INPUT");
        require(ret.moduleIds.length == ret.modulesFees.length, "WRONG_MODULE_IDS_INPUT");
    }

    /**
     * @dev Distributes fee portion of the rewards by minting and distributing corresponding amount of liquid tokens.
     * @param _preTotalPooledFtn Total supply before report-induced changes applied
     * @param _preTotalShares Total shares before report-induced changes applied
     * @param _totalRewards Total rewards accrued both on the Execution Layer and the Consensus Layer sides in wei.
     */
    function _distributeFee(
        uint256 _preTotalPooledFtn,
        uint256 _preTotalShares,
        uint256 _totalRewards
    ) internal returns (uint256 sharesMintedAsFees) {
        // We need to take a defined percentage of the reported reward as a fee, and we do
        // this by minting new token shares and assigning them to the fee recipients (see
        // StFTN docs for the explanation of the shares mechanics). The staking rewards fee
        // is defined in basis points (1 basis point is equal to 0.01%, 10000 (TOTAL_BASIS_POINTS) is 100%).
        //
        // Since we are increasing totalPooledFtn by _totalRewards (totalPooledFtnWithRewards),
        // the combined cost of all holders' shares has became _totalRewards StFTN tokens more,
        // effectively splitting the reward between each token holder proportionally to their token share.
        //
        // Now we want to mint new shares to the fee recipient, so that the total cost of the
        // newly-minted shares exactly corresponds to the fee taken:
        //
        // totalPooledFtnWithRewards = _preTotalPooledFtn + _totalRewards
        // shares2mint * newShareCost = (_totalRewards * totalFee) / PRECISION_POINTS
        // newShareCost = totalPooledFtnWithRewards / (_preTotalShares + shares2mint)
        //
        // which follows to:
        //
        //                        _totalRewards * totalFee * _preTotalShares
        // shares2mint = --------------------------------------------------------------
        //                 (totalPooledFtnWithRewards * PRECISION_POINTS) - (_totalRewards * totalFee)
        //
        // The effect is that the given percentage of the reward goes to the fee recipient, and
        // the rest of the reward is distributed between token holders proportionally to their
        // token shares.

        (
            StakingRewardsDistribution memory rewardsDistribution,
            IStakingRouter router
        ) = _getStakingRewardsDistribution();

        if (rewardsDistribution.totalFee > 0) {
            uint256 totalPooledFtnWithRewards = _preTotalPooledFtn.add(_totalRewards);

            sharesMintedAsFees =
                _totalRewards.mul(rewardsDistribution.totalFee).mul(_preTotalShares).div(
                    totalPooledFtnWithRewards.mul(
                        rewardsDistribution.precisionPoints
                    ).sub(_totalRewards.mul(rewardsDistribution.totalFee))
                );

            _mintShares(address(this), sharesMintedAsFees);

            (uint256[] memory moduleRewards, uint256 totalModuleRewards) =
                _transferModuleRewards(
                    rewardsDistribution.recipients,
                    rewardsDistribution.modulesFees,
                    rewardsDistribution.totalFee,
                    sharesMintedAsFees
                );

            _transferTreasuryRewards(sharesMintedAsFees.sub(totalModuleRewards));

            router.reportRewardsMinted(
                rewardsDistribution.moduleIds,
                moduleRewards
            );
        }
    }

    function _transferModuleRewards(
        address[] memory recipients,
        uint96[] memory modulesFees,
        uint256 totalFee,
        uint256 totalRewards
    ) internal returns (uint256[] memory moduleRewards, uint256 totalModuleRewards) {
        moduleRewards = new uint256[](recipients.length);

        for (uint256 i; i < recipients.length; ++i) {
            if (modulesFees[i] > 0) {
                uint256 iModuleRewards = totalRewards.mul(modulesFees[i]).div(totalFee);
                moduleRewards[i] = iModuleRewards;
                _transferShares(address(this), recipients[i], iModuleRewards);
                _emitTransferAfterMintingShares(recipients[i], iModuleRewards);
                totalModuleRewards = totalModuleRewards.add(iModuleRewards);
            }
        }
    }

    function _transferTreasuryRewards(uint256 treasuryReward) internal {
        address treasury = _treasury();
        _transferShares(address(this), treasury, treasuryReward);
        _emitTransferAfterMintingShares(treasury, treasuryReward);
    }

    /**
     * @dev Gets the amount of Ftn temporary buffered on this contract balance
     */
    function _getBufferedFtn() internal view returns (uint256) {
        return BUFFERED_FTN_POSITION.getStorageUint256();
    }

    function _setBufferedFtn(uint256 _newBufferedFtn) internal {
        BUFFERED_FTN_POSITION.setStorageUint256(_newBufferedFtn);
    }

    /// @dev Calculates and returns the total base balance (multiple of 8192) of validators in transient state,
    ///     i.e. submitted to the official Deposit contract but not yet visible in the CL state.
    /// @return transient balance in wei (1e-18 Ftn)
    function _getTransientBalance() internal view returns (uint256) {
        uint256 depositedValidators = DEPOSITED_VALIDATORS_POSITION.getStorageUint256();
        uint256 clValidators = CL_VALIDATORS_POSITION.getStorageUint256();
        // clValidators can never be less than deposited ones.
        assert(depositedValidators >= clValidators);
        return (depositedValidators - clValidators).mul(DEPOSIT_SIZE);
    }

    /**
     * @dev Gets the total amount of Ftn controlled by the system
     * @return total balance in wei
     */
    function _getTotalPooledFtn() internal view returns (uint256) {
        return _getBufferedFtn()
            .add(CL_BALANCE_POSITION.getStorageUint256())
            .add(_getTransientBalance());
    }

    function _pauseStaking() internal {
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakeLimitPauseState(true)
        );

        emit StakingPaused();
    }

    function _resumeStaking() internal {
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakeLimitPauseState(false)
        );

        emit StakingResumed();
    }

    function _getCurrentStakeLimit(StakeLimitState.Data memory _stakeLimitData) internal view returns (uint256) {
        if (_stakeLimitData.isStakingPaused()) {
            return 0;
        }
        if (!_stakeLimitData.isStakingLimitSet()) {
            return uint256(-1);
        }

        return _stakeLimitData.calculateCurrentStakeLimit();
    }

    /**
     * @dev Size-efficient analog of the `auth(_role)` modifier
     * @param _role Permission name
     */
    function _auth(bytes32 _role) internal view {
        require(canPerform(msg.sender, _role, new uint256[](0)), "APP_AUTH_FAILED");
    }

    /**
     * @dev Intermediate data structure for `_handleOracleReport`
     * Helps to overcome `stack too deep` issue.
     */
    struct OracleReportContext {
        uint256 preCLValidators;
        uint256 preCLBalance;
        uint256 preTotalPooledFtn;
        uint256 preTotalShares;
        uint256 ftnToLockOnWithdrawalQueue;
        uint256 sharesToBurnFromWithdrawalQueue;
        uint256 simulatedSharesToBurn;
        uint256 sharesToBurn;
        uint256 sharesMintedAsFees;
    }

    /**
     * @dev Handle oracle report method operating with the data-packed structs
     * Using structs helps to overcome 'stack too deep' issue.
     *
     * The method updates the protocol's accounting state.
     * Key steps:
     * 1. Take a snapshot of the current (pre-) state
     * 2. Pass the report data to sanity checker (reverts if malformed)
     * 3. Pre-calculate the ftn to lock for withdrawal queue and shares to be burnt
     * 4. Pass the accounting values to sanity checker to smoothen positive token rebase
     *    (i.e., postpone the extra rewards to be applied during the next rounds)
     * 5. Invoke finalization of the withdrawal requests
     * 6. Burn excess shares within the allowed limit (can postpone some shares to be burnt later)
     * 7. Distribute protocol fee (treasury & node operators)
     * 8. Complete token rebase by informing observers (emit an event and call the external receivers if any)
     * 9. Sanity check for the provided simulated share rate
     */
    function _handleOracleReport(OracleReportedData memory _reportedData) internal returns (uint256[4]) {
        OracleReportContracts memory contracts = _loadOracleReportContracts();

        require(msg.sender == contracts.accountingOracle, "APP_AUTH_FAILED");
        require(_reportedData.reportTimestamp <= block.timestamp, "INVALID_REPORT_TIMESTAMP");

        OracleReportContext memory reportContext;

        // Step 1.
        // Take a snapshot of the current (pre-) state
        reportContext.preTotalPooledFtn = _getTotalPooledFtn();
        reportContext.preTotalShares = _getTotalShares();
        reportContext.preCLValidators = CL_VALIDATORS_POSITION.getStorageUint256();
        reportContext.preCLBalance = _processClStateUpdate(
            _reportedData.reportTimestamp,
            reportContext.preCLValidators,
            _reportedData.clValidators,
            _reportedData.postCLBalance
        );

        // Step 2.
        // Pass the report data to sanity checker (reverts if malformed)
        _checkAccountingOracleReport(contracts, _reportedData, reportContext);

        // Step 3.
        // Pre-calculate the ftn to lock for withdrawal queue and shares to be burnt
        // due to withdrawal requests to finalize
        if (_reportedData.withdrawalFinalizationBatches.length != 0) {
            (
                reportContext.ftnToLockOnWithdrawalQueue,
                reportContext.sharesToBurnFromWithdrawalQueue
            ) = _calculateWithdrawals(contracts, _reportedData);

            if (reportContext.sharesToBurnFromWithdrawalQueue > 0) {
                IBurner(contracts.burner).requestBurnShares(
                    contracts.withdrawalQueue,
                    reportContext.sharesToBurnFromWithdrawalQueue
                );
            }
        }

        // Step 4.
        // Pass the accounting values to sanity checker to smoothen positive token rebase

        uint256 withdrawals;
        uint256 elRewards;
        (
            withdrawals, elRewards, reportContext.simulatedSharesToBurn, reportContext.sharesToBurn
        ) = IOracleReportSanityChecker(contracts.oracleReportSanityChecker).smoothenTokenRebase(
            reportContext.preTotalPooledFtn,
            reportContext.preTotalShares,
            reportContext.preCLBalance,
            _reportedData.postCLBalance,
            _reportedData.withdrawalVaultBalance,
            _reportedData.elRewardsVaultBalance,
            _reportedData.sharesRequestedToBurn,
            reportContext.ftnToLockOnWithdrawalQueue,
            reportContext.sharesToBurnFromWithdrawalQueue
        );

        // Step 5.
        // Invoke finalization of the withdrawal requests (send ftn to withdrawal queue, assign shares to be burnt)
        _collectRewardsAndProcessWithdrawals(
            contracts,
            withdrawals,
            elRewards,
            _reportedData.withdrawalFinalizationBatches,
            _reportedData.simulatedShareRate,
            reportContext.ftnToLockOnWithdrawalQueue
        );

        emit FTNDistributed(
            _reportedData.reportTimestamp,
            reportContext.preCLBalance,
            _reportedData.postCLBalance,
            withdrawals,
            elRewards,
            _getBufferedFtn()
        );

        // Step 6.
        // Burn the previously requested shares
        if (reportContext.sharesToBurn > 0) {
            IBurner(contracts.burner).commitSharesToBurn(reportContext.sharesToBurn);
            _burnShares(contracts.burner, reportContext.sharesToBurn);
        }

        // Step 7.
        // Distribute protocol fee (treasury & node operators)
        reportContext.sharesMintedAsFees = _processRewards(
            reportContext,
            _reportedData.postCLBalance,
            withdrawals,
            elRewards
        );

        // Step 8.
        // Complete token rebase by informing observers (emit an event and call the external receivers if any)
        (
            uint256 postTotalShares,
            uint256 postTotalPooledFtn
        ) = _completeTokenRebase(
            _reportedData,
            reportContext,
            IPostTokenRebaseReceiver(contracts.postTokenRebaseReceiver)
        );

        // Step 9. Sanity check for the provided simulated share rate
        if (_reportedData.withdrawalFinalizationBatches.length != 0) {
            IOracleReportSanityChecker(contracts.oracleReportSanityChecker).checkSimulatedShareRate(
                postTotalPooledFtn,
                postTotalShares,
                reportContext.ftnToLockOnWithdrawalQueue,
                reportContext.sharesToBurn.sub(reportContext.simulatedSharesToBurn),
                _reportedData.simulatedShareRate
            );
        }

        return [postTotalPooledFtn, postTotalShares, withdrawals, elRewards];
    }

    /**
     * @dev Pass the provided oracle data to the sanity checker contract
     * Works with structures to overcome `stack too deep`
     */
    function _checkAccountingOracleReport(
        OracleReportContracts memory _contracts,
        OracleReportedData memory _reportedData,
        OracleReportContext memory _reportContext
    ) internal view {
        IOracleReportSanityChecker(_contracts.oracleReportSanityChecker).checkAccountingOracleReport(
            _reportedData.timeElapsed,
            _reportContext.preCLBalance,
            _reportedData.postCLBalance,
            _reportedData.withdrawalVaultBalance,
            _reportedData.elRewardsVaultBalance,
            _reportedData.sharesRequestedToBurn,
            _reportContext.preCLValidators,
            _reportedData.clValidators
        );
    }

    /**
     * @dev Notify observers about the completed token rebase.
     * Emit events and call external receivers.
     */
    function _completeTokenRebase(
        OracleReportedData memory _reportedData,
        OracleReportContext memory _reportContext,
        IPostTokenRebaseReceiver _postTokenRebaseReceiver
    ) internal returns (uint256 postTotalShares, uint256 postTotalPooledFtn) {
        postTotalShares = _getTotalShares();
        postTotalPooledFtn = _getTotalPooledFtn();

        if (_postTokenRebaseReceiver != address(0)) {
            _postTokenRebaseReceiver.handlePostTokenRebase(
                _reportedData.reportTimestamp,
                _reportedData.timeElapsed,
                _reportContext.preTotalShares,
                _reportContext.preTotalPooledFtn,
                postTotalShares,
                postTotalPooledFtn,
                _reportContext.sharesMintedAsFees
            );
        }

        emit TokenRebased(
            _reportedData.reportTimestamp,
            _reportedData.timeElapsed,
            _reportContext.preTotalShares,
            _reportContext.preTotalPooledFtn,
            postTotalShares,
            postTotalPooledFtn,
            _reportContext.sharesMintedAsFees
        );
    }

    /**
     * @dev Load the contracts used for `handleOracleReport` internally.
     */
    function _loadOracleReportContracts() internal view returns (OracleReportContracts memory ret) {
        (
            ret.accountingOracle,
            ret.elRewardsVault,
            ret.oracleReportSanityChecker,
            ret.burner,
            ret.withdrawalQueue,
            ret.withdrawalVault,
            ret.postTokenRebaseReceiver
        ) = getLolikLocator().oracleReportComponentsForLolik();
    }

    function _stakingRouter() internal view returns (IStakingRouter) {
        return IStakingRouter(getLolikLocator().stakingRouter());
    }

    function _withdrawalQueue() internal view returns (IWithdrawalQueue) {
        return IWithdrawalQueue(getLolikLocator().withdrawalQueue());
    }

    function _treasury() internal view returns (address) {
        return getLolikLocator().treasury();
    }

    /**
     * @notice Mints shares on behalf of 0xdead address,
     * the shares amount is equal to the contract's balance.     *
     *
     * Allows to get rid of zero checks for `totalShares` and `totalPooledFtn`
     * and overcome corner cases.
     *
     * NB: reverts if the current contract's balance is zero.
     *
     * @dev must be invoked before using the token
     */
    function _bootstrapInitialHolder() internal {
        uint256 balance = address(this).balance;
        assert(balance != 0);

        if (_getTotalShares() == 0) {
            // if protocol is empty bootstrap it with the contract's balance
            // address(0xdead) is a holder for initial shares
            _setBufferedFtn(balance);
            // emitting `Submitted` before Transfer events to preserver events order in tx
            emit Submitted(INITIAL_TOKEN_HOLDER, balance, 0);
            _mintInitialShares(balance);
        }
    }
}
        

@aragon/os/contracts/acl/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));
    }
}
          

contracts/0.4.24/test_helpers/MockLegacyOracle.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.4.24;

import "../oracle/LegacyOracle.sol";

interface ILegacyOracle {
    function getBeaconSpec() external view returns (
        uint64 epochsPerFrame,
        uint64 slotsPerEpoch,
        uint64 secondsPerSlot,
        uint64 genesisTime
    );

    function getLastCompletedEpochId() external view returns (uint256);
}

interface ITimeProvider {
    function getTime() external view returns (uint256);
}


contract MockLegacyOracle is ILegacyOracle, LegacyOracle {

    struct HandleConsensusLayerReportCallData {
        uint256 totalCalls;
        uint256 refSlot;
        uint256 clBalance;
        uint256 clValidators;
    }

    HandleConsensusLayerReportCallData public lastCall__handleConsensusLayerReport;


    function getBeaconSpec() external view returns (
        uint64 epochsPerFrame,
        uint64 slotsPerEpoch,
        uint64 secondsPerSlot,
        uint64 genesisTime
    ) {

        ChainSpec memory spec =  _getChainSpec();
        epochsPerFrame = spec.epochsPerFrame;
        slotsPerEpoch = spec.slotsPerEpoch;
        secondsPerSlot = spec.secondsPerSlot;
        genesisTime = spec.genesisTime;
    }

    function setBeaconSpec( uint64 epochsPerFrame,
        uint64 slotsPerEpoch,
        uint64 secondsPerSlot,
        uint64 genesisTime) external {
            _setChainSpec(ChainSpec(epochsPerFrame,slotsPerEpoch,secondsPerSlot,genesisTime));
    }


     function _getTime() internal view returns (uint256) {
        address accountingOracle = ACCOUNTING_ORACLE_POSITION.getStorageAddress();
        return ITimeProvider(accountingOracle).getTime();
    }

     function getTime() external view returns (uint256) {
        return _getTime();
    }

    function handleConsensusLayerReport(uint256 refSlot, uint256 clBalance, uint256 clValidators)
        external
    {
        ++lastCall__handleConsensusLayerReport.totalCalls;
        lastCall__handleConsensusLayerReport.refSlot = refSlot;
        lastCall__handleConsensusLayerReport.clBalance = clBalance;
        lastCall__handleConsensusLayerReport.clValidators = clValidators;
    }


    function setParams(
        uint64 epochsPerFrame,
        uint64 slotsPerEpoch,
        uint64 secondsPerSlot,
        uint64 genesisTime,
        uint256 lastCompletedEpochId
    ) external {
          _setChainSpec(ChainSpec(epochsPerFrame,slotsPerEpoch,secondsPerSlot,genesisTime));
        LAST_COMPLETED_EPOCH_ID_POSITION.setStorageUint256(lastCompletedEpochId);
    }

    function setLastCompletedEpochId(uint256 lastCompletedEpochId) external {
         LAST_COMPLETED_EPOCH_ID_POSITION.setStorageUint256(lastCompletedEpochId);
    }

    function initializeAsVersion(uint256 _version) external {
        CONTRACT_VERSION_POSITION_DEPRECATED.setStorageUint256(_version);
    }

    // NB: overrides `getVersion()` to mimic the real legacy oracle
    function getVersion() external view returns (uint256) {
        return CONTRACT_VERSION_POSITION_DEPRECATED.getStorageUint256();
    }

    function setLolik(address lolik) external {
        LOLIK_POSITION.setStorageAddress(lolik);
    }

}
          

@aragon/os/contracts/common/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();
    }
}
          

@aragon/os/contracts/kernel/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);
}
          

@aragon/os/contracts/common/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);
    }
}
          

openzeppelin-solidity/contracts/token/ERC20/ERC20.sol

pragma solidity ^0.4.24;

import "./IERC20.sol";
import "../../math/SafeMath.sol";

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 * Originally based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 */
contract ERC20 is IERC20 {
  using SafeMath for uint256;

  mapping (address => uint256) private _balances;

  mapping (address => mapping (address => uint256)) private _allowed;

  uint256 private _totalSupply;

  /**
  * @dev Total number of tokens in existence
  */
  function totalSupply() public view returns (uint256) {
    return _totalSupply;
  }

  /**
  * @dev Gets the balance of the specified address.
  * @param owner The address to query the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address owner) public view returns (uint256) {
    return _balances[owner];
  }

  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param owner address The address which owns the funds.
   * @param spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(
    address owner,
    address spender
   )
    public
    view
    returns (uint256)
  {
    return _allowed[owner][spender];
  }

  /**
  * @dev Transfer token for a specified address
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function transfer(address to, uint256 value) public returns (bool) {
    _transfer(msg.sender, to, value);
    return true;
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param spender The address which will spend the funds.
   * @param value The amount of tokens to be spent.
   */
  function approve(address spender, uint256 value) public returns (bool) {
    require(spender != address(0));

    _allowed[msg.sender][spender] = value;
    emit Approval(msg.sender, spender, value);
    return true;
  }

  /**
   * @dev Transfer tokens from one address to another
   * @param from address The address which you want to send tokens from
   * @param to address The address which you want to transfer to
   * @param value uint256 the amount of tokens to be transferred
   */
  function transferFrom(
    address from,
    address to,
    uint256 value
  )
    public
    returns (bool)
  {
    require(value <= _allowed[from][msg.sender]);

    _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
    _transfer(from, to, value);
    return true;
  }

  /**
   * @dev Increase the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed_[_spender] == 0. To increment
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param spender The address which will spend the funds.
   * @param addedValue The amount of tokens to increase the allowance by.
   */
  function increaseAllowance(
    address spender,
    uint256 addedValue
  )
    public
    returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
      _allowed[msg.sender][spender].add(addedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
   * @dev Decrease the amount of tokens that an owner allowed to a spender.
   * approve should be called when allowed_[_spender] == 0. To decrement
   * allowed value is better to use this function to avoid 2 calls (and wait until
   * the first transaction is mined)
   * From MonolithDAO Token.sol
   * @param spender The address which will spend the funds.
   * @param subtractedValue The amount of tokens to decrease the allowance by.
   */
  function decreaseAllowance(
    address spender,
    uint256 subtractedValue
  )
    public
    returns (bool)
  {
    require(spender != address(0));

    _allowed[msg.sender][spender] = (
      _allowed[msg.sender][spender].sub(subtractedValue));
    emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
    return true;
  }

  /**
  * @dev Transfer token for a specified addresses
  * @param from The address to transfer from.
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function _transfer(address from, address to, uint256 value) internal {
    require(value <= _balances[from]);
    require(to != address(0));

    _balances[from] = _balances[from].sub(value);
    _balances[to] = _balances[to].add(value);
    emit Transfer(from, to, value);
  }

  /**
   * @dev Internal function that mints an amount of the token and assigns it to
   * an account. This encapsulates the modification of balances such that the
   * proper events are emitted.
   * @param account The account that will receive the created tokens.
   * @param value The amount that will be created.
   */
  function _mint(address account, uint256 value) internal {
    require(account != 0);
    _totalSupply = _totalSupply.add(value);
    _balances[account] = _balances[account].add(value);
    emit Transfer(address(0), account, value);
  }

  /**
   * @dev Internal function that burns an amount of the token of a given
   * account.
   * @param account The account whose tokens will be burnt.
   * @param value The amount that will be burnt.
   */
  function _burn(address account, uint256 value) internal {
    require(account != 0);
    require(value <= _balances[account]);

    _totalSupply = _totalSupply.sub(value);
    _balances[account] = _balances[account].sub(value);
    emit Transfer(account, address(0), value);
  }

  /**
   * @dev Internal function that burns an amount of the token of a given
   * account, deducting from the sender's allowance for said account. Uses the
   * internal burn function.
   * @param account The account whose tokens will be burnt.
   * @param value The amount that will be burnt.
   */
  function _burnFrom(address account, uint256 value) internal {
    require(value <= _allowed[account][msg.sender]);

    // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
    // this function needs to emit an event with the updated approval.
    _allowed[account][msg.sender] = _allowed[account][msg.sender].sub(
      value);
    _burn(account, value);
  }
}
          

@aragon/os/contracts/common/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);
}
          

contracts/0.4.24/test_helpers/VaultMock.sol

// SPDX-FileCopyrightText: 2020 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;


/**
  * @dev This is a mock. Don't use in production.
  */
contract VaultMock {
    function() public payable {}
}
          

@aragon/os/contracts/evmscript/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);
    }
}
          

openzeppelin-solidity/contracts/utils/Address.sol

pragma solidity ^0.4.24;

/**
 * Utility library of inline functions on addresses
 */
library Address {

  /**
   * Returns whether the target address is a contract
   * @dev This function will return false if invoked during the constructor of a contract,
   * as the code is not actually created until after the constructor finishes.
   * @param account address of the account to check
   * @return whether the target address is a contract
   */
  function isContract(address account) internal view returns (bool) {
    uint256 size;
    // XXX Currently there is no better way to check if there is a contract in an address
    // than to check the size of the code at that address.
    // See https://ethereum.stackexchange.com/a/14016/36603
    // for more details about how this works.
    // TODO Check this again before the Serenity release, because all addresses will be
    // contracts then.
    // solium-disable-next-line security/no-inline-assembly
    assembly { size := extcodesize(account) }
    return size > 0;
  }

}
          

contracts/0.4.24/nos/NodeOperatorsRegistry.sol

// SPDX-FileCopyrightText: 2023 Lolik <[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 {ILolikLocator} from "../../common/interfaces/ILolikLocator.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 IStFTN {
    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("lolik.NodeOperatorsRegistry.signingKeysMappingName");
    bytes32 internal constant SIGNING_KEYS_MAPPING_NAME = 0xeb2b7ad4d8ce5610cfb46470f03b14c197c2b751077c70209c5d0139f7c79ee9;

    // bytes32 internal constant LOLIK_LOCATOR_POSITION = keccak256("lolik.NodeOperatorsRegistry.LolikLocator");
    bytes32 internal constant LOLIK_LOCATOR_POSITION = 0xfb2059fd4b64256b64068a0f57046c6d40b9f0e592ba8bcfdf5b941910d03537;

    /// @dev Total number of operators
    // bytes32 internal constant TOTAL_OPERATORS_COUNT_POSITION = keccak256("lolik.NodeOperatorsRegistry.totalOperatorsCount");
    bytes32 internal constant TOTAL_OPERATORS_COUNT_POSITION =
        0xe2a589ae0816b289a9d29b7c085f8eba4b5525accca9fa8ff4dba3f5a41287e8;

    /// @dev Cached number of active operators
    // bytes32 internal constant ACTIVE_OPERATORS_COUNT_POSITION = keccak256("lolik.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("lolik.NodeOperatorsRegistry.keysOpIndex");
    bytes32 internal constant KEYS_OP_INDEX_POSITION = 0xcd91478ac3f2620f0776eacb9c24123a214bcb23c32ae7d28278aa846c8c380e;

    /// @dev module type
    // bytes32 internal constant TYPE_POSITION = keccak256("lolik.NodeOperatorsRegistry.type");
    bytes32 internal constant TYPE_POSITION = 0xbacf4236659a602d72c631ba0b0d67ec320aaf523f3ae3590d7faee4f42351d0;

    // bytes32 internal constant STUCK_PENALTY_DELAY_POSITION = keccak256("lolik.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 Bahamut address on Execution Layer which receives stFTN 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/lolik-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);
        LOLIK_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
        IStFTN(getLocator().lolik()).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 Bahamut 1 address which receives stFTN 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 Bahamut 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 stFTN 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 Bahamut 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, 8192000000000) 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, 8192000000000) 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, 8192000000000) 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, 8192000000000) messages
    /// @dev DEPRECATED use addSigningKeys instead
    function addSigningKeysOperatorBH(uint256 _nodeOperatorId, uint256 _keysCount, bytes _publicKeys, bytes _signatures)
        external
    {
        _addSigningKeys(_nodeOperatorId, _keysCount, _publicKeys, _signatures);
    }

    // TODO: add contract_address batch
    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 stFTN shares distributed among node operators
    function _distributeRewards() internal returns (uint256 distributed) {
        IStFTN stFTN = IStFTN(getLocator().lolik());

        uint256 sharesToDistribute = stFTN.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]);
            }
            stFTN.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 (ILolikLocator) {
        return ILolikLocator(LOLIK_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 Lolik address is forbidden explicitly because stFTN transfers on this contract will revert
        // See onExitedAndStuckValidatorsCountsUpdated() and StFTN._transferShares() for details
        require(_rewardAddress != getLocator().lolik(), "LOLIK_REWARD_ADDRESS");
    }

    function _onlyNonZeroAddress(address _a) internal pure {
        require(_a != address(0), "ZERO_ADDRESS");
    }
}
          

@aragon/os/contracts/kernel/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;
}
          

openzeppelin-solidity/contracts/introspection/IERC165.sol

pragma solidity ^0.4.24;

/**
 * @title IERC165
 * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
 */
interface IERC165 {

  /**
   * @notice Query if a contract implements an interface
   * @param interfaceId The interface identifier, as specified in ERC-165
   * @dev Interface identification is specified in ERC-165. This function
   * uses less than 30,000 gas.
   */
  function supportsInterface(bytes4 interfaceId)
    external
    view
    returns (bool);
}
          

contracts/0.4.24/test_helpers/StFTNMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../StFTN.sol";

/**
 * @dev Only for testing purposes!
 * StFTN mock version of mintable/burnable/stoppable token.
 */
contract StFTNMock is StFTN {
    uint256 private totalPooledFtn;

    constructor() public payable{
        _resume();
        // _bootstrapInitialHolder
        uint256 balance = address(this).balance;
        assert(balance != 0);

        // address(0xdead) is a holder for initial shares
        setTotalPooledEther(balance);
        _mintInitialShares(balance);
    }

    function _getTotalPooledFtn() internal view returns (uint256) {
        return totalPooledFtn;
    }

    function stop() external {
        _stop();
    }

    function resume() external {
        _resume();
    }

    function setTotalPooledEther(uint256 _totalPooledEther) public {
        totalPooledFtn = _totalPooledEther;
    }

    function mintShares(address _to, uint256 _sharesAmount) public returns (uint256 newTotalShares) {
        newTotalShares = _mintShares(_to, _sharesAmount);
        _emitTransferAfterMintingShares(_to, _sharesAmount);
    }

    function mintStftn(address _to) public payable  {
        uint256 sharesAmount = getSharesByPooledFtn(msg.value);
        mintShares(_to, sharesAmount);
        setTotalPooledEther(_getTotalPooledFtn().add(msg.value));
    }

    function burnShares(address _account, uint256 _sharesAmount) public returns (uint256 newTotalShares) {
        return _burnShares(_account, _sharesAmount);
    }
}
          

openzeppelin-solidity/contracts/token/ERC721/ERC721.sol

pragma solidity ^0.4.24;

import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../introspection/ERC165.sol";

/**
 * @title ERC721 Non-Fungible Token Standard basic implementation
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
contract ERC721 is ERC165, IERC721 {

  using SafeMath for uint256;
  using Address for address;

  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
  // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
  bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

  // Mapping from token ID to owner
  mapping (uint256 => address) private _tokenOwner;

  // Mapping from token ID to approved address
  mapping (uint256 => address) private _tokenApprovals;

  // Mapping from owner to number of owned token
  mapping (address => uint256) private _ownedTokensCount;

  // Mapping from owner to operator approvals
  mapping (address => mapping (address => bool)) private _operatorApprovals;

  bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
  /*
   * 0x80ac58cd ===
   *   bytes4(keccak256('balanceOf(address)')) ^
   *   bytes4(keccak256('ownerOf(uint256)')) ^
   *   bytes4(keccak256('approve(address,uint256)')) ^
   *   bytes4(keccak256('getApproved(uint256)')) ^
   *   bytes4(keccak256('setApprovalForAll(address,bool)')) ^
   *   bytes4(keccak256('isApprovedForAll(address,address)')) ^
   *   bytes4(keccak256('transferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
   */

  constructor()
    public
  {
    // register the supported interfaces to conform to ERC721 via ERC165
    _registerInterface(_InterfaceId_ERC721);
  }

  /**
   * @dev Gets the balance of the specified address
   * @param owner address to query the balance of
   * @return uint256 representing the amount owned by the passed address
   */
  function balanceOf(address owner) public view returns (uint256) {
    require(owner != address(0));
    return _ownedTokensCount[owner];
  }

  /**
   * @dev Gets the owner of the specified token ID
   * @param tokenId uint256 ID of the token to query the owner of
   * @return owner address currently marked as the owner of the given token ID
   */
  function ownerOf(uint256 tokenId) public view returns (address) {
    address owner = _tokenOwner[tokenId];
    require(owner != address(0));
    return owner;
  }

  /**
   * @dev Approves another address to transfer the given token ID
   * The zero address indicates there is no approved address.
   * There can only be one approved address per token at a given time.
   * Can only be called by the token owner or an approved operator.
   * @param to address to be approved for the given token ID
   * @param tokenId uint256 ID of the token to be approved
   */
  function approve(address to, uint256 tokenId) public {
    address owner = ownerOf(tokenId);
    require(to != owner);
    require(msg.sender == owner || isApprovedForAll(owner, msg.sender));

    _tokenApprovals[tokenId] = to;
    emit Approval(owner, to, tokenId);
  }

  /**
   * @dev Gets the approved address for a token ID, or zero if no address set
   * Reverts if the token ID does not exist.
   * @param tokenId uint256 ID of the token to query the approval of
   * @return address currently approved for the given token ID
   */
  function getApproved(uint256 tokenId) public view returns (address) {
    require(_exists(tokenId));
    return _tokenApprovals[tokenId];
  }

  /**
   * @dev Sets or unsets the approval of a given operator
   * An operator is allowed to transfer all tokens of the sender on their behalf
   * @param to operator address to set the approval
   * @param approved representing the status of the approval to be set
   */
  function setApprovalForAll(address to, bool approved) public {
    require(to != msg.sender);
    _operatorApprovals[msg.sender][to] = approved;
    emit ApprovalForAll(msg.sender, to, approved);
  }

  /**
   * @dev Tells whether an operator is approved by a given owner
   * @param owner owner address which you want to query the approval of
   * @param operator operator address which you want to query the approval of
   * @return bool whether the given operator is approved by the given owner
   */
  function isApprovedForAll(
    address owner,
    address operator
  )
    public
    view
    returns (bool)
  {
    return _operatorApprovals[owner][operator];
  }

  /**
   * @dev Transfers the ownership of a given token ID to another address
   * Usage of this method is discouraged, use `safeTransferFrom` whenever possible
   * Requires the msg sender to be the owner, approved, or operator
   * @param from current owner of the token
   * @param to address to receive the ownership of the given token ID
   * @param tokenId uint256 ID of the token to be transferred
  */
  function transferFrom(
    address from,
    address to,
    uint256 tokenId
  )
    public
  {
    require(_isApprovedOrOwner(msg.sender, tokenId));
    require(to != address(0));

    _clearApproval(from, tokenId);
    _removeTokenFrom(from, tokenId);
    _addTokenTo(to, tokenId);

    emit Transfer(from, to, tokenId);
  }

  /**
   * @dev Safely transfers the ownership of a given token ID to another address
   * If the target address is a contract, it must implement `onERC721Received`,
   * which is called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
   * the transfer is reverted.
   *
   * Requires the msg sender to be the owner, approved, or operator
   * @param from current owner of the token
   * @param to address to receive the ownership of the given token ID
   * @param tokenId uint256 ID of the token to be transferred
  */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId
  )
    public
  {
    // solium-disable-next-line arg-overflow
    safeTransferFrom(from, to, tokenId, "");
  }

  /**
   * @dev Safely transfers the ownership of a given token ID to another address
   * If the target address is a contract, it must implement `onERC721Received`,
   * which is called upon a safe transfer, and return the magic value
   * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
   * the transfer is reverted.
   * Requires the msg sender to be the owner, approved, or operator
   * @param from current owner of the token
   * @param to address to receive the ownership of the given token ID
   * @param tokenId uint256 ID of the token to be transferred
   * @param _data bytes data to send along with a safe transfer check
   */
  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes _data
  )
    public
  {
    transferFrom(from, to, tokenId);
    // solium-disable-next-line arg-overflow
    require(_checkOnERC721Received(from, to, tokenId, _data));
  }

  /**
   * @dev Returns whether the specified token exists
   * @param tokenId uint256 ID of the token to query the existence of
   * @return whether the token exists
   */
  function _exists(uint256 tokenId) internal view returns (bool) {
    address owner = _tokenOwner[tokenId];
    return owner != address(0);
  }

  /**
   * @dev Returns whether the given spender can transfer a given token ID
   * @param spender address of the spender to query
   * @param tokenId uint256 ID of the token to be transferred
   * @return bool whether the msg.sender is approved for the given token ID,
   *  is an operator of the owner, or is the owner of the token
   */
  function _isApprovedOrOwner(
    address spender,
    uint256 tokenId
  )
    internal
    view
    returns (bool)
  {
    address owner = ownerOf(tokenId);
    // Disable solium check because of
    // https://github.com/duaraghav8/Solium/issues/175
    // solium-disable-next-line operator-whitespace
    return (
      spender == owner ||
      getApproved(tokenId) == spender ||
      isApprovedForAll(owner, spender)
    );
  }

  /**
   * @dev Internal function to mint a new token
   * Reverts if the given token ID already exists
   * @param to The address that will own the minted token
   * @param tokenId uint256 ID of the token to be minted by the msg.sender
   */
  function _mint(address to, uint256 tokenId) internal {
    require(to != address(0));
    _addTokenTo(to, tokenId);
    emit Transfer(address(0), to, tokenId);
  }

  /**
   * @dev Internal function to burn a specific token
   * Reverts if the token does not exist
   * @param tokenId uint256 ID of the token being burned by the msg.sender
   */
  function _burn(address owner, uint256 tokenId) internal {
    _clearApproval(owner, tokenId);
    _removeTokenFrom(owner, tokenId);
    emit Transfer(owner, address(0), tokenId);
  }

  /**
   * @dev Internal function to add a token ID to the list of a given address
   * Note that this function is left internal to make ERC721Enumerable possible, but is not
   * intended to be called by custom derived contracts: in particular, it emits no Transfer event.
   * @param to address representing the new owner of the given token ID
   * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
   */
  function _addTokenTo(address to, uint256 tokenId) internal {
    require(_tokenOwner[tokenId] == address(0));
    _tokenOwner[tokenId] = to;
    _ownedTokensCount[to] = _ownedTokensCount[to].add(1);
  }

  /**
   * @dev Internal function to remove a token ID from the list of a given address
   * Note that this function is left internal to make ERC721Enumerable possible, but is not
   * intended to be called by custom derived contracts: in particular, it emits no Transfer event,
   * and doesn't clear approvals.
   * @param from address representing the previous owner of the given token ID
   * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
   */
  function _removeTokenFrom(address from, uint256 tokenId) internal {
    require(ownerOf(tokenId) == from);
    _ownedTokensCount[from] = _ownedTokensCount[from].sub(1);
    _tokenOwner[tokenId] = address(0);
  }

  /**
   * @dev Internal function to invoke `onERC721Received` on a target address
   * The call is not executed if the target address is not a contract
   * @param from address representing the previous owner of the given token ID
   * @param to target address that will receive the tokens
   * @param tokenId uint256 ID of the token to be transferred
   * @param _data bytes optional data to send along with the call
   * @return whether the call correctly returned the expected magic value
   */
  function _checkOnERC721Received(
    address from,
    address to,
    uint256 tokenId,
    bytes _data
  )
    internal
    returns (bool)
  {
    if (!to.isContract()) {
      return true;
    }
    bytes4 retval = IERC721Receiver(to).onERC721Received(
      msg.sender, from, tokenId, _data);
    return (retval == _ERC721_RECEIVED);
  }

  /**
   * @dev Private function to clear current approval of a given token ID
   * Reverts if the given address is not indeed the owner of the token
   * @param owner owner of the token
   * @param tokenId uint256 ID of the token to be transferred
   */
  function _clearApproval(address owner, uint256 tokenId) private {
    require(ownerOf(tokenId) == owner);
    if (_tokenApprovals[tokenId] != address(0)) {
      _tokenApprovals[tokenId] = address(0);
    }
  }
}
          

contracts/common/lib/SignatureUtils.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: MIT

/* See contracts/COMPILERS.md */
// solhint-disable-next-line lolik/fixed-compiler-version
pragma solidity 0.4.24;

import {ECDSA} from "./ECDSA.sol";


library SignatureUtils {
    /**
     * @dev The selector of the ERC1271's `isValidSignature(bytes32 hash, bytes signature)` function,
     * serving at the same time as the magic value that the function should return upon success.
     *
     * See https://eips.ethereum.org/EIPS/eip-1271.
     *
     * bytes4(keccak256("isValidSignature(bytes32,bytes)")
     */
    bytes4 internal constant ERC1271_IS_VALID_SIGNATURE_SELECTOR = 0x1626ba7e;

    /**
     * @dev Checks signature validity.
     *
     * If the signer address doesn't contain any code, assumes that the address is externally owned
     * and the signature is a ECDSA signature generated using its private key. Otherwise, issues a
     * static call to the signer address to check the signature validity using the ERC-1271 standard.
     */
    function isValidSignature(
        address signer,
        bytes32 msgHash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal view returns (bool) {
        if (_hasCode(signer)) {
            bytes memory sig = abi.encodePacked(r, s, v);
            // Solidity <0.5 generates a regular CALL instruction even if the function being called
            // is marked as `view`, and the only way to perform a STATICCALL is to use assembly
            bytes memory data = abi.encodeWithSelector(ERC1271_IS_VALID_SIGNATURE_SELECTOR, msgHash, sig);
            bytes32 retval;
            /// @solidity memory-safe-assembly
            assembly {
                // allocate memory for storing the return value
                let outDataOffset := mload(0x40)
                mstore(0x40, add(outDataOffset, 32))
                // issue a static call and load the result if the call succeeded
                let success := staticcall(gas(), signer, add(data, 32), mload(data), outDataOffset, 32)
                if and(eq(success, 1), eq(returndatasize(), 32)) {
                    retval := mload(outDataOffset)
                }
            }
            return retval == bytes32(ERC1271_IS_VALID_SIGNATURE_SELECTOR);
        } else {
            return ECDSA.recover(msgHash, v, r, s) == signer;
        }
    }

    function _hasCode(address addr) internal view returns (bool) {
        uint256 size;
        /// @solidity memory-safe-assembly
        assembly { size := extcodesize(addr) }
        return size > 0;
    }

    function getSignatureRoot(bytes memory signature) internal pure returns (bytes32) {
        bytes32 part1;
        bytes32 part2;
        assembly {
            mstore(part1, mload(add(signature, 32)))
            mstore(part2, mload(add(signature, 64)))
        }
        return sha256(abi.encodePacked(part1, part2));
    }
}
          

contracts/0.4.24/test_helpers/VersionedMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.4.24;

import { Versioned } from "../utils/Versioned.sol";

contract VersionedMock is Versioned {
    constructor() public {}

    function getPetrifiedVersionMark() external pure returns (uint256) {
        return PETRIFIED_VERSION_MARK;
    }

    function checkContractVersion(uint256 version) external view {
        _checkContractVersion(version);
    }

    function setContractVersion(uint256 version) external {
        _setContractVersion(version);
    }
}
          

openzeppelin-solidity/contracts/token/ERC721/IERC721.sol

pragma solidity ^0.4.24;

import "../../introspection/IERC165.sol";

/**
 * @title ERC721 Non-Fungible Token Standard basic interface
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
 */
contract IERC721 is IERC165 {

  event Transfer(
    address indexed from,
    address indexed to,
    uint256 indexed tokenId
  );
  event Approval(
    address indexed owner,
    address indexed approved,
    uint256 indexed tokenId
  );
  event ApprovalForAll(
    address indexed owner,
    address indexed operator,
    bool approved
  );

  function balanceOf(address owner) public view returns (uint256 balance);
  function ownerOf(uint256 tokenId) public view returns (address owner);

  function approve(address to, uint256 tokenId) public;
  function getApproved(uint256 tokenId)
    public view returns (address operator);

  function setApprovalForAll(address operator, bool _approved) public;
  function isApprovedForAll(address owner, address operator)
    public view returns (bool);

  function transferFrom(address from, address to, uint256 tokenId) public;
  function safeTransferFrom(address from, address to, uint256 tokenId)
    public;

  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId,
    bytes data
  )
    public;
}
          

@aragon/os/contracts/lib/math/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;
    }
}
          

contracts/0.4.24/test_helpers/SigningKeysMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.4.24;

import {SigningKeys} from "../lib/SigningKeys.sol";

contract SigningKeysMock {
    using SigningKeys for bytes32;

    bytes32 public constant KEYSSIGS_POSITION = keccak256("KEYSSIGS_POSITION");

    uint256[] public _nodeOperatorIds;

    constructor(uint256[] memory ids) public {
        _nodeOperatorIds = ids;
    }

    function getKeyOffset(uint256 _nodeOperatorId, uint256 _keyIndex) external pure returns (uint256) {
        return KEYSSIGS_POSITION.getKeyOffset(_nodeOperatorId, _keyIndex);
    }

    function saveKeysSigs(
        uint256 _nodeOperatorId,
        uint256 _startIndex,
        uint256 _keysCount,
        bytes _publicKeys,
        bytes _signatures
    ) external returns (uint256) {
        return KEYSSIGS_POSITION.saveKeysSigs(_nodeOperatorId, _startIndex, _keysCount, _publicKeys, _signatures);
    }

    function removeKeysSigs(uint256 _nodeOperatorId, uint256 _startIndex, uint256 _keysCount, uint256 _lastIndex)
        external
        returns (uint256)
    {
        return KEYSSIGS_POSITION.removeKeysSigs(_nodeOperatorId, _startIndex, _keysCount, _lastIndex);
    }

    function loadKeysSigs(uint256 _nodeOperatorId, uint256 _startIndex, uint256 _keysCount)
        external
        view
        returns (bytes memory pubkeys, bytes memory signatures)
    {
        (pubkeys, signatures) = SigningKeys.initKeysSigsBuf(_keysCount);
        KEYSSIGS_POSITION.loadKeysSigs(
            _nodeOperatorId,
            _startIndex,
            _keysCount,
            pubkeys,
            signatures,
            0 // key offset inside _pubkeys/_signatures buffers
        );
    }

    function loadKeysSigsBatch(uint256[] _nodeOpIds, uint256[] _startIndexes, uint256[] _keysCounts)
        external
        view
        returns (bytes memory pubkeys, bytes memory signatures)
    {
        require(_nodeOpIds.length == _startIndexes.length && _startIndexes.length == _keysCounts.length, "LENGTH_MISMATCH");
        uint256 totalKeysCount;
        uint256 i;
        for (i = 0; i < _nodeOpIds.length; ++i) {
            totalKeysCount += _keysCounts[i];
        }
        (pubkeys, signatures) = SigningKeys.initKeysSigsBuf(totalKeysCount);
        uint256 loadedKeysCount;
        for (i = 0; i < _nodeOpIds.length; ++i) {
            KEYSSIGS_POSITION.loadKeysSigs(
                _nodeOpIds[i],
                _startIndexes[i],
                _keysCounts[i],
                pubkeys,
                signatures,
                loadedKeysCount // key offset inside _pubkeys/_signatures buffers
            );
            loadedKeysCount += _keysCounts[i];
        }
    }
}
          

@aragon/os/contracts/common/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();
    }
}
          

openzeppelin-solidity/contracts/token/ERC20/IERC20.sol

pragma solidity ^0.4.24;

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20 {
  function totalSupply() external view returns (uint256);

  function balanceOf(address who) external view returns (uint256);

  function allowance(address owner, address spender)
    external view returns (uint256);

  function transfer(address to, uint256 value) external returns (bool);

  function approve(address spender, uint256 value)
    external returns (bool);

  function transferFrom(address from, address to, uint256 value)
    external returns (bool);

  event Transfer(
    address indexed from,
    address indexed to,
    uint256 value
  );

  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}
          

contracts/0.4.24/test_helpers/ERC721Mock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol";

contract ERC721Mock is ERC721 {
    constructor() public ERC721() {}

    function mint(address _account, uint256 _tokenId) public {
        _mint(_account, _tokenId);
    }
}
          

contracts/common/lib/Math256.sol

// SPDX-FileCopyrightText: 2023 Lolik <[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;

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;
    }
}
          

@aragon/os/contracts/common/DepositableStorage.sol

pragma solidity 0.4.24;

import "./UnstructuredStorage.sol";


contract DepositableStorage {
    using UnstructuredStorage for bytes32;

    // keccak256("aragonOS.depositableStorage.depositable")
    bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea;

    function isDepositable() public view returns (bool) {
        return DEPOSITABLE_POSITION.getStorageBool();
    }

    function setDepositable(bool _depositable) internal {
        DEPOSITABLE_POSITION.setStorageBool(_depositable);
    }
}
          

contracts/0.4.24/test_helpers/StakeLimitUtilsMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../lib/StakeLimitUtils.sol";

contract StakeLimitUtilsMock {
    using UnstructuredStorage for bytes32;
    using StakeLimitUnstructuredStorage for bytes32;
    using StakeLimitUtils for StakeLimitState.Data;

    bytes32 internal constant STAKING_STATE_POSITION = keccak256("abcdef");

    event StakeLimitValue(
        uint256 currentLimit
    );

    function getStorageStakeLimit(uint256 _slotValue) public view returns (
        uint32 prevStakeBlockNumber,
        uint96 prevStakeLimit,
        uint32 maxStakeLimitGrowthBlocks,
        uint96 maxStakeLimit
    ) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        StakeLimitState.Data memory data = STAKING_STATE_POSITION.getStorageStakeLimitStruct();

        prevStakeBlockNumber = data.prevStakeBlockNumber;
        prevStakeLimit = data.prevStakeLimit;
        maxStakeLimitGrowthBlocks = data.maxStakeLimitGrowthBlocks;
        maxStakeLimit = data.maxStakeLimit;
    }

    function setStorageStakeLimitStruct(
        uint32 _prevStakeBlockNumber,
        uint96 _prevStakeLimit,
        uint32 _maxStakeLimitGrowthBlocks,
        uint96 _maxStakeLimit
    ) public view returns (uint256 ret) {
        StakeLimitState.Data memory data;
        data.prevStakeBlockNumber = _prevStakeBlockNumber;
        data.prevStakeLimit = _prevStakeLimit;
        data.maxStakeLimitGrowthBlocks = _maxStakeLimitGrowthBlocks;
        data.maxStakeLimit = _maxStakeLimit;

        STAKING_STATE_POSITION.setStorageStakeLimitStruct(data);
        return STAKING_STATE_POSITION.getStorageUint256();
    }

    function calculateCurrentStakeLimit(uint256 _slotValue) public view returns(uint256 limit) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        return STAKING_STATE_POSITION.getStorageStakeLimitStruct().calculateCurrentStakeLimit();
    }

    function emitCurrentStakeLimit(uint256 _slotValue) public {
        uint256 limit = calculateCurrentStakeLimit(_slotValue);
        emit StakeLimitValue(limit);
    }

    function isStakingPaused(uint256 _slotValue) public view returns(bool) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        return STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingPaused();
    }

    function isStakingLimitSet(uint256 _slotValue) public view returns(bool) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        return STAKING_STATE_POSITION.getStorageStakeLimitStruct().isStakingLimitSet();
    }

    function setStakingLimit(
        uint256 _slotValue, uint256 _maxStakeLimit, uint256 _stakeLimitIncreasePerBlock
    ) public view returns (uint256) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakingLimit(
                _maxStakeLimit, _stakeLimitIncreasePerBlock
            )
        );
        return STAKING_STATE_POSITION.getStorageUint256();
    }

    function removeStakingLimit(uint256 _slotValue) public view returns(uint256) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().removeStakingLimit()
        );
        return STAKING_STATE_POSITION.getStorageUint256();
    }

    function updatePrevStakeLimit(uint256 _slotValue, uint256 _newPrevLimit) public view returns(uint256) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().updatePrevStakeLimit(_newPrevLimit)
        );
        return STAKING_STATE_POSITION.getStorageUint256();
    }

    function setStakeLimitPauseState(uint256 _slotValue, bool _isPaused) public view returns(uint256) {
        STAKING_STATE_POSITION.setStorageUint256(_slotValue);
        STAKING_STATE_POSITION.setStorageStakeLimitStruct(
            STAKING_STATE_POSITION.getStorageStakeLimitStruct().setStakeLimitPauseState(_isPaused)
        );
        return STAKING_STATE_POSITION.getStorageUint256();
    }
}
          

@aragon/os/contracts/lib/math/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;
    }
}
          

@aragon/os/contracts/common/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) }
    }
}
          

contracts/0.4.24/test_helpers/AragonVaultMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "@aragon/os/contracts/apps/UnsafeAragonApp.sol";
import "@aragon/os/contracts/common/DepositableStorage.sol";


contract AragonVaultMock is UnsafeAragonApp, DepositableStorage {
    event LogFund(address sender, uint256 amount);

    function initialize() external {
        initialized();
        setDepositable(true);
    }

    function () external payable {
        emit LogFund(msg.sender, msg.value);
    }
}
          

@aragon/os/contracts/common/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)
        }
    }
}
          

@aragon/os/contracts/lib/token/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
    );
}
          

openzeppelin-solidity/contracts/math/SafeMath.sol

pragma solidity ^0.4.24;

/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @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);

    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); // 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);
    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);

    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);
    return a % b;
  }
}
          

contracts/0.4.24/lib/SigningKeys.sol

// SPDX-FileCopyrightText: 2023 Lolik <[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)));
    }
}
          

contracts/common/interfaces/IBurner.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity 0.4.24;

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 Lolik._burnShares())
     */
    function commitSharesToBurn(uint256 _stFTNSharesToBurn) 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);
}
          

@aragon/os/contracts/common/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);
}
          

contracts/0.4.24/StFTN.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "@aragon/os/contracts/common/UnstructuredStorage.sol";
import "@aragon/os/contracts/lib/math/SafeMath.sol";
import "./utils/Pausable.sol";

/**
 * @title Interest-bearing ERC20-like token for Lolik Liquid Stacking protocol.
 *
 * This contract is abstract. To make the contract deployable override the
 * `_getTotalPooledFtn` function. `Lolik.sol` contract inherits StFTN and defines
 * the `_getTotalPooledFtn` function.
 *
 * StFTN balances are dynamic and represent the holder's share in the total amount
 * of Ftn controlled by the protocol. Account shares aren't normalized, so the
 * contract also stores the sum of all shares to calculate each account's token balance
 * which equals to:
 *
 *   shares[account] * _getTotalPooledFtn() / _getTotalShares()
 *
 * For example, assume that we have:
 *
 *   _getTotalPooledFtn() -> 10 FTN
 *   sharesOf(user1) -> 100
 *   sharesOf(user2) -> 400
 *
 * Therefore:
 *
 *   balanceOf(user1) -> 2 tokens which corresponds 2 FTN
 *   balanceOf(user2) -> 8 tokens which corresponds 8 FTN
 *
 * Since balances of all token holders change when the amount of total pooled Ftn
 * changes, this token cannot fully implement ERC20 standard: it only emits `Transfer`
 * events upon explicit transfer between holders. In contrast, when total amount of
 * pooled Ftn increases, no `Transfer` events are generated: doing so would require
 * emitting an event for each token holder and thus running an unbounded loop.
 *
 * The token inherits from `Pausable` and uses `whenNotStopped` modifier for methods
 * which change `shares` or `allowances`. `_stop` and `_resume` functions are overridden
 * in `Lolik.sol` and might be called by an account with the `PAUSE_ROLE` assigned by the
 * DAO. This is useful for emergency scenarios, e.g. a protocol bug, where one might want
 * to freeze all token transfers and approvals until the emergency is resolved.
 */
contract StFTN is IERC20, Pausable {
    using SafeMath for uint256;
    using UnstructuredStorage for bytes32;

    address constant internal INITIAL_TOKEN_HOLDER = 0xdead;
    uint256 constant internal INFINITE_ALLOWANCE = ~uint256(0);

    /**
     * @dev StFTN balances are dynamic and are calculated based on the accounts' shares
     * and the total amount of Ftn controlled by the protocol. Account shares aren't
     * normalized, so the contract also stores the sum of all shares to calculate
     * each account's token balance which equals to:
     *
     *   shares[account] * _getTotalPooledFtn() / _getTotalShares()
    */
    mapping (address => uint256) private shares;

    /**
     * @dev Allowances are nominated in tokens, not token shares.
     */
    mapping (address => mapping (address => uint256)) private allowances;

    /**
     * @dev Storage position used for holding the total amount of shares in existence.
     *
     * The Lolik protocol is built on top of Aragon and uses the Unstructured Storage pattern
     * for value types:
     *
     * https://blog.openzeppelin.com/upgradeability-using-unstructured-storage
     * https://blog.8bitzen.com/posts/20-02-2020-understanding-how-solidity-upgradeable-unstructured-proxies-work
     *
     * For reference types, conventional storage variables are used since it's non-trivial
     * and error-prone to implement reference-type unstructured storage using Solidity v0.4;
     * see https://github.com/lidofinance/lolik-dao/issues/181#issuecomment-736098834
     *
     * keccak256("lolik.StFTN.totalShares")
     */
    bytes32 internal constant TOTAL_SHARES_POSITION =
        0xe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e;

    /**
      * @notice An executed shares transfer from `sender` to `recipient`.
      *
      * @dev emitted in pair with an ERC20-defined `Transfer` event.
      */
    event TransferShares(
        address indexed from,
        address indexed to,
        uint256 sharesValue
    );

    /**
     * @notice An executed `burnShares` request
     *
     * @dev Reports simultaneously burnt shares amount
     * and corresponding stFTN amount.
     * The stFTN amount is calculated twice: before and after the burning incurred rebase.
     *
     * @param account holder of the burnt shares
     * @param preRebaseTokenAmount amount of stFTN the burnt shares corresponded to before the burn
     * @param postRebaseTokenAmount amount of stFTN the burnt shares corresponded to after the burn
     * @param sharesAmount amount of burnt shares
     */
    event SharesBurnt(
        address indexed account,
        uint256 preRebaseTokenAmount,
        uint256 postRebaseTokenAmount,
        uint256 sharesAmount
    );

    /**
     * @return the name of the token.
     */
    function name() external pure returns (string) {
        return "Liquid staked Fasttoken";
    }

    /**
     * @return the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() external pure returns (string) {
        return "stFTN";
    }

    /**
     * @return the number of decimals for getting user representation of a token amount.
     */
    function decimals() external pure returns (uint8) {
        return 18;
    }

    /**
     * @return the amount of tokens in existence.
     *
     * @dev Always equals to `_getTotalPooledFtn()` since token amount
     * is pegged to the total amount of Ftn controlled by the protocol.
     */
    function totalSupply() external view returns (uint256) {
        return _getTotalPooledFtn();
    }

    /**
     * @return the entire amount of Ftn controlled by the protocol.
     *
     * @dev The sum of all FTN balances in the protocol, equals to the total supply of stFTN.
     */
    function getTotalPooledFtn() external view returns (uint256) {
        return _getTotalPooledFtn();
    }

    /**
     * @return the amount of tokens owned by the `_account`.
     *
     * @dev Balances are dynamic and equal the `_account`'s share in the amount of the
     * total Ftn controlled by the protocol. See `sharesOf`.
     */
    function balanceOf(address _account) external view returns (uint256) {
        return getPooledFtnByShares(_sharesOf(_account));
    }

    /**
     * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
     *
     * @return a boolean value indicating whether the operation succeeded.
     * Emits a `Transfer` event.
     * Emits a `TransferShares` event.
     *
     * Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the caller must have a balance of at least `_amount`.
     * - the contract must not be paused.
     *
     * @dev The `_amount` argument is the amount of tokens, not shares.
     */
    function transfer(address _recipient, uint256 _amount) external returns (bool) {
        _transfer(msg.sender, _recipient, _amount);
        return true;
    }

    /**
     * @return the remaining number of tokens that `_spender` is allowed to spend
     * on behalf of `_owner` through `transferFrom`. This is zero by default.
     *
     * @dev This value changes when `approve` or `transferFrom` is called.
     */
    function allowance(address _owner, address _spender) external view returns (uint256) {
        return allowances[_owner][_spender];
    }

    /**
     * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
     *
     * @return a boolean value indicating whether the operation succeeded.
     * Emits an `Approval` event.
     *
     * Requirements:
     *
     * - `_spender` cannot be the zero address.
     *
     * @dev The `_amount` argument is the amount of tokens, not shares.
     */
    function approve(address _spender, uint256 _amount) external returns (bool) {
        _approve(msg.sender, _spender, _amount);
        return true;
    }

    /**
     * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
     * allowance mechanism. `_amount` is then deducted from the caller's
     * allowance.
     *
     * @return a boolean value indicating whether the operation succeeded.
     *
     * Emits a `Transfer` event.
     * Emits a `TransferShares` event.
     * Emits an `Approval` event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `_sender` and `_recipient` cannot be the zero addresses.
     * - `_sender` must have a balance of at least `_amount`.
     * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
     * - the contract must not be paused.
     *
     * @dev The `_amount` argument is the amount of tokens, not shares.
     */
    function transferFrom(address _sender, address _recipient, uint256 _amount) external returns (bool) {
        _spendAllowance(_sender, msg.sender, _amount);
        _transfer(_sender, _recipient, _amount);
        return true;
    }

    /**
     * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
     *
     * This is an alternative to `approve` that can be used as a mitigation for
     * problems described in:
     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b709eae01d1da91902d06ace340df6b324e6f049/contracts/token/ERC20/IERC20.sol#L57
     * Emits an `Approval` event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `_spender` cannot be the the zero address.
     */
    function increaseAllowance(address _spender, uint256 _addedValue) external returns (bool) {
        _approve(msg.sender, _spender, allowances[msg.sender][_spender].add(_addedValue));
        return true;
    }

    /**
     * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
     *
     * This is an alternative to `approve` that can be used as a mitigation for
     * problems described in:
     * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b709eae01d1da91902d06ace340df6b324e6f049/contracts/token/ERC20/IERC20.sol#L57
     * Emits an `Approval` event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `_spender` cannot be the zero address.
     * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
     */
    function decreaseAllowance(address _spender, uint256 _subtractedValue) external returns (bool) {
        uint256 currentAllowance = allowances[msg.sender][_spender];
        require(currentAllowance >= _subtractedValue, "ALLOWANCE_BELOW_ZERO");
        _approve(msg.sender, _spender, currentAllowance.sub(_subtractedValue));
        return true;
    }

    /**
     * @return the total amount of shares in existence.
     *
     * @dev The sum of all accounts' shares can be an arbitrary number, therefore
     * it is necessary to store it in order to calculate each account's relative share.
     */
    function getTotalShares() external view returns (uint256) {
        return _getTotalShares();
    }

    /**
     * @return the amount of shares owned by `_account`.
     */
    function sharesOf(address _account) external view returns (uint256) {
        return _sharesOf(_account);
    }

    /**
     * @return the amount of shares that corresponds to `_ftnAmount` protocol-controlled Ftn.
     */
    function getSharesByPooledFtn(uint256 _ftnAmount) public view returns (uint256) {
        return _ftnAmount
            .mul(_getTotalShares())
            .div(_getTotalPooledFtn());
    }

    /**
     * @return the amount of Ftn that corresponds to `_sharesAmount` token shares.
     */
    function getPooledFtnByShares(uint256 _sharesAmount) public view returns (uint256) {
        return _sharesAmount
            .mul(_getTotalPooledFtn())
            .div(_getTotalShares());
    }

    /**
     * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
     *
     * @return amount of transferred tokens.
     * Emits a `TransferShares` event.
     * Emits a `Transfer` event.
     *
     * Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the caller must have at least `_sharesAmount` shares.
     * - the contract must not be paused.
     *
     * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
     */
    function transferShares(address _recipient, uint256 _sharesAmount) external returns (uint256) {
        _transferShares(msg.sender, _recipient, _sharesAmount);
        uint256 tokensAmount = getPooledFtnByShares(_sharesAmount);
        _emitTransferEvents(msg.sender, _recipient, tokensAmount, _sharesAmount);
        return tokensAmount;
    }

    /**
     * @notice Moves `_sharesAmount` token shares from the `_sender` account to the `_recipient` account.
     *
     * @return amount of transferred tokens.
     * Emits a `TransferShares` event.
     * Emits a `Transfer` event.
     *
     * Requirements:
     *
     * - `_sender` and `_recipient` cannot be the zero addresses.
     * - `_sender` must have at least `_sharesAmount` shares.
     * - the caller must have allowance for `_sender`'s tokens of at least `getPooledFtnByShares(_sharesAmount)`.
     * - the contract must not be paused.
     *
     * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
     */
    function transferSharesFrom(
        address _sender, address _recipient, uint256 _sharesAmount
    ) external returns (uint256) {
        uint256 tokensAmount = getPooledFtnByShares(_sharesAmount);
        _spendAllowance(_sender, msg.sender, tokensAmount);
        _transferShares(_sender, _recipient, _sharesAmount);
        _emitTransferEvents(_sender, _recipient, tokensAmount, _sharesAmount);
        return tokensAmount;
    }

    /**
     * @return the total amount (in wei) of Ftn controlled by the protocol.
     * @dev This is used for calculating tokens from shares and vice versa.
     * @dev This function is required to be implemented in a derived contract.
     */
    function _getTotalPooledFtn() internal view returns (uint256);

    /**
     * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
     * Emits a `Transfer` event.
     * Emits a `TransferShares` event.
     */
    function _transfer(address _sender, address _recipient, uint256 _amount) internal {
        uint256 _sharesToTransfer = getSharesByPooledFtn(_amount);
        _transferShares(_sender, _recipient, _sharesToTransfer);
        _emitTransferEvents(_sender, _recipient, _amount, _sharesToTransfer);
    }

    /**
     * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
     *
     * Emits an `Approval` event.
     *
     * NB: the method can be invoked even if the protocol paused.
     *
     * Requirements:
     *
     * - `_owner` cannot be the zero address.
     * - `_spender` cannot be the zero address.
     */
    function _approve(address _owner, address _spender, uint256 _amount) internal {
        require(_owner != address(0), "APPROVE_FROM_ZERO_ADDR");
        require(_spender != address(0), "APPROVE_TO_ZERO_ADDR");

        allowances[_owner][_spender] = _amount;
        emit Approval(_owner, _spender, _amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address _owner, address _spender, uint256 _amount) internal {
        uint256 currentAllowance = allowances[_owner][_spender];
        if (currentAllowance != INFINITE_ALLOWANCE) {
            require(currentAllowance >= _amount, "ALLOWANCE_EXCEEDED");
            _approve(_owner, _spender, currentAllowance - _amount);
        }
    }

    /**
     * @return the total amount of shares in existence.
     */
    function _getTotalShares() internal view returns (uint256) {
        return TOTAL_SHARES_POSITION.getStorageUint256();
    }

    /**
     * @return the amount of shares owned by `_account`.
     */
    function _sharesOf(address _account) internal view returns (uint256) {
        return shares[_account];
    }

    /**
     * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
     *
     * Requirements:
     *
     * - `_sender` cannot be the zero address.
     * - `_recipient` cannot be the zero address or the `stFTN` token contract itself
     * - `_sender` must hold at least `_sharesAmount` shares.
     * - the contract must not be paused.
     */
    function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
        require(_sender != address(0), "TRANSFER_FROM_ZERO_ADDR");
        require(_recipient != address(0), "TRANSFER_TO_ZERO_ADDR");
        require(_recipient != address(this), "TRANSFER_TO_STFTN_CONTRACT");
        _whenNotStopped();

        uint256 currentSenderShares = shares[_sender];
        require(_sharesAmount <= currentSenderShares, "BALANCE_EXCEEDED");

        shares[_sender] = currentSenderShares.sub(_sharesAmount);
        shares[_recipient] = shares[_recipient].add(_sharesAmount);
    }

    /**
     * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
     * @dev This doesn't increase the token total supply.
     *
     * NB: The method doesn't check protocol pause relying on the external enforcement.
     *
     * Requirements:
     *
     * - `_recipient` cannot be the zero address.
     * - the contract must not be paused.
     */
    function _mintShares(address _recipient, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
        require(_recipient != address(0), "MINT_TO_ZERO_ADDR");

        newTotalShares = _getTotalShares().add(_sharesAmount);
        TOTAL_SHARES_POSITION.setStorageUint256(newTotalShares);

        shares[_recipient] = shares[_recipient].add(_sharesAmount);

        // Notice: we're not emitting a Transfer event from the zero address here since shares mint
        // works by taking the amount of tokens corresponding to the minted shares from all other
        // token holders, proportionally to their share. The total supply of the token doesn't change
        // as the result. This is equivalent to performing a send from each other token holder's
        // address to `address`, but we cannot reflect this as it would require sending an unbounded
        // number of events.
    }

    /**
     * @notice Destroys `_sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
     * @dev This doesn't decrease the token total supply.
     *
     * Requirements:
     *
     * - `_account` cannot be the zero address.
     * - `_account` must hold at least `_sharesAmount` shares.
     * - the contract must not be paused.
     */
    function _burnShares(address _account, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
        require(_account != address(0), "BURN_FROM_ZERO_ADDR");

        uint256 accountShares = shares[_account];
        require(_sharesAmount <= accountShares, "BALANCE_EXCEEDED");

        uint256 preRebaseTokenAmount = getPooledFtnByShares(_sharesAmount);

        newTotalShares = _getTotalShares().sub(_sharesAmount);
        TOTAL_SHARES_POSITION.setStorageUint256(newTotalShares);

        shares[_account] = accountShares.sub(_sharesAmount);

        uint256 postRebaseTokenAmount = getPooledFtnByShares(_sharesAmount);

        emit SharesBurnt(_account, preRebaseTokenAmount, postRebaseTokenAmount, _sharesAmount);

        // Notice: we're not emitting a Transfer event to the zero address here since shares burn
        // works by redistributing the amount of tokens corresponding to the burned shares between
        // all other token holders. The total supply of the token doesn't change as the result.
        // This is equivalent to performing a send from `address` to each other token holder address,
        // but we cannot reflect this as it would require sending an unbounded number of events.

        // We're emitting `SharesBurnt` event to provide an explicit rebase log record nonetheless.
    }

    /**
     * @dev Emits {Transfer} and {TransferShares} events
     */
    function _emitTransferEvents(address _from, address _to, uint _tokenAmount, uint256 _sharesAmount) internal {
        emit Transfer(_from, _to, _tokenAmount);
        emit TransferShares(_from, _to, _sharesAmount);
    }

    /**
     * @dev Emits {Transfer} and {TransferShares} events where `from` is 0 address. Indicates mint events.
     */
    function _emitTransferAfterMintingShares(address _to, uint256 _sharesAmount) internal {
        _emitTransferEvents(address(0), _to, getPooledFtnByShares(_sharesAmount), _sharesAmount);
    }

    /**
     * @dev Mints shares to INITIAL_TOKEN_HOLDER
     */
    function _mintInitialShares(uint256 _sharesAmount) internal {
        _mintShares(INITIAL_TOKEN_HOLDER, _sharesAmount);
        _emitTransferAfterMintingShares(INITIAL_TOKEN_HOLDER, _sharesAmount);
    }
}
          

@aragon/os/contracts/evmscript/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);
}
          

@aragon/os/contracts/evmscript/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);
}
          

contracts/0.4.24/lib/Packed64x4.sol

// SPDX-FileCopyrightText: 2023 Lolik <[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));
    }
}
          

contracts/0.4.24/StFTNPermit.sol

// SPDX-FileCopyrightText: 2023 OpenZeppelin, Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
pragma solidity 0.4.24;

import {UnstructuredStorage} from "@aragon/os/contracts/common/UnstructuredStorage.sol";

import {SignatureUtils} from "../common/lib/SignatureUtils.sol";
import {IEIP712StFTN} from "../common/interfaces/IEIP712StFTN.sol";

import {StFTN} from "./StFTN.sol";

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ftn at all.
 */
interface IERC2612 {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     */
    function permit(
        address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}


contract StFTNPermit is IERC2612, StFTN {
    using UnstructuredStorage for bytes32;

    /**
     * @dev Service event for initialization
     */
    event EIP712StFTNInitialized(address eip712StFTN);

    /**
     * @dev Nonces for ERC-2612 (Permit)
     */
    mapping(address => uint256) internal noncesByAddress;

    /**
     * @dev Storage position used for the EIP712 message utils contract
     *
     * keccak256("lolik.StFTNPermit.eip712StFTN")
     */
    bytes32 internal constant EIP712_STFTN_POSITION =
        0x42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c;

    /**
     * @dev Typehash constant for ERC-2612 (Permit)
     *
     * keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
     */
    bytes32 internal constant PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     */
    function permit(
        address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s
    ) external {
        require(block.timestamp <= _deadline, "DEADLINE_EXPIRED");

        bytes32 structHash = keccak256(
            abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, _useNonce(_owner), _deadline)
        );

        bytes32 hash = IEIP712StFTN(getEIP712StFTN()).hashTypedDataV4(address(this), structHash);

        require(SignatureUtils.isValidSignature(_owner, hash, _v, _r, _s), "INVALID_SIGNATURE");
        _approve(_owner, _spender, _value);
    }

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256) {
        return noncesByAddress[owner];
    }

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32) {
        return IEIP712StFTN(getEIP712StFTN()).domainSeparatorV4(address(this));
    }

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     *
     * NB: compairing to the full-fledged ERC-5267 version:
     * - `salt` and `extensions` are unused
     * - `flags` is hex"0f" or 01111b
     *
     * @dev using shortened returns to reduce a bytecode size
     */
    function eip712Domain() external view returns (
        string memory name,
        string memory version,
        uint256 chainId,
        address verifyingContract
    ) {
        return IEIP712StFTN(getEIP712StFTN()).eip712Domain(address(this));
    }

    /**
     * @dev "Consume a nonce": return the current value and increment.
     */
    function _useNonce(address _owner) internal returns (uint256 current) {
        current = noncesByAddress[_owner];
        noncesByAddress[_owner] = current.add(1);
    }

    /**
     * @dev Initialize EIP712 message utils contract for stFTN
     */
    function _initializeEIP712StFTN(address _eip712StFTN) internal {
        require(_eip712StFTN != address(0), "ZERO_EIP712STFTN");
        require(getEIP712StFTN() == address(0), "EIP712STFTN_ALREADY_SET");

        EIP712_STFTN_POSITION.setStorageAddress(_eip712StFTN);

        emit EIP712StFTNInitialized(_eip712StFTN);
    }

    /**
     * @dev Get EIP712 message utils contract
     */
    function getEIP712StFTN() public view returns (address) {
        return EIP712_STFTN_POSITION.getStorageAddress();
    }
}
          

contracts/0.4.24/test_helpers/NodeOperatorsRegistryMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../nos/NodeOperatorsRegistry.sol";

contract NodeOperatorsRegistryMock is NodeOperatorsRegistry {

    function increaseNodeOperatorDepositedSigningKeysCount(uint256 _nodeOperatorId, uint64 _keysCount) external {
        Packed64x4.Packed memory signingKeysStats = _nodeOperators[_nodeOperatorId].signingKeysStats;
        signingKeysStats.set(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET, signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET) + _keysCount);
        _nodeOperators[_nodeOperatorId].signingKeysStats = signingKeysStats;

        Packed64x4.Packed memory totalSigningKeysStats = _loadSummarySigningKeysStats();
        totalSigningKeysStats.set(
            TOTAL_DEPOSITED_KEYS_COUNT_OFFSET, totalSigningKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET).add(_keysCount)
        );
        _saveSummarySigningKeysStats(totalSigningKeysStats);

        _updateSummaryMaxValidatorsCount(_nodeOperatorId);
    }

    function testing_markAllKeysDeposited() external {
        uint256 nodeOperatorsCount = getNodeOperatorsCount();
        Packed64x4.Packed memory signingKeysStats;
        for (uint256 i; i < nodeOperatorsCount; ++i) {
            signingKeysStats =  _loadOperatorSigningKeysStats(i);
            testing_setDepositedSigningKeysCount(i, signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET));
        }
    }

    function testing_markAllKeysDeposited(uint256 _nodeOperatorId) external {
        _onlyExistedNodeOperator(_nodeOperatorId);
        Packed64x4.Packed memory signingKeysStats = _nodeOperators[_nodeOperatorId].signingKeysStats;
        testing_setDepositedSigningKeysCount(_nodeOperatorId, signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET));
    }

    function testing_setDepositedSigningKeysCount(uint256 _nodeOperatorId, uint256 _depositedSigningKeysCount) public {
        _onlyExistedNodeOperator(_nodeOperatorId);
        // NodeOperator storage nodeOperator = _nodeOperators[_nodeOperatorId];
        Packed64x4.Packed memory signingKeysStats = _loadOperatorSigningKeysStats(_nodeOperatorId);
        uint256 depositedSigningKeysCountBefore = signingKeysStats.get(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET);
        if (_depositedSigningKeysCount == depositedSigningKeysCountBefore) {
            return;
        }

        require(
            _depositedSigningKeysCount <= signingKeysStats.get(TOTAL_VETTED_KEYS_COUNT_OFFSET),
            "DEPOSITED_SIGNING_KEYS_COUNT_TOO_HIGH"
        );
        require(
            _depositedSigningKeysCount >= signingKeysStats.get(TOTAL_EXITED_KEYS_COUNT_OFFSET), "DEPOSITED_SIGNING_KEYS_COUNT_TOO_LOW"
        );

        signingKeysStats.set(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET, uint64(_depositedSigningKeysCount));
        _saveOperatorSigningKeysStats(_nodeOperatorId, signingKeysStats);

        emit DepositedSigningKeysCountChanged(_nodeOperatorId, _depositedSigningKeysCount);
        _increaseValidatorsKeysNonce();
    }

    function testing_unsafeDeactivateNodeOperator(uint256 _nodeOperatorId) external {
        NodeOperator storage operator = _nodeOperators[_nodeOperatorId];
        operator.active = false;
    }

    function testing_addNodeOperator(
        string _name,
        address _rewardAddress,
        uint64 totalSigningKeysCount,
        uint64 vettedSigningKeysCount,
        uint64 depositedSigningKeysCount,
        uint64 exitedSigningKeysCount
    ) external returns (uint256 id) {
        id = getNodeOperatorsCount();

        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;

        Packed64x4.Packed memory signingKeysStats;
        signingKeysStats.set(TOTAL_DEPOSITED_KEYS_COUNT_OFFSET, depositedSigningKeysCount);
        signingKeysStats.set(TOTAL_VETTED_KEYS_COUNT_OFFSET, vettedSigningKeysCount);
        signingKeysStats.set(TOTAL_EXITED_KEYS_COUNT_OFFSET, exitedSigningKeysCount);
        signingKeysStats.set(TOTAL_KEYS_COUNT_OFFSET, totalSigningKeysCount);

        operator.signingKeysStats = signingKeysStats;

        Packed64x4.Packed memory operatorTargetStats;
        operatorTargetStats.set(MAX_VALIDATORS_COUNT_OFFSET, vettedSigningKeysCount);
        operator.targetValidatorsStats = operatorTargetStats;

        emit NodeOperatorAdded(id, _name, _rewardAddress, 0);

        Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats();
        summarySigningKeysStats.add(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET, vettedSigningKeysCount);
        summarySigningKeysStats.add(SUMMARY_EXITED_KEYS_COUNT_OFFSET, exitedSigningKeysCount);
        summarySigningKeysStats.add(SUMMARY_TOTAL_KEYS_COUNT_OFFSET, totalSigningKeysCount);
        summarySigningKeysStats.add(SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET, depositedSigningKeysCount);
        _saveSummarySigningKeysStats(summarySigningKeysStats);
    }

    function testing_setNodeOperatorLimits(
        uint256 _nodeOperatorId,
        uint64 stuckValidatorsCount,
        uint64 refundedValidatorsCount,
        uint64 stuckPenaltyEndAt
    ) external {
        Packed64x4.Packed memory stuckPenaltyStats = _nodeOperators[_nodeOperatorId].stuckPenaltyStats;
        stuckPenaltyStats.set(STUCK_VALIDATORS_COUNT_OFFSET, stuckValidatorsCount);
        stuckPenaltyStats.set(REFUNDED_VALIDATORS_COUNT_OFFSET, refundedValidatorsCount);
        stuckPenaltyStats.set(STUCK_PENALTY_END_TIMESTAMP_OFFSET, stuckPenaltyEndAt);
        _nodeOperators[_nodeOperatorId].stuckPenaltyStats = stuckPenaltyStats;
        _updateSummaryMaxValidatorsCount(_nodeOperatorId);
    }

    function testing_getTotalSigningKeysStats()
        external
        view
        returns (
            uint256 totalSigningKeysCount,
            uint256 maxValidatorsCount,
            uint256 depositedSigningKeysCount,
            uint256 exitedSigningKeysCount
        )
    {
        Packed64x4.Packed memory summarySigningKeysStats = _loadSummarySigningKeysStats();
        totalSigningKeysCount = summarySigningKeysStats.get(SUMMARY_TOTAL_KEYS_COUNT_OFFSET);
        maxValidatorsCount = summarySigningKeysStats.get(SUMMARY_MAX_VALIDATORS_COUNT_OFFSET);
        depositedSigningKeysCount = summarySigningKeysStats.get(SUMMARY_DEPOSITED_KEYS_COUNT_OFFSET);
        exitedSigningKeysCount = summarySigningKeysStats.get(SUMMARY_EXITED_KEYS_COUNT_OFFSET);
    }

    function testing_setBaseVersion(uint256 _newBaseVersion) external {
        _setContractVersion(_newBaseVersion);
    }

    function testing_resetRegistry() external {
        uint256 totalOperatorsCount = TOTAL_OPERATORS_COUNT_POSITION.getStorageUint256();
        TOTAL_OPERATORS_COUNT_POSITION.setStorageUint256(0);
        ACTIVE_OPERATORS_COUNT_POSITION.setStorageUint256(0);
        KEYS_OP_INDEX_POSITION.setStorageUint256(0);

        _nodeOperatorSummary = NodeOperatorSummary({
            summarySigningKeysStats: Packed64x4.Packed(0)
        });

        Packed64x4.Packed memory tmp;
        for (uint256 i = 0; i < totalOperatorsCount; ++i) {
            _nodeOperators[i] = NodeOperator(false, address(0), new string(0), tmp, tmp, tmp);
        }
    }

    function testing_getSigningKeysAllocationData(uint256 _keysCount)
        external
        view
        returns (
            uint256 allocatedKeysCount,
            uint256[] memory nodeOperatorIds,
            uint256[] memory activeKeyCountsAfterAllocation
        )
    {
        return _getSigningKeysAllocationData(_keysCount);
    }

    function testing_obtainDepositData(uint256 _keysToAllocate)
        external
        returns (uint256 loadedValidatorsKeysCount, bytes memory publicKeys, bytes memory signatures)
    {
        (publicKeys, signatures) = this.obtainDepositData(_keysToAllocate, new bytes(0));
        emit ValidatorsKeysLoaded(publicKeys, signatures);
    }

    function testing_isNodeOperatorPenalized(uint256 operatorId) external view returns (bool) {
        Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(operatorId);
        if (
            stuckPenaltyStats.get(REFUNDED_VALIDATORS_COUNT_OFFSET) < stuckPenaltyStats.get(STUCK_VALIDATORS_COUNT_OFFSET)
                || block.timestamp <= stuckPenaltyStats.get(STUCK_PENALTY_END_TIMESTAMP_OFFSET)
        ) {
            return true;
        }
        return false;
    }

    function testing_getNodeOperator(uint256 operatorId) external view
        returns (uint256 exitedSigningKeysCount, uint256 depositedSigningKeysCount, uint256 maxSigningKeysCount)
    {
        return _getNodeOperator(operatorId);
    }

    event ValidatorsKeysLoaded(bytes publicKeys, bytes signatures);

    function testing_distributeRewards() external returns (uint256) {
        return _distributeRewards();
    }

     function testing_setNodeOperatorPenalty(
        uint256 _nodeOperatorId,
        uint256 _refundedValidatorsCount,
        uint256 _stuckValidatorsCount,
        uint256 _stuckPenaltyEndTimestamp
    ) external {
        _requireValidRange(_refundedValidatorsCount <= UINT64_MAX);
        _requireValidRange(_stuckValidatorsCount <= UINT64_MAX);
        _requireValidRange(_stuckPenaltyEndTimestamp <= UINT64_MAX);
        Packed64x4.Packed memory stuckPenaltyStats = _loadOperatorStuckPenaltyStats(
            _nodeOperatorId
        );

        stuckPenaltyStats.set(REFUNDED_VALIDATORS_COUNT_OFFSET, uint64(_refundedValidatorsCount));
        stuckPenaltyStats.set(STUCK_VALIDATORS_COUNT_OFFSET, uint64(_stuckValidatorsCount));
        stuckPenaltyStats.set(STUCK_PENALTY_END_TIMESTAMP_OFFSET, uint64(_stuckPenaltyEndTimestamp));
        _saveOperatorStuckPenaltyStats(_nodeOperatorId, stuckPenaltyStats);
        _updateSummaryMaxValidatorsCount(_nodeOperatorId);
    }
}
          

@aragon/os/contracts/common/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);
}
          

@aragon/os/contracts/acl/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);
}
          

@aragon/os/contracts/apps/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
    }
}
          

@aragon/os/contracts/common/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;
    }
}
          

contracts/common/lib/MinFirstAllocationStrategy.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
// solhint-disable-next-line
pragma solidity 0.4.24;

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;
    }
}
          

contracts/common/lib/ECDSA.sol

// SPDX-License-Identifier: MIT

// Extracted from:
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/cryptography/ECDSA.sol#L53
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/541e821/contracts/utils/cryptography/ECDSA.sol#L112

/* See contracts/COMPILERS.md */
// solhint-disable-next-line
pragma solidity 0.4.24;


library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`).
     * This address can then be used for verification purposes.
     * Receives the `v`, `r` and `s` signature fields separately.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address)
    {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Overload of `recover` that receives the `r` and `vs` short-signature fields separately.
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return recover(hash, v, r, s);
    }
}
          

openzeppelin-solidity/contracts/introspection/ERC165.sol

pragma solidity ^0.4.24;

import "./IERC165.sol";

/**
 * @title ERC165
 * @author Matt Condon (@shrugs)
 * @dev Implements ERC165 using a lookup table.
 */
contract ERC165 is IERC165 {

  bytes4 private constant _InterfaceId_ERC165 = 0x01ffc9a7;
  /**
   * 0x01ffc9a7 ===
   *   bytes4(keccak256('supportsInterface(bytes4)'))
   */

  /**
   * @dev a mapping of interface id to whether or not it's supported
   */
  mapping(bytes4 => bool) private _supportedInterfaces;

  /**
   * @dev A contract implementing SupportsInterfaceWithLookup
   * implement ERC165 itself
   */
  constructor()
    internal
  {
    _registerInterface(_InterfaceId_ERC165);
  }

  /**
   * @dev implement supportsInterface(bytes4) using a lookup table
   */
  function supportsInterface(bytes4 interfaceId)
    external
    view
    returns (bool)
  {
    return _supportedInterfaces[interfaceId];
  }

  /**
   * @dev internal method for registering an interface
   */
  function _registerInterface(bytes4 interfaceId)
    internal
  {
    require(interfaceId != 0xffffffff);
    _supportedInterfaces[interfaceId] = true;
  }
}
          

contracts/0.4.24/utils/Pausable.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "@aragon/os/contracts/common/UnstructuredStorage.sol";


contract Pausable {
    using UnstructuredStorage for bytes32;

    event Stopped();
    event Resumed();

    // keccak256("lolik.Pausable.activeFlag")
    bytes32 internal constant ACTIVE_FLAG_POSITION =
        0x644132c4ddd5bb6f0655d5fe2870dcec7870e6be4758890f366b83441f9fdece;

    function _whenNotStopped() internal view {
        require(ACTIVE_FLAG_POSITION.getStorageBool(), "CONTRACT_IS_STOPPED");
    }

    function _whenStopped() internal view {
        require(!ACTIVE_FLAG_POSITION.getStorageBool(), "CONTRACT_IS_ACTIVE");
    }

    function isStopped() public view returns (bool) {
        return !ACTIVE_FLAG_POSITION.getStorageBool();
    }

    function _stop() internal {
        _whenNotStopped();

        ACTIVE_FLAG_POSITION.setStorageBool(false);
        emit Stopped();
    }

    function _resume() internal {
        _whenStopped();

        ACTIVE_FLAG_POSITION.setStorageBool(true);
        emit Resumed();
    }
}
          

contracts/0.4.24/test_helpers/StFTNPermitMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../StFTNPermit.sol";
import "./StFTNMock.sol";

/**
 * @dev Only for testing purposes!
 * StFTNPermit mock version of mintable/burnable/stoppable token.
 */
contract StFTNPermitMock is StFTNPermit, StFTNMock {
    function initializeEIP712StFTN(address _eip712StFTN) external {
        _initializeEIP712StFTN(_eip712StFTN);
    }

    function getBlockTime() external view returns (uint256) {
        return block.timestamp;
    }
}
          

contracts/0.4.24/utils/Versioned.sol

// SPDX-FileCopyrightText: 2023 Lolik <[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("lolik.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);
    }
}
          

@aragon/os/contracts/common/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);
    }
}
          

@aragon/os/contracts/apps/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);
    }
}
          

@aragon/os/contracts/apps/UnsafeAragonApp.sol

/*
 * SPDX-License-Identifier:    MIT
 */

pragma solidity ^0.4.24;

import "../common/UnstructuredStorage.sol";
import "./AragonApp.sol";


// Using UnsafeAragonApp means you'll be playing with 🔥.
// A number of safe defaults are provided with AragonApp, to help you avoid dangerous situations
// and mistakes with how your contract's developed as well as how it's deployed.
// UnsafeAragonApp turns off these safety features to give you greater control over your contract.
// In particular, it allows you to:
//   - Use deployed base contracts as apps directly, without a proxy
contract UnsafeAragonApp is AragonApp {
    using UnstructuredStorage for bytes32;

    constructor() public {
        // Removes auto petrifying; simulates a delete at INITIALIZATION_BLOCK_POSITION
        INITIALIZATION_BLOCK_POSITION.setStorageUint256(0);
    }
}
          

contracts/0.4.24/test_helpers/ERC20WrongTransferMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";


/**
  * @dev This is a mock. Don't use in production.
  */
contract ERC20WrongTransferMock is ERC20 {
    function mint(address account, uint256 value) public {
        _mint(account, value);
    }

    function transfer(address /*to*/, uint256 /*value*/) public returns (bool) {
        return false;
    }
}
          

contracts/common/interfaces/ILolikLocator.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity 0.4.24;

interface ILolikLocator {
    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 lolik() 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 oracleReportComponentsForLolik() external view returns(
        address accountingOracle,
        address elRewardsVault,
        address oracleReportSanityChecker,
        address burner,
        address withdrawalQueue,
        address withdrawalVault,
        address postTokenRebaseReceiver
    );
}
          

@aragon/os/contracts/common/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);
    }
}
          

@aragon/os/contracts/common/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;
    }
}
          

contracts/0.4.24/test_helpers/LolikMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../Lolik.sol";
import "./VaultMock.sol";

/**
 * @dev Only for testing purposes! Lolik version with some functions exposed.
 */
contract LolikMock is Lolik {
    bytes32 internal constant ALLOW_TOKEN_POSITION = keccak256("lolik.Lolik.allowToken");
    uint256 internal constant UNLIMITED_TOKEN_REBASE = uint256(-1);

    function initialize(
        address _lolikLocator,
        address _eip712StFTN
    )
        public
        payable
    {
        super.initialize(
            _lolikLocator,
            _eip712StFTN
        );

        setAllowRecoverability(true);
    }

    /**
     * @dev For use in tests to make protocol operational after deployment
     */
    function resumeProtocolAndStaking() public {
        _resume();
        _resumeStaking();
    }

    /**
     * @dev Only for testing recovery vault
     */
    function makeUnaccountedEther() public payable {}

    function setVersion(uint256 _version) external {
        CONTRACT_VERSION_POSITION.setStorageUint256(_version);
    }

    function allowRecoverability(address /*token*/) public view returns (bool) {
        return getAllowRecoverability();
    }

    function setAllowRecoverability(bool allow) public {
        ALLOW_TOKEN_POSITION.setStorageBool(allow);
    }

    function getAllowRecoverability() public view returns (bool) {
        return ALLOW_TOKEN_POSITION.getStorageBool();
    }

    function resetEip712StFTN() external {
        EIP712_STFTN_POSITION.setStorageAddress(0);
    }

    function burnShares(address _account, uint256 _amount) external {
        _burnShares(_account, _amount);
    }
}
          

contracts/0.4.24/oracle/LegacyOracle.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
pragma solidity 0.4.24;

import "@aragon/os/contracts/apps/AragonApp.sol";

import "../../common/interfaces/ILolikLocator.sol";

import "../utils/Versioned.sol";


interface IAccountingOracle {
    function getConsensusContract() external view returns (address);
}


interface IHashConsensus {
    function getChainConfig() external view returns (
        uint256 slotsPerEpoch,
        uint256 secondsPerSlot,
        uint256 genesisTime
    );

    function getFrameConfig() external view returns (
        uint256 initialEpoch,
        uint256 epochsPerFrame
    );

    function getCurrentFrame() external view returns (
        uint256 refSlot,
        uint256 reportProcessingDeadlineSlot
    );
}


/**
 * @title DEPRECATED legacy oracle contract stub kept for compatibility purposes only.
 * Should not be used in new code.
 *
 * Previously, the oracle contract was located at this address. Currently, the oracle lives
 * at a different address, and this contract is kept for the compatibility, supporting a
 * limited subset of view functions and events.
 *
 * See docs.lolik.fi for more info.
 */
contract LegacyOracle is Versioned, AragonApp {

    struct ChainSpec {
        uint64 epochsPerFrame;
        uint64 slotsPerEpoch;
        uint64 secondsPerSlot;
        uint64 genesisTime;
    }

    /// @notice DEPRECATED, kept for compatibility purposes only. The new Rebase event emitted
    /// from the main Lolik contract should be used instead.
    ///
    /// This event is still emitted after oracle committee reaches consensus on a report, but
    /// only for compatibility purposes. The values in this event are not enough to calculate
    /// APR or TVL anymore due to withdrawals, execution layer rewards, and consensus layer
    /// rewards skimming.
    event Completed(
        uint256 epochId,
        uint128 beaconBalance,
        uint128 beaconValidators
    );

    /// @notice DEPRECATED, kept for compatibility purposes only. The new Rebase event emitted
    /// from the main Lolik contract should be used instead.
    ///
    /// This event is still emitted after each rebase but only for compatibility purposes.
    /// The values in this event are not enough to correctly calculate the rebase APR since
    /// a rebase can result from shares burning without changing total FTN held by the
    /// protocol.
    event PostTotalShares(
        uint256 postTotalPooledFtn,
        uint256 preTotalPooledFtn,
        uint256 timeElapsed,
        uint256 totalShares
    );

    /// Address of the Lolik contract
    bytes32 internal constant LOLIK_POSITION =
        0xf6978a4f7e200f6d3a24d82d44c48bddabce399a3b8ec42a480ea8a2d5fe6ec5; // keccak256("lolik.LolikOracle.lolik")

    /// Address of the new accounting oracle contract
    bytes32 internal constant ACCOUNTING_ORACLE_POSITION =
        0xea0b659bb027a76ad14e51fad85cb5d4cedf3fd9dc4531be67b31d6d8725e9c6; // keccak256("lolik.LolikOracle.accountingOracle");

    /// Storage for the Bahamut chain specification
    bytes32 internal constant BEACON_SPEC_POSITION =
        0x805e82d53a51be3dfde7cfed901f1f96f5dad18e874708b082adb8841e8ca909; // keccak256("lolik.LolikOracle.beaconSpec")

    /// Version of the initialized contract data (DEPRECATED)
    bytes32 internal constant CONTRACT_VERSION_POSITION_DEPRECATED =
        0x75be19a3f314d89bd1f84d30a6c84e2f1cd7afc7b6ca21876564c265113bb7e4; // keccak256("lolik.LolikOracle.contractVersion")

    /// Historic data about 2 last completed reports and their times
    bytes32 internal constant FTNPOST_COMPLETED_TOTAL_POOLED_FTN_POSITION =
        0xaa8433b13d2b111d4f84f6f374bc7acbe20794944308876aa250fa9a73dc7f53; // keccak256("lolik.LolikOracle.postCompletedTotalPooledFtn")
    bytes32 internal constant PRE_COMPLETED_TOTAL_POOLED_FTN_POSITION =
        0x1043177539af09a67d747435df3ff1155a64cd93a347daaac9132a591442d43e; // keccak256("lolik.LolikOracle.preCompletedTotalPooledFtn")
    bytes32 internal constant LAST_COMPLETED_EPOCH_ID_POSITION =
        0xdad15c0beecd15610092d84427258e369d2582df22869138b4c5265f049f574c; // keccak256("lolik.LolikOracle.lastCompletedEpochId")
    bytes32 internal constant TIME_ELAPSED_POSITION =
        0x8fe323f4ecd3bf0497252a90142003855cc5125cee76a5b5ba5d508c7ec28c3a; // keccak256("lolik.LolikOracle.timeElapsed")

    /**
     * @notice Returns the Lolik contract address.
     */
    function getLolik() public view returns (address) {
        return LOLIK_POSITION.getStorageAddress();
    }

    /**
     * @notice Returns the accounting (new) oracle contract address.
     */
    function getAccountingOracle() public view returns (address) {
        return ACCOUNTING_ORACLE_POSITION.getStorageAddress();
    }

    ///
    /// Compatibility interface (DEPRECATED)
    ///

    /**
     * @notice Returns the initialized version of this contract starting from 0.
     */
    function getVersion() external view returns (uint256) {
        return getContractVersion();
    }

    /**
     * @notice DEPRECATED, kept for compatibility purposes only.
     *
     * Returns the Bahamut chain specification.
     */
    function getBeaconSpec()
        external
        view
        returns (
            uint64 epochsPerFrame,
            uint64 slotsPerEpoch,
            uint64 secondsPerSlot,
            uint64 genesisTime
        )
    {
        (, uint256 epochsPerFrame_) = _getAccountingConsensusContract().getFrameConfig();
        epochsPerFrame = uint64(epochsPerFrame_);

        ChainSpec memory spec = _getChainSpec();
        slotsPerEpoch = spec.slotsPerEpoch;
        secondsPerSlot = spec.secondsPerSlot;
        genesisTime = spec.genesisTime;
    }

    /**
     * @notice DEPRECATED, kept for compatibility purposes only.
     *
     * Returns the epoch calculated from current timestamp
     */
    function getCurrentEpochId() external view returns (uint256) {
        ChainSpec memory spec = _getChainSpec();
        // solhint-disable-line not-rely-on-time
        return (_getTime() - spec.genesisTime) / (spec.slotsPerEpoch * spec.secondsPerSlot);
    }

    /**
     * @notice DEPRECATED, kept for compatibility purposes only.
     *
     * Returns the first epoch of the current reporting frame as well as its start and end
     * times in seconds.
     */
    function getCurrentFrame()
        external
        view
        returns (
            uint256 frameEpochId,
            uint256 frameStartTime,
            uint256 frameEndTime
        )
    {
        return _getCurrentFrameFromAccountingOracle();
    }

    /**
     * @notice DEPRECATED, kept for compatibility purposes only.
     *
     * Returns the starting epoch of the last frame in which an oracle report was received
     * and applied.
     */
    function getLastCompletedEpochId() external view returns (uint256) {
        return LAST_COMPLETED_EPOCH_ID_POSITION.getStorageUint256();
    }

    /**
     * @notice DEPRECATED, kept for compatibility purposes only.
     *
     * The change of the protocol TVL that the last rebase resulted in. Notice that, during
     * a rebase, stFTN shares can be minted to distribute protocol fees and burnt to apply
     * cover for losses incurred by slashed or unresponsive validators. A rebase might be
     * triggered without changing the protocol TVL. Thus, it's impossible to correctly
     * calculate APR from the numbers returned by this function.
     *
     * See docs.lolik.fi for the correct way of onchain and offchain APR calculation.
     */
    function getLastCompletedReportDelta()
        external
        view
        returns (
            uint256 postTotalPooledFtn,
            uint256 preTotalPooledFtn,
            uint256 timeElapsed
        )
    {
        postTotalPooledFtn = FTNPOST_COMPLETED_TOTAL_POOLED_FTN_POSITION.getStorageUint256();
        preTotalPooledFtn = PRE_COMPLETED_TOTAL_POOLED_FTN_POSITION.getStorageUint256();
        timeElapsed = TIME_ELAPSED_POSITION.getStorageUint256();
    }

    ///
    /// Internal interface & implementation.
    ///

    /**
     * @notice Called by Lolik on each rebase.
     */
    function handlePostTokenRebase(
        uint256 /* reportTimestamp */,
        uint256 timeElapsed,
        uint256 /* preTotalShares */,
        uint256 preTotalFtn,
        uint256 postTotalShares,
        uint256 postTotalFtn,
        uint256 /* totalSharesMintedAsFees */
    )
        external
    {
        require(msg.sender == getLolik(), "SENDER_NOT_ALLOWED");

        PRE_COMPLETED_TOTAL_POOLED_FTN_POSITION.setStorageUint256(preTotalFtn);
        FTNPOST_COMPLETED_TOTAL_POOLED_FTN_POSITION.setStorageUint256(postTotalFtn);
        TIME_ELAPSED_POSITION.setStorageUint256(timeElapsed);

        emit PostTotalShares(postTotalFtn, preTotalFtn, timeElapsed, postTotalShares);
    }

    /**
     * @notice Called by the new accounting oracle on each report.
     */
    function handleConsensusLayerReport(uint256 _refSlot, uint256 _clBalance, uint256 _clValidators)
        external
    {
        require(msg.sender == getAccountingOracle(), "SENDER_NOT_ALLOWED");

        // new accounting oracle's ref. slot is the last slot of the epoch preceding the one the frame starts at
        uint256 epochId = (_refSlot + 1) / _getChainSpec().slotsPerEpoch;
        LAST_COMPLETED_EPOCH_ID_POSITION.setStorageUint256(epochId);

        emit Completed(epochId, uint128(_clBalance), uint128(_clValidators));
    }

    /**
     * @notice Initializes the contract (the compat-only deprecated version 4) from scratch.
     * @param _lolikLocator Address of the Lolik Locator contract.
     * @param _accountingOracleConsensusContract Address of consensus contract of the new accounting oracle contract.
     */
    function initialize(
        address _lolikLocator,
        address _accountingOracleConsensusContract
    ) external onlyInit {
        // Initializations for v0 --> v3
        _checkContractVersion(0);
        // deprecated version slot must be empty
        require(CONTRACT_VERSION_POSITION_DEPRECATED.getStorageUint256() == 0, "WRONG_BASE_VERSION");
        require(_lolikLocator != address(0), "ZERO_LOCATOR_ADDRESS");
        ILolikLocator locator = ILolikLocator(_lolikLocator);

        LOLIK_POSITION.setStorageAddress(locator.lolik());

        // Initializations for v3 --> v4
        _initialize_v4(locator.accountingOracle());

        // Cannot get consensus contract from new oracle because at this point new oracle is
        // not initialized with consensus contract address yet
        _setChainSpec(_getAccountingOracleChainSpec(_accountingOracleConsensusContract));

        // Needed to finish the Aragon part of initialization (otherwise auth() modifiers will fail)
        initialized();
    }

    /**
     * @notice A function to finalize upgrade v3 -> v4 (the compat-only deprecated impl).
     * Can be called only once.
     */
    function finalizeUpgrade_v4(address _accountingOracle) external {
        // deprecated version slot must be set to v3
        require(CONTRACT_VERSION_POSITION_DEPRECATED.getStorageUint256() == 3, "WRONG_BASE_VERSION");
        // current version slot must not be initialized yet
        _checkContractVersion(0);

        IHashConsensus consensus = IHashConsensus(IAccountingOracle(_accountingOracle).getConsensusContract());

        _initialize_v4(_accountingOracle);

        ChainSpec memory spec = _getChainSpec();
        ChainSpec memory newSpec = _getAccountingOracleChainSpec(consensus);

        require(
            spec.slotsPerEpoch == newSpec.slotsPerEpoch &&
            spec.secondsPerSlot == newSpec.secondsPerSlot &&
            spec.genesisTime == newSpec.genesisTime,
            "UNEXPECTED_CHAIN_SPEC"
        );
    }

    function _initialize_v4(address _accountingOracle) internal {
        require(_accountingOracle != address(0), "ZERO_ACCOUNTING_ORACLE_ADDRESS");
        ACCOUNTING_ORACLE_POSITION.setStorageAddress(_accountingOracle);
        // write current version slot
        _setContractVersion(4);
        // reset deprecated version slot
        CONTRACT_VERSION_POSITION_DEPRECATED.setStorageUint256(0);
    }

    function _getTime() internal view returns (uint256) {
        return block.timestamp; // solhint-disable-line not-rely-on-time
    }

    function _getChainSpec()
        internal
        view
        returns (ChainSpec memory chainSpec)
    {
        uint256 data = BEACON_SPEC_POSITION.getStorageUint256();
        chainSpec.epochsPerFrame = uint64(data >> 192);
        chainSpec.slotsPerEpoch = uint64(data >> 128);
        chainSpec.secondsPerSlot = uint64(data >> 64);
        chainSpec.genesisTime = uint64(data);
        return chainSpec;
    }

    function _setChainSpec(ChainSpec memory _chainSpec) internal {
        require(_chainSpec.slotsPerEpoch > 0, "BAD_SLOTS_PER_EPOCH");
        require(_chainSpec.secondsPerSlot > 0, "BAD_SECONDS_PER_SLOT");
        require(_chainSpec.genesisTime > 0, "BAD_GENESIS_TIME");
        require(_chainSpec.epochsPerFrame > 0, "BAD_EPOCHS_PER_FRAME");

        uint256 data = (
            uint256(_chainSpec.epochsPerFrame) << 192 |
            uint256(_chainSpec.slotsPerEpoch) << 128 |
            uint256(_chainSpec.secondsPerSlot) << 64 |
            uint256(_chainSpec.genesisTime)
        );

        BEACON_SPEC_POSITION.setStorageUint256(data);
    }

    function _getAccountingOracleChainSpec(address _accountingOracleConsensusContract)
        internal
        view
        returns (ChainSpec memory spec)
    {
        IHashConsensus consensus = IHashConsensus(_accountingOracleConsensusContract);
        (uint256 slotsPerEpoch, uint256 secondsPerSlot, uint256 genesisTime) = consensus.getChainConfig();
        (, uint256 epochsPerFrame_) = consensus.getFrameConfig();

        spec.epochsPerFrame = uint64(epochsPerFrame_);
        spec.slotsPerEpoch = uint64(slotsPerEpoch);
        spec.secondsPerSlot = uint64(secondsPerSlot);
        spec.genesisTime = uint64(genesisTime);
    }

    function _getCurrentFrameFromAccountingOracle()
        internal
        view
        returns (
            uint256 frameEpochId,
            uint256 frameStartTime,
            uint256 frameEndTime
        )
    {
        ChainSpec memory spec = _getChainSpec();
        IHashConsensus consensus = _getAccountingConsensusContract();
        uint256 refSlot;
        (refSlot,) =  consensus.getCurrentFrame();

        // new accounting oracle's ref. slot is the last slot of the epoch preceding the one the frame starts at
        frameStartTime = spec.genesisTime + (refSlot + 1) * spec.secondsPerSlot;
        // new accounting oracle's frame ends at the timestamp of the frame's last slot; old oracle's frame
        // ended a second before the timestamp of the first slot of the next frame
        frameEndTime = frameStartTime + spec.secondsPerSlot * spec.slotsPerEpoch * spec.epochsPerFrame - 1;
        frameEpochId = (refSlot + 1) / spec.slotsPerEpoch;
    }

    function _getAccountingConsensusContract() internal view returns (IHashConsensus) {
        return IHashConsensus(IAccountingOracle(getAccountingOracle()).getConsensusContract());
    }
}
          

openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol

pragma solidity ^0.4.24;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
contract IERC721Receiver {
  /**
   * @notice Handle the receipt of an NFT
   * @dev The ERC721 smart contract calls this function on the recipient
   * after a `safeTransfer`. This function MUST return the function selector,
   * otherwise the caller will revert the transaction. The selector to be
   * returned can be obtained as `this.onERC721Received.selector`. This
   * function MAY throw to revert and reject the transfer.
   * Note: the ERC721 contract address is always the message sender.
   * @param operator The address which called `safeTransferFrom` function
   * @param from The address which previously owned the token
   * @param tokenId The NFT identifier which is being transferred
   * @param data Additional data with no specified format
   * @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
   */
  function onERC721Received(
    address operator,
    address from,
    uint256 tokenId,
    bytes data
  )
    public
    returns(bytes4);
}
          

contracts/0.4.24/lib/StakeLimitUtils.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

/* See contracts/COMPILERS.md */
pragma solidity 0.4.24;

import "@aragon/os/contracts/common/UnstructuredStorage.sol";

//
// We need to pack four variables into the same 256bit-wide storage slot
// to lower the costs per each staking request.
//
// As a result, slot's memory aligned as follows:
//
// MSB ------------------------------------------------------------------------------> LSB
// 256____________160_________________________128_______________32_____________________ 0
// |_______________|___________________________|________________|_______________________|
// | maxStakeLimit | maxStakeLimitGrowthBlocks | prevStakeLimit | prevStakeBlockNumber  |
// |<-- 96 bits -->|<---------- 32 bits ------>|<-- 96 bits --->|<----- 32 bits ------->|
//
//
// NB: Internal representation conventions:
//
// - the `maxStakeLimitGrowthBlocks` field above represented as follows:
// `maxStakeLimitGrowthBlocks` = `maxStakeLimit` / `stakeLimitIncreasePerBlock`
//           32 bits                 96 bits               96 bits
//
//
// - the "staking paused" state is encoded by `prevStakeBlockNumber` being zero,
// - the "staking unlimited" state is encoded by `maxStakeLimit` being zero and `prevStakeBlockNumber` being non-zero.
//

/**
* @notice Library for the internal structs definitions
* @dev solidity <0.6 doesn't support top-level structs
* using the library to have a proper namespace
*/
library StakeLimitState {
    /**
      * @dev Internal representation struct (slot-wide)
      */
    struct Data {
        uint32 prevStakeBlockNumber;      // block number of the previous stake submit
        uint96 prevStakeLimit;            // limit value (<= `maxStakeLimit`) obtained on the previous stake submit
        uint32 maxStakeLimitGrowthBlocks; // limit regeneration speed expressed in blocks
        uint96 maxStakeLimit;             // maximum limit value
    }
}

library StakeLimitUnstructuredStorage {
    using UnstructuredStorage for bytes32;

    /// @dev Storage offset for `maxStakeLimit` (bits)
    uint256 internal constant MAX_STAKE_LIMIT_OFFSET = 160;
    /// @dev Storage offset for `maxStakeLimitGrowthBlocks` (bits)
    uint256 internal constant MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET = 128;
    /// @dev Storage offset for `prevStakeLimit` (bits)
    uint256 internal constant PREV_STAKE_LIMIT_OFFSET = 32;
    /// @dev Storage offset for `prevStakeBlockNumber` (bits)
    uint256 internal constant PREV_STAKE_BLOCK_NUMBER_OFFSET = 0;

    /**
    * @dev Read stake limit state from the unstructured storage position
    * @param _position storage offset
    */
    function getStorageStakeLimitStruct(bytes32 _position) internal view returns (StakeLimitState.Data memory stakeLimit) {
        uint256 slotValue = _position.getStorageUint256();

        stakeLimit.prevStakeBlockNumber = uint32(slotValue >> PREV_STAKE_BLOCK_NUMBER_OFFSET);
        stakeLimit.prevStakeLimit = uint96(slotValue >> PREV_STAKE_LIMIT_OFFSET);
        stakeLimit.maxStakeLimitGrowthBlocks = uint32(slotValue >> MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET);
        stakeLimit.maxStakeLimit = uint96(slotValue >> MAX_STAKE_LIMIT_OFFSET);
    }

     /**
    * @dev Write stake limit state to the unstructured storage position
    * @param _position storage offset
    * @param _data stake limit state structure instance
    */
    function setStorageStakeLimitStruct(bytes32 _position, StakeLimitState.Data memory _data) internal {
        _position.setStorageUint256(
            uint256(_data.prevStakeBlockNumber) << PREV_STAKE_BLOCK_NUMBER_OFFSET
                | uint256(_data.prevStakeLimit) << PREV_STAKE_LIMIT_OFFSET
                | uint256(_data.maxStakeLimitGrowthBlocks) << MAX_STAKE_LIMIT_GROWTH_BLOCKS_OFFSET
                | uint256(_data.maxStakeLimit) << MAX_STAKE_LIMIT_OFFSET
        );
    }
}

/**
* @notice Interface library with helper functions to deal with stake limit struct in a more high-level approach.
*/
library StakeLimitUtils {
    /**
    * @notice Calculate stake limit for the current block.
    * @dev using `_constGasMin` to make gas consumption independent of the current block number
    */
    function calculateCurrentStakeLimit(StakeLimitState.Data memory _data) internal view returns(uint256 limit) {
        uint256 stakeLimitIncPerBlock;
        if (_data.maxStakeLimitGrowthBlocks != 0) {
            stakeLimitIncPerBlock = _data.maxStakeLimit / _data.maxStakeLimitGrowthBlocks;
        }

        uint256 blocksPassed = block.number - _data.prevStakeBlockNumber;
        uint256 projectedLimit = _data.prevStakeLimit + blocksPassed * stakeLimitIncPerBlock;

        limit = _constGasMin(
            projectedLimit,
            _data.maxStakeLimit
        );
    }

    /**
    * @notice check if staking is on pause
    */
    function isStakingPaused(StakeLimitState.Data memory _data) internal pure returns(bool) {
        return _data.prevStakeBlockNumber == 0;
    }

    /**
    * @notice check if staking limit is set (otherwise staking is unlimited)
    */
    function isStakingLimitSet(StakeLimitState.Data memory _data) internal pure returns(bool) {
        return _data.maxStakeLimit != 0;
    }

    /**
    * @notice update stake limit repr with the desired limits
    * @dev input `_data` param is mutated and the func returns effectively the same pointer
    * @param _data stake limit state struct
    * @param _maxStakeLimit stake limit max value
    * @param _stakeLimitIncreasePerBlock stake limit increase (restoration) per block
    */
    function setStakingLimit(
        StakeLimitState.Data memory _data,
        uint256 _maxStakeLimit,
        uint256 _stakeLimitIncreasePerBlock
    ) internal view returns (StakeLimitState.Data memory) {
        require(_maxStakeLimit != 0, "ZERO_MAX_STAKE_LIMIT");
        require(_maxStakeLimit <= uint96(-1), "TOO_LARGE_MAX_STAKE_LIMIT");
        require(_maxStakeLimit >= _stakeLimitIncreasePerBlock, "TOO_LARGE_LIMIT_INCREASE");
        require(
            (_stakeLimitIncreasePerBlock == 0)
            || (_maxStakeLimit / _stakeLimitIncreasePerBlock <= uint32(-1)),
            "TOO_SMALL_LIMIT_INCREASE"
        );

        // reset prev stake limit to the new max stake limit if
        if (
            // staking was paused or
            _data.prevStakeBlockNumber == 0 ||
            // staking was unlimited or
            _data.maxStakeLimit == 0 ||
            // new maximum limit value is lower than the value obtained on the previous stake submit
            _maxStakeLimit < _data.prevStakeLimit
        ) {
            _data.prevStakeLimit = uint96(_maxStakeLimit);
        }
        _data.maxStakeLimitGrowthBlocks =
            _stakeLimitIncreasePerBlock != 0 ? uint32(_maxStakeLimit / _stakeLimitIncreasePerBlock) : 0;

        _data.maxStakeLimit = uint96(_maxStakeLimit);

        if (_data.prevStakeBlockNumber != 0) {
            _data.prevStakeBlockNumber = uint32(block.number);
        }

        return _data;
    }

    /**
    * @notice update stake limit repr to remove the limit
    * @dev input `_data` param is mutated and the func returns effectively the same pointer
    * @param _data stake limit state struct
    */
    function removeStakingLimit(
        StakeLimitState.Data memory _data
    ) internal pure returns (StakeLimitState.Data memory) {
        _data.maxStakeLimit = 0;

        return _data;
    }

    /**
    * @notice update stake limit repr after submitting user's ftn
    * @dev input `_data` param is mutated and the func returns effectively the same pointer
    * @param _data stake limit state struct
    * @param _newPrevStakeLimit new value for the `prevStakeLimit` field
    */
    function updatePrevStakeLimit(
        StakeLimitState.Data memory _data,
        uint256 _newPrevStakeLimit
    ) internal view returns (StakeLimitState.Data memory) {
        assert(_newPrevStakeLimit <= uint96(-1));
        assert(_data.prevStakeBlockNumber != 0);

        _data.prevStakeLimit = uint96(_newPrevStakeLimit);
        _data.prevStakeBlockNumber = uint32(block.number);

        return _data;
    }

    /**
    * @notice set stake limit pause state (on or off)
    * @dev input `_data` param is mutated and the func returns effectively the same pointer
    * @param _data stake limit state struct
    * @param _isPaused pause state flag
    */
    function setStakeLimitPauseState(
        StakeLimitState.Data memory _data,
        bool _isPaused
    ) internal view returns (StakeLimitState.Data memory) {
        _data.prevStakeBlockNumber = uint32(_isPaused ? 0 : block.number);

        return _data;
    }

    /**
     * @notice find a minimum of two numbers with a constant gas consumption
     * @dev doesn't use branching logic inside
     * @param _lhs left hand side value
     * @param _rhs right hand side value
     */
    function _constGasMin(uint256 _lhs, uint256 _rhs) internal pure returns (uint256 min) {
        uint256 lhsIsLess;
        assembly {
            lhsIsLess := lt(_lhs, _rhs) // lhsIsLess = (_lhs < _rhs) ? 1 : 0
        }
        min = (_lhs * lhsIsLess) + (_rhs * (1 - lhsIsLess));
    }
}
          

contracts/common/interfaces/IEIP712StFTN.sol

// SPDX-FileCopyrightText: 2023 OpenZeppelin, Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
// solhint-disable-next-line
pragma solidity 0.4.24;

/**
 * @dev Helper interface of EIP712 StFTN-dedicated helper.
 *
 * Has an access to the CHAIN_ID opcode and relies on immutables internally
 * Both are unavailable for Solidity 0.4.24.
 */
interface IEIP712StFTN {
    /**
     * @dev Returns the domain separator for the current chain.
     */
    function domainSeparatorV4(address _stFTN) external view returns (bytes32);

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function hashTypedDataV4(address _stFTN, bytes32 _structHash) external view returns (bytes32);

    /**
     * @dev returns the fields and values that describe the domain separator
     * used by stFTN for EIP-712 signature.
     */
    function eip712Domain(address _stFTN) external view returns (
        string memory name,
        string memory version,
        uint256 chainId,
        address verifyingContract
    );
}
          

contracts/0.4.24/test_helpers/LolikPushableMock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "../Lolik.sol";
import "./VaultMock.sol";

/**
 * @dev Mock for unit-testing handleOracleReport and how reward get calculated
 */
contract LolikPushableMock is Lolik {
    uint256 internal constant UNLIMITED_TOKEN_REBASE = uint256(-1);

    uint256 public totalRewards;
    bool public distributeFeeCalled;

    function initialize(address _lolikLocator) public onlyInit {
        LOLIK_LOCATOR_POSITION.setStorageAddress(_lolikLocator);
        _resume();
        initialized();
    }

    function setDepositedValidators(uint256 _depositedValidators) public {
        DEPOSITED_VALIDATORS_POSITION.setStorageUint256(_depositedValidators);
    }

    function setBeaconBalance(uint256 _beaconBalance) public {
        CL_BALANCE_POSITION.setStorageUint256(_beaconBalance);
    }

    // value sent to this function becomes buffered
    function setbufferedFtn() public payable {
        BUFFERED_FTN_POSITION.setStorageUint256(msg.value);
    }

    function setBeaconValidators(uint256 _beaconValidators) public {
        CL_VALIDATORS_POSITION.setStorageUint256(_beaconValidators);
    }

    function setTotalShares(uint256 _totalShares) public {
        TOTAL_SHARES_POSITION.setStorageUint256(_totalShares);
    }

    function resetDistributeFee() public {
        totalRewards = 0;
        distributeFeeCalled = false;
    }

    function getWithdrawalCredentials() external view returns (bytes32) {
        IStakingRouter stakingRouter = IStakingRouter(getLolikLocator().stakingRouter());

        if (address(stakingRouter) != address(0)) {
            return stakingRouter.getWithdrawalCredentials();
        }
        return bytes32(0);
    }

    function _distributeFee(uint256 _totalRewards) internal returns(uint256 sharesMintedAsFees) {
        totalRewards = _totalRewards;
        distributeFeeCalled = true;
    }
}
          

contracts/0.4.24/test_helpers/ERC20Mock.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";


/**
  * @dev This is a mock. Don't use in production.
  */
contract ERC20Mock is ERC20 {
    function mint(address account, uint256 value) public {
        _mint(account, value);
    }

    function burn(address account, uint256 value) public {
        _burn(account, value);
    }
}
          

@aragon/os/contracts/common/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);
    }
}
          

contracts/common/test_helpers/SignatureUtilsConsumer_0_4_24.sol

// SPDX-FileCopyrightText: 2023 Lolik <[email protected]>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.4.24;

import {SignatureUtils} from "../lib/SignatureUtils.sol";


contract SignatureUtilsConsumer_0_4_24 {

    function isValidSignature(
        address signer,
        bytes32 msgHash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external view returns (bool) {
        return SignatureUtils.isValidSignature(signer, msgHash, v, r, s);
    }

}
          

Compiler Settings

{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"constantinople"}
              

Contract ABI

[{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"resume","inputs":[],"constant":false},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"string","name":""}],"name":"name","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"stop","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"hasInitialized","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"approve","inputs":[{"type":"address","name":"_spender"},{"type":"uint256","name":"_amount"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getPooledFtnByShares","inputs":[{"type":"uint256","name":"_sharesAmount"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"STAKING_CONTROL_ROLE","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"totalSupply","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isStakingPaused","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getSharesByPooledFtn","inputs":[{"type":"uint256","name":"_ftnAmount"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"transferFrom","inputs":[{"type":"address","name":"_sender"},{"type":"address","name":"_recipient"},{"type":"uint256","name":"_amount"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getEVMScriptExecutor","inputs":[{"type":"bytes","name":"_script"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"setStakingLimit","inputs":[{"type":"uint256","name":"_maxStakeLimit"},{"type":"uint256","name":"_stakeLimitIncreasePerBlock"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"RESUME_ROLE","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"finalizeUpgrade_v2","inputs":[{"type":"address","name":"_lolikLocator"},{"type":"address","name":"_eip712StFTN"}],"constant":false},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"uint8","name":""}],"name":"decimals","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getRecoveryVault","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"DOMAIN_SEPARATOR","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"unsafeChangeDepositedValidators","inputs":[{"type":"uint256","name":"_newDepositedValidators"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"PAUSE_ROLE","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"increaseAllowance","inputs":[{"type":"address","name":"_spender"},{"type":"uint256","name":"_addedValue"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getTreasury","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isStopped","inputs":[],"constant":true},{"type":"function","stateMutability":"payable","payable":true,"outputs":[],"name":"initialize","inputs":[{"type":"address","name":"_lolikLocator"},{"type":"address","name":"_eip712StFTN"}],"constant":false},{"type":"function","stateMutability":"payable","payable":true,"outputs":[],"name":"receiveELRewards","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getBufferedFtn","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"getWithdrawalCredentials","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getCurrentStakeLimit","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":"isStakingPaused"},{"type":"bool","name":"isStakingLimitSet"},{"type":"uint256","name":"currentStakeLimit"},{"type":"uint256","name":"maxStakeLimit"},{"type":"uint256","name":"maxStakeLimitGrowthBlocks"},{"type":"uint256","name":"prevStakeLimit"},{"type":"uint256","name":"prevStakeBlockNumber"}],"name":"getStakeLimitFullInfo","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"transferSharesFrom","inputs":[{"type":"address","name":"_sender"},{"type":"address","name":"_recipient"},{"type":"uint256","name":"_sharesAmount"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"balanceOf","inputs":[{"type":"address","name":"_account"}],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"resumeStaking","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint16","name":"treasuryFeeBasisPoints"},{"type":"uint16","name":"insuranceFeeBasisPoints"},{"type":"uint16","name":"operatorsFeeBasisPoints"}],"name":"getFeeDistribution","inputs":[],"constant":true},{"type":"function","stateMutability":"payable","payable":true,"outputs":[],"name":"receiveWithdrawals","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"allowRecoverability","inputs":[{"type":"address","name":"token"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"nonces","inputs":[{"type":"address","name":"owner"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"appId","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getOracle","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"string","name":"name"},{"type":"string","name":"version"},{"type":"uint256","name":"chainId"},{"type":"address","name":"verifyingContract"}],"name":"eip712Domain","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getContractVersion","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getInitializationBlock","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"transferShares","inputs":[{"type":"address","name":"_recipient"},{"type":"uint256","name":"_sharesAmount"}],"constant":false},{"type":"function","stateMutability":"pure","payable":false,"outputs":[{"type":"string","name":""}],"name":"symbol","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferToVault","inputs":[{"type":"address","name":""}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"canPerform","inputs":[{"type":"address","name":"_sender"},{"type":"bytes32","name":"_role"},{"type":"uint256[]","name":"_params"}],"constant":true},{"type":"function","stateMutability":"payable","payable":true,"outputs":[{"type":"uint256","name":""}],"name":"submit","inputs":[{"type":"address","name":"_referral"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"_spender"},{"type":"uint256","name":"_subtractedValue"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getEVMScriptRegistry","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"transfer","inputs":[{"type":"address","name":"_recipient"},{"type":"uint256","name":"_amount"}],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"deposit","inputs":[{"type":"uint256","name":"_maxDepositsCount"},{"type":"uint256","name":"_stakingModuleId"},{"type":"bytes","name":"_depositCalldata"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"UNSAFE_CHANGE_DEPOSITED_VALIDATORS_ROLE","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":"depositedValidators"},{"type":"uint256","name":"beaconValidators"},{"type":"uint256","name":"beaconBalance"}],"name":"getBeaconStat","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"removeStakingLimit","inputs":[],"constant":false},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"uint256[4]","name":"postRebaseAmounts"}],"name":"handleOracleReport","inputs":[{"type":"uint256","name":"_reportTimestamp"},{"type":"uint256","name":"_timeElapsed"},{"type":"uint256","name":"_clValidators"},{"type":"uint256","name":"_clBalance"},{"type":"uint256","name":"_withdrawalVaultBalance"},{"type":"uint256","name":"_elRewardsVaultBalance"},{"type":"uint256","name":"_sharesRequestedToBurn"},{"type":"uint256[]","name":"_withdrawalFinalizationBatches"},{"type":"uint256","name":"_simulatedShareRate"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getTotalPooledFtn","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getLolikLocator","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint16","name":"totalFee"}],"name":"getFee","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getEIP712StFTN","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"kernel","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getTotalShares","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"permit","inputs":[{"type":"address","name":"_owner"},{"type":"address","name":"_spender"},{"type":"uint256","name":"_value"},{"type":"uint256","name":"_deadline"},{"type":"uint8","name":"_v"},{"type":"bytes32","name":"_r"},{"type":"bytes32","name":"_s"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"allowance","inputs":[{"type":"address","name":"_owner"},{"type":"address","name":"_spender"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isPetrified","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"canDeposit","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bytes32","name":""}],"name":"STAKING_PAUSE_ROLE","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"sharesOf","inputs":[{"type":"address","name":"_account"}],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getDepositableFtn","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"pauseStaking","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getTotalELRewardsCollected","inputs":[],"constant":true},{"type":"fallback","stateMutability":"payable","payable":true},{"type":"event","name":"StakingPaused","inputs":[],"anonymous":false},{"type":"event","name":"StakingResumed","inputs":[],"anonymous":false},{"type":"event","name":"StakingLimitSet","inputs":[{"type":"uint256","name":"maxStakeLimit","indexed":false},{"type":"uint256","name":"stakeLimitIncreasePerBlock","indexed":false}],"anonymous":false},{"type":"event","name":"StakingLimitRemoved","inputs":[],"anonymous":false},{"type":"event","name":"CLValidatorsUpdated","inputs":[{"type":"uint256","name":"reportTimestamp","indexed":true},{"type":"uint256","name":"preCLValidators","indexed":false},{"type":"uint256","name":"postCLValidators","indexed":false}],"anonymous":false},{"type":"event","name":"DepositedValidatorsChanged","inputs":[{"type":"uint256","name":"depositedValidators","indexed":false}],"anonymous":false},{"type":"event","name":"FTNDistributed","inputs":[{"type":"uint256","name":"reportTimestamp","indexed":true},{"type":"uint256","name":"preCLBalance","indexed":false},{"type":"uint256","name":"postCLBalance","indexed":false},{"type":"uint256","name":"withdrawalsWithdrawn","indexed":false},{"type":"uint256","name":"executionLayerRewardsWithdrawn","indexed":false},{"type":"uint256","name":"postBufferedFtn","indexed":false}],"anonymous":false},{"type":"event","name":"TokenRebased","inputs":[{"type":"uint256","name":"reportTimestamp","indexed":true},{"type":"uint256","name":"timeElapsed","indexed":false},{"type":"uint256","name":"preTotalShares","indexed":false},{"type":"uint256","name":"preTotalFtn","indexed":false},{"type":"uint256","name":"postTotalShares","indexed":false},{"type":"uint256","name":"postTotalFtn","indexed":false},{"type":"uint256","name":"sharesMintedAsFees","indexed":false}],"anonymous":false},{"type":"event","name":"LolikLocatorSet","inputs":[{"type":"address","name":"LolikLocator","indexed":false}],"anonymous":false},{"type":"event","name":"ELRewardsReceived","inputs":[{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawalsReceived","inputs":[{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"Submitted","inputs":[{"type":"address","name":"sender","indexed":true},{"type":"uint256","name":"amount","indexed":false},{"type":"address","name":"referral","indexed":false}],"anonymous":false},{"type":"event","name":"Unbuffered","inputs":[{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"ScriptResult","inputs":[{"type":"address","name":"executor","indexed":true},{"type":"bytes","name":"script","indexed":false},{"type":"bytes","name":"input","indexed":false},{"type":"bytes","name":"returnData","indexed":false}],"anonymous":false},{"type":"event","name":"RecoverToVault","inputs":[{"type":"address","name":"vault","indexed":true},{"type":"address","name":"token","indexed":true},{"type":"uint256","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"EIP712StFTNInitialized","inputs":[{"type":"address","name":"eip712StFTN","indexed":false}],"anonymous":false},{"type":"event","name":"TransferShares","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"sharesValue","indexed":false}],"anonymous":false},{"type":"event","name":"SharesBurnt","inputs":[{"type":"address","name":"account","indexed":true},{"type":"uint256","name":"preRebaseTokenAmount","indexed":false},{"type":"uint256","name":"postRebaseTokenAmount","indexed":false},{"type":"uint256","name":"sharesAmount","indexed":false}],"anonymous":false},{"type":"event","name":"Stopped","inputs":[],"anonymous":false},{"type":"event","name":"Resumed","inputs":[],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","indexed":true},{"type":"address","name":"spender","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false},{"type":"event","name":"ContractVersionSet","inputs":[{"type":"uint256","name":"version","indexed":false}],"anonymous":false}]
              

Contract Creation Code

0x60806040526200003e7f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a660001962000054602090811b6200387317901c565b6200004e6200005860201b60201c565b6200026b565b9055565b620000686200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a454400000000000000006020820152901562000144576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101562000108578181015183820152602001620000ee565b50505050905090810190601f168015620001365780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50620001586000196200018d60201b60201c565b565b60006200018860008051602062005dbf83398151915260001b600019166200026760201b620038bb1760201c565b905090565b6200019d6200015a60201b60201c565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156200023c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360008381101562000108578181015183820152602001620000ee565b506200026460008051602062005dbf8339815191528262000054602090811b6200387317901c565b50565b5490565b615b44806200027b6000396000f3006080604052600436106103105760003560e01c63ffffffff168063046f7da21461037357806306fdde031461038a57806307da68f5146104145780630803fac014610429578063095ea7b3146104525780630d48764e14610476578063136dd43c146104a057806318160ddd146104b55780631ea7ca89146104ca57806322cc5e94146104df57806323b872dd146104f75780632914b9bd146105215780632cb5f784146105965780632de03aa1146105b15780632f85e57c146105c6578063313ce567146105ed57806332f0a3b5146106185780633644e5151461062d5780633899862414610642578063389ed2671461065a578063395093511461066f5780633b19e84a146106935780633f683b6a146106a8578063485cc955146106bd5780634ad509b2146106d7578063532fbcd8146106df57806356396715146106f4578063609c4c6c14610709578063665b4b0b1461071e5780636d7804591461076d57806370a08231146107975780637475f913146107b8578063752f77f1146107cd57806378ffcfe2146108085780637e7db6e1146108105780637ecebe001461083157806380afdea814610852578063833b1fce1461086757806384b0196e1461087c5780638aa10435146109845780638b3dd749146109995780638fcb4e5b146109ae57806395d89b41146109d25780639d4941d8146109e7578063a1658fad14610a08578063a1903eab14610a6f578063a457c2d714610a83578063a479e50814610aa7578063a9059cbb14610abc578063aa0b7db714610ae0578063ad1394e914610b08578063ae2e353814610b1d578063b3320d9a14610b50578063bac3f3c514610b65578063bcb411d9146104b5578063c7de174014610bdd578063ced72f8714610bf2578063cf7e12af14610c1e578063d4aae0c414610c33578063d5002f2e14610c48578063d505accf14610c5d578063dd62ed3e14610c96578063de4796ed14610cbd578063e78a587514610cd2578063eb85262f14610ce7578063f5eb42dc14610cfc578063f974049114610d1d578063f999c50614610d32578063fa64ebac14610d47575b3615610366576040805160e560020a62461bcd02815260206004820152600e60248201527f4e4f4e5f454d5054595f44415441000000000000000000000000000000000000604482015290519081900360640190fd5b6103706000610d5c565b50005b34801561037f57600080fd5b50610388610f62565b005b34801561039657600080fd5b5061039f610f9d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156103d95781810151838201526020016103c1565b50505050905090810190601f1680156104065780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561042057600080fd5b50610388610fd4565b34801561043557600080fd5b5061043e61100d565b604080519115158252519081900360200190f35b34801561045e57600080fd5b5061043e600160a060020a0360043516602435611036565b34801561048257600080fd5b5061048e60043561104c565b60408051918252519081900360200190f35b3480156104ac57600080fd5b5061048e611085565b3480156104c157600080fd5b5061048e611097565b3480156104d657600080fd5b5061043e6110a6565b3480156104eb57600080fd5b5061048e6004356110c7565b34801561050357600080fd5b5061043e600160a060020a03600435811690602435166044356110df565b34801561052d57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261057a9436949293602493928401919081908401838280828437509497506111019650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156105a257600080fd5b506103886004356024356111e4565b3480156105bd57600080fd5b5061048e611266565b3480156105d257600080fd5b50610388600160a060020a036004358116906024351661128a565b3480156105f957600080fd5b50610602611421565b6040805160ff9092168252519081900360200190f35b34801561062457600080fd5b5061057a611426565b34801561063957600080fd5b5061048e61149b565b34801561064e57600080fd5b506103886004356114fc565b34801561066657600080fd5b5061048e611579565b34801561067b57600080fd5b5061043e600160a060020a036004351660243561159d565b34801561069f57600080fd5b5061057a6115de565b3480156106b457600080fd5b5061043e6115e8565b610388600160a060020a0360043581169060243516611607565b6103886116eb565b3480156106eb57600080fd5b5061048e6117de565b34801561070057600080fd5b5061048e6117e8565b34801561071557600080fd5b5061048e61182c565b34801561072a57600080fd5b5061073361184d565b6040805197151588529515156020880152868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b34801561077957600080fd5b5061048e600160a060020a03600435811690602435166044356118de565b3480156107a357600080fd5b5061048e600160a060020a036004351661191a565b3480156107c457600080fd5b5061038861192d565b3480156107d957600080fd5b506107e26119a2565b6040805161ffff9485168152928416602084015292168183015290519081900360600190f35b610388611b77565b34801561081c57600080fd5b5061043e600160a060020a0360043516611c2e565b34801561083d57600080fd5b5061048e600160a060020a0360043516611c34565b34801561085e57600080fd5b5061048e611c4f565b34801561087357600080fd5b5061057a611c7a565b34801561088857600080fd5b50610891611cbe565b60408051908101839052600160a060020a038216606082015260808082528551908201528451819060208083019160a084019189019080838360005b838110156108e55781810151838201526020016108cd565b50505050905090810190601f1680156109125780820380516001836020036101000a031916815260200191505b50838103825286518152865160209182019188019080838360005b8381101561094557818101518382015260200161092d565b50505050905090810190601f1680156109725780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561099057600080fd5b5061048e611e0d565b3480156109a557600080fd5b5061048e611e38565b3480156109ba57600080fd5b5061048e600160a060020a0360043516602435611e63565b3480156109de57600080fd5b5061039f611e93565b3480156109f357600080fd5b50610388600160a060020a0360043516611eca565b348015610a1457600080fd5b50604080516020600460443581810135838102808601850190965280855261043e958335600160a060020a0316956024803596369695606495939492019291829185019084908082843750949750611f1a9650505050505050565b61048e600160a060020a0360043516612065565b348015610a8f57600080fd5b5061043e600160a060020a0360043516602435612070565b348015610ab357600080fd5b5061057a612101565b348015610ac857600080fd5b5061043e600160a060020a03600435166024356121b6565b348015610aec57600080fd5b50610388600480359060248035916044359182019101356121c3565b348015610b1457600080fd5b5061048e612532565b348015610b2957600080fd5b50610b32612556565b60408051938452602084019290925282820152519081900360600190f35b348015610b5c57600080fd5b506103886125aa565b348015610b7157600080fd5b50610ba56004803590602480359160443591606435916084359160a4359160c4359160e4359081019101356101043561260e565b6040518082608080838360005b83811015610bca578181015183820152602001610bb2565b5050505090500191505060405180910390f35b348015610be957600080fd5b5061057a6126a2565b348015610bfe57600080fd5b50610c076126cd565b6040805161ffff9092168252519081900360200190f35b348015610c2a57600080fd5b5061057a612711565b348015610c3f57600080fd5b5061057a61273c565b348015610c5457600080fd5b5061048e612767565b348015610c6957600080fd5b50610388600160a060020a036004358116906024351660443560643560ff6084351660a43560c435612771565b348015610ca257600080fd5b5061048e600160a060020a03600435811690602435166129bc565b348015610cc957600080fd5b5061043e6129e7565b348015610cde57600080fd5b5061043e6129fa565b348015610cf357600080fd5b5061048e612a7a565b348015610d0857600080fd5b5061048e600160a060020a0360043516612a9e565b348015610d2957600080fd5b5061048e612aa9565b348015610d3e57600080fd5b50610388612b41565b348015610d5357600080fd5b5061048e612b6a565b6000610d66615950565b600080341515610dc0576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f4445504f5349540000000000000000000000000000000000000000604482015290519081900360640190fd5b610dd7600080516020615af9833981519152612b95565b9250610de283612be8565b15610e37576040805160e560020a62461bcd02815260206004820152600e60248201527f5354414b494e475f504155534544000000000000000000000000000000000000604482015290519081900360640190fd5b610e4083612bf3565b15610ed857610e4e83612c05565b915034821015610ea8576040805160e560020a62461bcd02815260206004820152600b60248201527f5354414b455f4c494d4954000000000000000000000000000000000000000000604482015290519081900360640190fd5b610ed8610ebd8434850363ffffffff612c9116565b600080516020615af98339815191529063ffffffff612cd516565b610ee1346110c7565b9050610eed3382612d30565b50610f0e610f0934610efd612e1a565b9063ffffffff612e4516565b612ed3565b60408051348152600160a060020a0387166020820152815133927f96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a928290030190a2610f5a3382612f06565b949350505050565b610f8b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7612f1b565b610f93612f8d565b610f9b612fdf565b565b60408051808201909152601781527f4c6971756964207374616b65642046617374746f6b656e000000000000000000602082015290565b610ffd7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d612f1b565b611005613035565b610f9b613087565b600080611018611e38565b9050801580159061103057508061102d6130d1565b10155b91505090565b60006110433384846130d5565b50600192915050565b600061107d6110596131f7565b611071611064613222565b859063ffffffff61325116565b9063ffffffff6132fc16565b90505b919050565b600080516020615a9983398151915281565b60006110a1613222565b905090565b60006110a16110c2600080516020615af9833981519152612b95565b612be8565b600061107d6110d4613222565b6110716110646131f7565b60006110ec84338461339c565b6110f7848484613436565b5060019392505050565b600061110b612101565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561116657818101518382015260200161114e565b50505050905090810190601f1680156111935780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b1580156111b257600080fd5b505af11580156111c6573d6000803e3d6000fd5b505050506040513d60208110156111dc57600080fd5b505192915050565b6111fb600080516020615a99833981519152612f1b565b611227610ebd838361121a600080516020615af9833981519152612b95565b919063ffffffff61345a16565b604080518381526020810183905281517fce9fddf6179affa1ea7bf36d80a6bf0284e0f3b91f4b2fa6eea2af923e7fac2d929181900390910190a15050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b611294600061367e565b61129c61100d565b15156112f2576040805160e560020a62461bcd02815260206004820152600f60248201527f4e4f545f494e495449414c495a45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0382161515611352576040805160e560020a62461bcd02815260206004820152601a60248201527f4c4f4c494b5f4c4f4341544f525f5a45524f5f41444452455353000000000000604482015290519081900360640190fd5b600160a060020a03811615156113b2576040805160e560020a62461bcd02815260206004820152601960248201527f4549503731325f535446544e5f5a45524f5f4144445245535300000000000000604482015290519081900360640190fd5b6113bd61dead6136dc565b1515611413576040805160e560020a62461bcd02815260206004820152601560248201527f494e495449414c5f484f4c4445525f4558495354530000000000000000000000604482015290519081900360640190fd5b61141d82826136f7565b5050565b601290565b600061143061273c565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b505af115801561147e573d6000803e3d6000fd5b505050506040513d602081101561149457600080fd5b5051905090565b60006114a5612711565b600160a060020a031663b8f120b3306040518263ffffffff1660e01b81526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561146a57600080fd5b6115257fe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c612f1b565b611543600080516020615a798339815191528263ffffffff61387316565b6040805182815290517fe0aacfc334457703148118055ec794ac17654c6f918d29638ba3b18003cee5ff9181900360200190a150565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b336000818152600160209081526040808320600160a060020a038716845290915281205490916110439185906115d9908663ffffffff612e4516565b6130d5565b60006110a1613877565b6000611601600080516020615a598339815191526138bb565b15905090565b61160f611e38565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156116d05760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561169557818101518382015260200161167d565b50505050905090810190601f1680156116c25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506116d96138bf565b6116e382826136f7565b61141d613926565b6116f36126a2565b600160a060020a031663e441d25f6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561172d57600080fd5b505af1158015611741573d6000803e3d6000fd5b505050506040513d602081101561175757600080fd5b5051600160a060020a0316331461176d57600080fd5b6117a961177c34610efd612b6a565b7fafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb9063ffffffff61387316565b6040805134815290517fd27f9b0c98bdee27044afa149eadcd2047d6399cb6613a45c5b87e6aca76e6b59181900360200190a1565b60006110a1612e1a565b60006117f26139ec565b600160a060020a031663563967156040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b60006110a1611848600080516020615af9833981519152612b95565b613a30565b6000806000806000806000611860615950565b611877600080516020615af9833981519152612b95565b905061188281612be8565b975061188d81612bf3565b965061189881613a30565b955080606001516001606060020a03169450806040015163ffffffff16935080602001516001606060020a03169250806000015163ffffffff1691505090919293949596565b6000806118ea8361104c565b90506118f785338361339c565b611902858585613a69565b61190e85858386613c6d565b8091505b509392505050565b600061107d611928836136dc565b61104c565b611944600080516020615a99833981519152612f1b565b61194c61100d565b1515610f93576040805160e560020a62461bcd02815260206004820152600f60248201527f4e4f545f494e495449414c495a45440000000000000000000000000000000000604482015290519081900360640190fd5b6000806000806000806000806119b66139ec565b945084600160a060020a031663271662ec6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156119f357600080fd5b505af1158015611a07573d6000803e3d6000fd5b505050506040513d6020811015611a1d57600080fd5b5051604080517f9fbb7bae0000000000000000000000000000000000000000000000000000000081529051919550600160a060020a03871691639fbb7bae916004808201926020929091908290030181600087803b158015611a7e57600080fd5b505af1158015611a92573d6000803e3d6000fd5b505050506040513d6020811015611aa857600080fd5b5051604080517fefcdcc0e000000000000000000000000000000000000000000000000000000008152815161ffff9093169550600160a060020a0388169263efcdcc0e926004808401939192918290030181600087803b158015611b0b57600080fd5b505af1158015611b1f573d6000803e3d6000fd5b505050506040513d6040811015611b3557600080fd5b5080516020909101516000985061ffff918216935016905082848302811515611b5a57fe5b04975082848202811515611b6a57fe5b0495505050505050909192565b611b7f6126a2565b600160a060020a03166369d421486040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611bb957600080fd5b505af1158015611bcd573d6000803e3d6000fd5b505050506040513d6020811015611be357600080fd5b5051600160a060020a03163314611bf957600080fd5b6040805134815290517f6e5086f7e1ab04bd826e77faae35b1bcfe31bd144623361a40ea4af51670b1c39181900360200190a1565b50600190565b600160a060020a031660009081526002602052604090205490565b60006110a17fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b6138bb565b6000611c846126a2565b600160a060020a031663996107aa6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b606080600080611ccc612711565b600160a060020a031663f4409319306040518263ffffffff1660e01b81526004018082600160a060020a0316600160a060020a03168152602001915050600060405180830381600087803b158015611d2357600080fd5b505af1158015611d37573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611d6057600080fd5b810190808051640100000000811115611d7857600080fd5b82016020810184811115611d8b57600080fd5b8151640100000000811182820187101715611da557600080fd5b50509291906020018051640100000000811115611dc157600080fd5b82016020810184811115611dd457600080fd5b8151640100000000811182820187101715611dee57600080fd5b5050602082015160409092015194985096509450919250505090919293565b60006110a17f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a66138bb565b60006110a17febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e6138bb565b600080611e71338585613a69565b611e7a8361104c565b9050611e8833858386613c6d565b8091505b5092915050565b60408051808201909152600581527f737446544e000000000000000000000000000000000000000000000000000000602082015290565b6040805160e560020a62461bcd02815260206004820152600d60248201527f4e4f545f535550504f5254454400000000000000000000000000000000000000604482015290519081900360640190fd5b600080611f2561100d565b1515611f345760009150611912565b611f3c61273c565b9050600160a060020a0381161515611f575760009150611912565b80600160a060020a031663fdef9106863087611f7288613d0d565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b83811015611fe1578181015183820152602001611fc9565b50505050905090810190601f16801561200e5780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561203057600080fd5b505af1158015612044573d6000803e3d6000fd5b505050506040513d602081101561205a57600080fd5b505195945050505050565b600061107d82610d5c565b336000908152600160209081526040808320600160a060020a0386168452909152812054828110156120ec576040805160e560020a62461bcd02815260206004820152601460248201527f414c4c4f57414e43455f42454c4f575f5a45524f000000000000000000000000604482015290519081900360640190fd5b6110f733856115d9848763ffffffff613d1716565b60008061210c61273c565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b1580156111b257600080fd5b6000611043338484613436565b60008060008060006121d36126a2565b945084600160a060020a031663472c17766040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561221057600080fd5b505af1158015612224573d6000803e3d6000fd5b505050506040513d602081101561223a57600080fd5b5051600160a060020a0316331461229b576040805160e560020a62461bcd02815260206004820152601360248201527f4150505f415554485f44534d5f4641494c454400000000000000000000000000604482015290519081900360640190fd5b6122a36129fa565b15156122f9576040805160e560020a62461bcd02815260206004820152600f60248201527f43414e5f4e4f545f4445504f5349540000000000000000000000000000000000604482015290519081900360640190fd5b6123016139ec565b935061238d8985600160a060020a03166319c64b798b61231f612aa9565b6040518363ffffffff1660e01b81526004018083815260200182815260200192505050602060405180830381600087803b15801561235c57600080fd5b505af1158015612370573d6000803e3d6000fd5b505050506040513d602081101561238657600080fd5b5051613dab565b9250600083111561249e576123b2836901bc16d674ec8000000063ffffffff61325116565b91506123fc6123cf836123c3612e1a565b9063ffffffff613d1716565b7fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b09063ffffffff61387316565b6040805183815290517f76a397bea5768d4fca97ef47792796e35f98dc81b16c1de84e28a818e1f971089181900360200190a161244a83610efd600080516020615a798339815191526138bb565b905061246a600080516020615a798339815191528263ffffffff61387316565b6040805182815290517fe0aacfc334457703148118055ec794ac17654c6f918d29638ba3b18003cee5ff9181900360200190a15b83600160a060020a031663aa0b7db783858b8b8b6040518663ffffffff1660e01b8152600401808581526020018481526020018060200182810382528484828181526020019250808284378201915050955050505050506000604051808303818588803b15801561250e57600080fd5b505af1158015612522573d6000803e3d6000fd5b5050505050505050505050505050565b7fe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c81565b60008080612571600080516020615a798339815191526138bb565b925061258a600080516020615ab98339815191526138bb565b91506125a3600080516020615ad98339815191526138bb565b9050909192565b6125c1600080516020615a99833981519152612f1b565b6125e3610ebd6125de600080516020615af9833981519152612b95565b613dc3565b6040517f9b2a687c198898fcc32a33bbc610d478f177a73ab7352023e6cc1de5bf12a3df90600090a1565b612616615977565b61261e613dd6565b612693610120604051908101604052808d81526020018c81526020018b81526020018a8152602001898152602001888152602001878152602001868680806020026020016040519081016040528093929190818152602001838360200280828437505050928452505050602001849052613e43565b9b9a5050505050505050505050565b60006110a17f9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df76138bb565b60006126d76139ec565b600160a060020a0316639fbb7bae6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b60006110a17f42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c6138bb565b60006110a17f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b6138bb565b60006110a16131f7565b600080428610156127cc576040805160e560020a62461bcd02815260206004820152601060248201527f444541444c494e455f4558504952454400000000000000000000000000000000604482015290519081900360640190fd5b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98989896127f983614346565b60408051602080820197909752600160a060020a0395861681830152939094166060840152608083019190915260a082015260c08082018a90528251808303909101815260e090910191829052805190928291908401908083835b602083106128735780518252601f199092019160209182019101612854565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902091506128aa612711565b604080517f804e5eb3000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a03929092169163804e5eb3916044808201926020929091908290030181600087803b15801561291557600080fd5b505af1158015612929573d6000803e3d6000fd5b505050506040513d602081101561293f57600080fd5b505190506129508982878787614390565b15156129a6576040805160e560020a62461bcd02815260206004820152601160248201527f494e56414c49445f5349474e4154555245000000000000000000000000000000604482015290519081900360640190fd5b6129b18989896130d5565b505050505050505050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b60006000196129f4611e38565b14905090565b6000612a04614555565b600160a060020a0316632b95b7816040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612a3e57600080fd5b505af1158015612a52573d6000803e3d6000fd5b505050506040513d6020811015612a6857600080fd5b50511580156110a157506116016115e8565b7f84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de881565b600061107d826136dc565b6000806000612ab6612e1a565b9150612ac0614555565b600160a060020a0316634388484d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612afa57600080fd5b505af1158015612b0e573d6000803e3d6000fd5b505050506040513d6020811015612b2457600080fd5b50519050808211612b36576000612b3a565b8082035b9250505090565b6110057f84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de8612f1b565b60006110a17fafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb6138bb565b612b9d615950565b6000612ba8836138bb565b63ffffffff600082901c811684526001606060020a03602083811c821690860152608083901c909116604085015260a09190911c16606083015250919050565b5163ffffffff161590565b606001516001606060020a0316151590565b600080600080846040015163ffffffff166000141515612c4e57846040015163ffffffff1685606001516001606060020a0316811515612c4157fe5b046001606060020a031692505b846000015163ffffffff164303915082820285602001516001606060020a0316019050612c888186606001516001606060020a0316614599565b95945050505050565b612c99615950565b6001606060020a03821115612caa57fe5b825163ffffffff161515612cba57fe5b506001606060020a031660208201524363ffffffff16815290565b61141d60a082606001516001606060020a0316901b6080836040015163ffffffff16901b602084602001516001606060020a0316901b6000856000015163ffffffff16901b171717836000191661387390919063ffffffff16565b6000600160a060020a0383161515612d92576040805160e560020a62461bcd02815260206004820152601160248201527f4d494e545f544f5f5a45524f5f41444452000000000000000000000000000000604482015290519081900360640190fd5b612d9e82610efd6131f7565b9050612dd07fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e8263ffffffff61387316565b600160a060020a038316600090815260208190526040902054612df9908363ffffffff612e4516565b600160a060020a039093166000908152602081905260409020929092555090565b60006110a17fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b06138bb565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156119125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b612f037fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b08263ffffffff61387316565b50565b61141d600083612f158461104c565b84613c6d565b604080516000815260208101909152612f379033908390611f1a565b1515612f03576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b612f956145aa565b612fb4600080516020615a59833981519152600163ffffffff61387316565b6040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f990600090a1565b61300a610ebd6000612ffe600080516020615af9833981519152612b95565b9063ffffffff61461616565b6040517fedaeeae9aed70c4545d3ab0065713261c9cee8d6cf5c8b07f52f0a65fd91efda90600090a1565b61303d613dd6565b61305c600080516020615a59833981519152600063ffffffff61387316565b6040517f7acc84e34091ae817647a4c49116f5cc07f319078ba80f8f5fde37ea7e25cbd690600090a1565b6130a6610ebd6001612ffe600080516020615af9833981519152612b95565b6040517f26d1807b479eaba249c1214b82e4b65bbb0cc73ee8a17901324b1ef1b5904e4990600090a1565b4390565b600160a060020a0383161515613135576040805160e560020a62461bcd02815260206004820152601660248201527f415050524f56455f46524f4d5f5a45524f5f4144445200000000000000000000604482015290519081900360640190fd5b600160a060020a0382161515613195576040805160e560020a62461bcd02815260206004820152601460248201527f415050524f56455f544f5f5a45524f5f41444452000000000000000000000000604482015290519081900360640190fd5b600160a060020a03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b60006110a17fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e6138bb565b60006110a161322f61463b565b610efd613249600080516020615ad98339815191526138bb565b610efd612e1a565b6000808315156132645760009150611e8c565b5082820282848281151561327457fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146119125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b60408051808201909152600d81527f4d4154485f4449565f5a45524f00000000000000000000000000000000000000602082015260009081908184116133875760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b50828481151561339357fe5b04949350505050565b600160a060020a0380841660009081526001602090815260408083209386168352929052205460001981146134305781811015613423576040805160e560020a62461bcd02815260206004820152601260248201527f414c4c4f57414e43455f45584345454445440000000000000000000000000000604482015290519081900360640190fd5b61343084848484036130d5565b50505050565b6000613441826110c7565b905061344e848483613a69565b61343084848484613c6d565b613462615950565b8215156134b9576040805160e560020a62461bcd02815260206004820152601460248201527f5a45524f5f4d41585f5354414b455f4c494d4954000000000000000000000000604482015290519081900360640190fd5b6001606060020a03831115613518576040805160e560020a62461bcd02815260206004820152601960248201527f544f4f5f4c415247455f4d41585f5354414b455f4c494d495400000000000000604482015290519081900360640190fd5b81831015613570576040805160e560020a62461bcd02815260206004820152601860248201527f544f4f5f4c415247455f4c494d49545f494e4352454153450000000000000000604482015290519081900360640190fd5b81158061358c575063ffffffff828481151561358857fe5b0411155b15156135e2576040805160e560020a62461bcd02815260206004820152601860248201527f544f4f5f534d414c4c5f4c494d49545f494e4352454153450000000000000000604482015290519081900360640190fd5b835163ffffffff161580613601575060608401516001606060020a0316155b80613618575083602001516001606060020a031683105b1561362d576001606060020a03831660208501525b81151561363b576000613648565b818381151561364657fe5b045b63ffffffff90811660408601526001606060020a0384166060860152845116156136765763ffffffff431684525b509192915050565b613686611e0d565b8114612f03576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b600160a060020a031660009081526020819052604090205490565b6137016002614697565b6137317f9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df78363ffffffff61387316565b61373a816146fd565b61383382600160a060020a03166337d5fe996040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561377857600080fd5b505af115801561378c573d6000803e3d6000fd5b505050506040513d60208110156137a257600080fd5b5051604080517f27810b6e0000000000000000000000000000000000000000000000000000000081529051600160a060020a038616916327810b6e9160048083019260209291908290030181600087803b1580156137ff57600080fd5b505af1158015613813573d6000803e3d6000fd5b505050506040513d602081101561382957600080fd5b50516000196130d5565b60408051600160a060020a038416815290517f66a24487f11bf1960b5e3706756c6b00545a8f3ad8e0ec08a4ae7934288a21ca9181900360200190a15050565b9055565b60006138816126a2565b600160a060020a03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b5490565b30318015156138ca57fe5b6138d26131f7565b1515612f03576138e181612ed3565b6040805182815260006020820152815161dead927f96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a928290030190a2612f0381614834565b61392e611e38565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156139b35760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b50610f9b6139bf6130d1565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff61387316565b60006139f66126a2565b600160a060020a031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b6000613a3b82612be8565b15613a4857506000611080565b613a5182612bf3565b1515613a605750600019611080565b61107d82612c05565b6000600160a060020a0384161515613acb576040805160e560020a62461bcd02815260206004820152601760248201527f5452414e534645525f46524f4d5f5a45524f5f41444452000000000000000000604482015290519081900360640190fd5b600160a060020a0383161515613b2b576040805160e560020a62461bcd02815260206004820152601560248201527f5452414e534645525f544f5f5a45524f5f414444520000000000000000000000604482015290519081900360640190fd5b600160a060020a038316301415613b8c576040805160e560020a62461bcd02815260206004820152601a60248201527f5452414e534645525f544f5f535446544e5f434f4e5452414354000000000000604482015290519081900360640190fd5b613b94613dd6565b50600160a060020a03831660009081526020819052604090205480821115613c06576040805160e560020a62461bcd02815260206004820152601060248201527f42414c414e43455f455843454544454400000000000000000000000000000000604482015290519081900360640190fd5b613c16818363ffffffff613d1716565b600160a060020a038086166000908152602081905260408082209390935590851681522054613c4b908363ffffffff612e4516565b600160a060020a03909316600090815260208190526040902092909255505050565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a382600160a060020a031684600160a060020a03167f9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb836040518082815260200191505060405180910390a350505050565b5490565b8051602002815290565b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613da35760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b505050900390565b6000818310613dba5781613dbc565b825b9392505050565b613dcb615950565b506000606082015290565b613ded600080516020615a598339815191526138bb565b1515610f9b576040805160e560020a62461bcd02815260206004820152601360248201527f434f4e54524143545f49535f53544f5050454400000000000000000000000000604482015290519081900360640190fd5b613e4b615977565b613e53615996565b613e5b6159d2565b600080600080613e6961484d565b8051909650600160a060020a03163314613ecd576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b8751421015613f26576040805160e560020a62461bcd02815260206004820152601860248201527f494e56414c49445f5245504f52545f54494d455354414d500000000000000000604482015290519081900360640190fd5b613f2e613222565b6040860152613f3b6131f7565b6060860152613f57600080516020615ab98339815191526138bb565b808652885160408a015160608b0151613f7193919061491e565b6020860152613f81868987614abc565b60e0880151511561403257613f968689614b7b565b60a08701819052608087019190915260001015614032578560600151600160a060020a0316634611492887608001518760a001516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561401957600080fd5b505af115801561402d573d6000803e3d6000fd5b505050505b8560400151600160a060020a031663b8498a398660400151876060015188602001518c606001518d608001518e60a001518f60c001518d608001518e60a001516040518a63ffffffff1660e01b8152600401808a81526020018981526020018881526020018781526020018681526020018581526020018481526020018381526020018281526020019950505050505050505050608060405180830381600087803b1580156140e057600080fd5b505af11580156140f4573d6000803e3d6000fd5b505050506040513d608081101561410a57600080fd5b5080516020820151604083015160609093015160e0808a019190915260c0890193909352918a01516101008b01516080890151929750929550614154928992889288929091614d5a565b87600001517f89e36cef94d4462d02672198dd9e1388f7cb6b21474a0449465d192e687b397486602001518a60600151878761418e612e1a565b6040805195865260208601949094528484019290925260608401526080830152519081900360a00190a260008560e00151111561423f578560600151600160a060020a031663636e6b668660e001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561421357600080fd5b505af1158015614227573d6000803e3d6000fd5b5050505061423d86606001518660e00151614f05565b505b61424f85896060015186866150af565b61010086015260c0860151614267908990879061510c565b60e08a01515191935091501561431d578560400151600160a060020a03166363e56b9f828488608001516142ac8a60c001518b60e00151613d1790919063ffffffff16565b8d61010001516040518663ffffffff1660e01b81526004018086815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b15801561430457600080fd5b505af1158015614318573d6000803e3d6000fd5b505050505b604080516080810182529182526020820192909252908101929092526060820152949350505050565b600160a060020a03811660009081526002602052604090205461437081600163ffffffff612e4516565b600160a060020a0390921660009081526002602052604090209190915590565b600060608060006143a08961524c565b156145265760408051602080820189905281830188905260ff8a1660f81b606083015282516041818403018152606183018452608583018c815260a58401948552815160c585015281519197507f1626ba7e00000000000000000000000000000000000000000000000000000000948d9489949293919260e5909101919085019080838360005b8381101561443f578181015183820152602001614427565b50505050905090810190601f16801561446c5780820380516001836020036101000a031916815260200191505b509350505050604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509150604051602081016040526020818451602086018d5afa60203d146001821416156144fa57815192505b50507f1626ba7e0000000000000000000000000000000000000000000000000000000081149350614549565b88600160a060020a031661453c89898989615254565b600160a060020a03161493505b50505095945050505050565b600061455f6126a2565b600160a060020a03166337d5fe996040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b808210918202600192909203020190565b6145c1600080516020615a598339815191526138bb565b15610f9b576040805160e560020a62461bcd02815260206004820152601260248201527f434f4e54524143545f49535f4143544956450000000000000000000000000000604482015290519081900360640190fd5b61461e615950565b81614629574361462c565b60005b63ffffffff1683525090919050565b60008080614656600080516020615a798339815191526138bb565b915061466f600080516020615ab98339815191526138bb565b90508082101561467b57fe5b612b3a8183036901bc16d674ec8000000063ffffffff61325116565b6146c77f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff61387316565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b600160a060020a038116151561475d576040805160e560020a62461bcd02815260206004820152601060248201527f5a45524f5f454950373132535446544e00000000000000000000000000000000604482015290519081900360640190fd5b6000614767612711565b600160a060020a0316146147c5576040805160e560020a62461bcd02815260206004820152601760248201527f454950373132535446544e5f414c52454144595f534554000000000000000000604482015290519081900360640190fd5b6147f57f42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c8263ffffffff61387316565b60408051600160a060020a038316815290517f410358095ccd195bc4f9aa535a730aeb19f5588ce07e9dfcee5de815241b8cc49181900360200190a150565b61484061dead82612d30565b50612f0361dead82612f06565b614855615996565b61485d6126a2565b600160a060020a031663e1078b8c6040518163ffffffff1660e01b815260040160e060405180830381600087803b15801561489757600080fd5b505af11580156148ab573d6000803e3d6000fd5b505050506040513d60e08110156148c157600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c09b8c0151600160a060020a039081169c8e019c909c528b16908c01528916908a015287169088015285169086015283169084015216815290565b60008080614939600080516020615a798339815191526138bb565b915081851115614993576040805160e560020a62461bcd02815260206004820152601760248201527f5245504f525445445f4d4f52455f4445504f5349544544000000000000000000604482015290519081900360640190fd5b858510156149eb576040805160e560020a62461bcd02815260206004820152601860248201527f5245504f525445445f4c4553535f56414c494441544f52530000000000000000604482015290519081900360640190fd5b85851115614a1157614a11600080516020615ab98339815191528663ffffffff61387316565b50848403614a2c600080516020615ad98339815191526138bb565b9250614a58614a4b826901bc16d674ec8000000063ffffffff61325116565b849063ffffffff612e4516565b9250614a78600080516020615ad98339815191528563ffffffff61387316565b6040805187815260208101879052815189927f1252331d4f3ee8a9f0a3484c4c2fb059c70a047b5dc5482a3ee6415f742d9f2e928290030190a25050949350505050565b8260400151600160a060020a0316638024cca183602001518360200151856060015186608001518760a001518860c0015188600001518a604001516040518963ffffffff1660e01b81526004018089815260200188815260200187815260200186815260200185815260200184815260200183815260200182815260200198505050505050505050600060405180830381600087803b158015614b5e57600080fd5b505af1158015614b72573d6000803e3d6000fd5b50505050505050565b60008060008460800151905080600160a060020a031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614bc257600080fd5b505af1158015614bd6573d6000803e3d6000fd5b505050506040513d6020811015614bec57600080fd5b50511515614d52578460400151600160a060020a0316636a84f2fd8560e0015160018760e001515103815181101515614c2157fe5b9060200190602002015186600001516040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015614c6d57600080fd5b505af1158015614c81573d6000803e3d6000fd5b5050505080600160a060020a031663a52e9c9f8560e001518661010001516040518363ffffffff1660e01b81526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b83811015614cf4578181015183820152602001614cdc565b5050505090500193505050506040805180830381600087803b158015614d1957600080fd5b505af1158015614d2d573d6000803e3d6000fd5b505050506040513d6040811015614d4357600080fd5b50805160209091015190935091505b509250929050565b6000806000861115614ddd578760200151600160a060020a0316639342c8f4876040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015614db057600080fd5b505af1158015614dc4573d6000803e3d6000fd5b505050506040513d6020811015614dda57600080fd5b50505b6000871115614e49578760a00151600160a060020a0316633194528a886040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015614e3057600080fd5b505af1158015614e44573d6000803e3d6000fd5b505050505b6000831115614edc578760800151915081600160a060020a031663b6013cef84876001895103815181101515614e7b57fe5b90602001906020020151876040518463ffffffff1660e01b815260040180838152602001828152602001925050506000604051808303818588803b158015614ec257600080fd5b505af1158015614ed6573d6000803e3d6000fd5b50505050505b614ef0836123c389610efd8a610efd612e1a565b9050614efb81612ed3565b5050505050505050565b6000808080600160a060020a0386161515614f6a576040805160e560020a62461bcd02815260206004820152601360248201527f4255524e5f46524f4d5f5a45524f5f4144445200000000000000000000000000604482015290519081900360640190fd5b600160a060020a038616600090815260208190526040902054925082851115614fdd576040805160e560020a62461bcd02815260206004820152601060248201527f42414c414e43455f455843454544454400000000000000000000000000000000604482015290519081900360640190fd5b614fe68561104c565b9150614ff4856123c36131f7565b93506150267fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e8563ffffffff61387316565b615036838663ffffffff613d1716565b600160a060020a0387166000908152602081905260409020556150588561104c565b60408051848152602081018390528082018890529051919250600160a060020a038816917f8b2a1e1ad5e0578c3dd82494156e985dade827a87c573b5c1c7716a32162ad649181900360600190a250505092915050565b600080806150c3868663ffffffff612e4516565b91508660200151821115615102578660200151820390506150ff876040015188606001516150fa8785612e4590919063ffffffff16565b6153be565b92505b5050949350505050565b6000806151176131f7565b9150615121613222565b9050600160a060020a038316156151d65782600160a060020a03166389136ec0866000015187602001518760600151886040015187878b61010001516040518863ffffffff1660e01b815260040180888152602001878152602001868152602001858152602001848152602001838152602001828152602001975050505050505050600060405180830381600087803b1580156151bd57600080fd5b505af11580156151d1573d6000803e3d6000fd5b505050505b84516020808701516060808801516040808a01516101008b0151825195865295850192909252838101919091529082018690526080820185905260a082019290925290517fff08c3ef606d198e316ef5b822193c489965899eb4e3c248cea1a4626c3eda509181900360c00190a2935093915050565b6000903b1190565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083821c11156152f7576040805160e560020a62461bcd02815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60408051600080825260208083018085528a905260ff8916838501526060830188905260808301879052925160019360a0808501949193601f19840193928390039091019190865af1158015615351573d6000803e3d6000fd5b5050604051601f190151915050600160a060020a0381161515612c88576040805160e560020a62461bcd02815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015290519081900360640190fd5b60006153c8615a1f565b600080606060006153d76155ad565b94509450600085606001516001606060020a031611156155a157615401898863ffffffff612e4516565b925061546f61543f61542987606001516001606060020a03168a61325190919063ffffffff16565b60808801516123c390879063ffffffff61325116565b6110718a61546389606001516001606060020a03168c61325190919063ffffffff16565b9063ffffffff61325116565b955061547b3087612d30565b5061549d8560000151866040015187606001516001606060020a031689615809565b90925090506154ba6154b5878363ffffffff613d1716565b61592f565b83600160a060020a031663af1240978660200151846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561552257818101518382015260200161550a565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015615561578181015183820152602001615549565b50505050905001945050505050600060405180830381600087803b15801561558857600080fd5b505af115801561559c573d6000803e3d6000fd5b505050505b50505050509392505050565b6155b5615a1f565b60006155bf6139ec565b905080600160a060020a031663ba21ccae6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156155fc57600080fd5b505af1158015615610573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260a081101561563957600080fd5b81019080805164010000000081111561565157600080fd5b8201602081018481111561566457600080fd5b815185602082028301116401000000008211171561568157600080fd5b5050929190602001805164010000000081111561569d57600080fd5b820160208101848111156156b057600080fd5b81518560208202830111640100000000821117156156cd57600080fd5b505092919060200180516401000000008111156156e957600080fd5b820160208101848111156156fc57600080fd5b815185602082028301116401000000008211171561571957600080fd5b505060208083015160409384015160808b01526001606060020a031660608a0152918801819052908701939093525050818452519051146157a4576040805160e560020a62461bcd02815260206004820152601660248201527f57524f4e475f524543495049454e54535f494e50555400000000000000000000604482015290519081900360640190fd5b60408201515160208301515114615805576040805160e560020a62461bcd02815260206004820152601660248201527f57524f4e475f4d4f44554c455f4944535f494e50555400000000000000000000604482015290519081900360640190fd5b9091565b60606000806000875160405190808252806020026020018201604052801561583b578160200160208202803883390190505b5093505b8751821015615924576000878381518110151561585857fe5b906020019060200201516001606060020a03161115615919576158a686611071898581518110151561588657fe5b6020908102909101015188906001606060020a031663ffffffff61325116565b90508084838151811015156158b757fe5b6020908102909101015287516158e59030908a90859081106158d557fe5b9060200190602002015183613a69565b61590688838151811015156158f657fe5b9060200190602002015182612f06565b615916838263ffffffff612e4516565b92505b81600101915061583f565b505094509492505050565b6000615939613877565b9050615946308284613a69565b61141d8183612f06565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6080604051908101604052806004906020820280388339509192915050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b610120604051908101604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60a06040519081016040528060608152602001606081526020016060815260200160006001606060020a031681526020016000815250905600644132c4ddd5bb6f0655d5fe2870dcec7870e6be4758890f366b83441f9fdecee6e35175eb53fc006520a2a9c3e9711a7c00de6ff2c32dd31df8c5a24cac1b5ca42eee1333c0758ba72be38e728b6dadb32ea767de5b4ddbaea1dae85b1b051f9f70001d82b6ef54e9d3725b46581c3eb9ee3aa02b941b6aa54d678a9ca35b10a66d35f054e68143c18f32c990ed5cb972bb68a68f500cd2dd3a16bbf3686483a3678de4a579be090bed1177e0a24f77cc29d181ac22fd7688aca344d8938015a165627a7a72305820fc8f14f7a142e4c98d60a27e11f0cbbc1de56fc9b01e1d56c2be1abc205cd2550029ebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e

Deployed ByteCode

0x6080604052600436106103105760003560e01c63ffffffff168063046f7da21461037357806306fdde031461038a57806307da68f5146104145780630803fac014610429578063095ea7b3146104525780630d48764e14610476578063136dd43c146104a057806318160ddd146104b55780631ea7ca89146104ca57806322cc5e94146104df57806323b872dd146104f75780632914b9bd146105215780632cb5f784146105965780632de03aa1146105b15780632f85e57c146105c6578063313ce567146105ed57806332f0a3b5146106185780633644e5151461062d5780633899862414610642578063389ed2671461065a578063395093511461066f5780633b19e84a146106935780633f683b6a146106a8578063485cc955146106bd5780634ad509b2146106d7578063532fbcd8146106df57806356396715146106f4578063609c4c6c14610709578063665b4b0b1461071e5780636d7804591461076d57806370a08231146107975780637475f913146107b8578063752f77f1146107cd57806378ffcfe2146108085780637e7db6e1146108105780637ecebe001461083157806380afdea814610852578063833b1fce1461086757806384b0196e1461087c5780638aa10435146109845780638b3dd749146109995780638fcb4e5b146109ae57806395d89b41146109d25780639d4941d8146109e7578063a1658fad14610a08578063a1903eab14610a6f578063a457c2d714610a83578063a479e50814610aa7578063a9059cbb14610abc578063aa0b7db714610ae0578063ad1394e914610b08578063ae2e353814610b1d578063b3320d9a14610b50578063bac3f3c514610b65578063bcb411d9146104b5578063c7de174014610bdd578063ced72f8714610bf2578063cf7e12af14610c1e578063d4aae0c414610c33578063d5002f2e14610c48578063d505accf14610c5d578063dd62ed3e14610c96578063de4796ed14610cbd578063e78a587514610cd2578063eb85262f14610ce7578063f5eb42dc14610cfc578063f974049114610d1d578063f999c50614610d32578063fa64ebac14610d47575b3615610366576040805160e560020a62461bcd02815260206004820152600e60248201527f4e4f4e5f454d5054595f44415441000000000000000000000000000000000000604482015290519081900360640190fd5b6103706000610d5c565b50005b34801561037f57600080fd5b50610388610f62565b005b34801561039657600080fd5b5061039f610f9d565b6040805160208082528351818301528351919283929083019185019080838360005b838110156103d95781810151838201526020016103c1565b50505050905090810190601f1680156104065780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561042057600080fd5b50610388610fd4565b34801561043557600080fd5b5061043e61100d565b604080519115158252519081900360200190f35b34801561045e57600080fd5b5061043e600160a060020a0360043516602435611036565b34801561048257600080fd5b5061048e60043561104c565b60408051918252519081900360200190f35b3480156104ac57600080fd5b5061048e611085565b3480156104c157600080fd5b5061048e611097565b3480156104d657600080fd5b5061043e6110a6565b3480156104eb57600080fd5b5061048e6004356110c7565b34801561050357600080fd5b5061043e600160a060020a03600435811690602435166044356110df565b34801561052d57600080fd5b506040805160206004803580820135601f810184900484028501840190955284845261057a9436949293602493928401919081908401838280828437509497506111019650505050505050565b60408051600160a060020a039092168252519081900360200190f35b3480156105a257600080fd5b506103886004356024356111e4565b3480156105bd57600080fd5b5061048e611266565b3480156105d257600080fd5b50610388600160a060020a036004358116906024351661128a565b3480156105f957600080fd5b50610602611421565b6040805160ff9092168252519081900360200190f35b34801561062457600080fd5b5061057a611426565b34801561063957600080fd5b5061048e61149b565b34801561064e57600080fd5b506103886004356114fc565b34801561066657600080fd5b5061048e611579565b34801561067b57600080fd5b5061043e600160a060020a036004351660243561159d565b34801561069f57600080fd5b5061057a6115de565b3480156106b457600080fd5b5061043e6115e8565b610388600160a060020a0360043581169060243516611607565b6103886116eb565b3480156106eb57600080fd5b5061048e6117de565b34801561070057600080fd5b5061048e6117e8565b34801561071557600080fd5b5061048e61182c565b34801561072a57600080fd5b5061073361184d565b6040805197151588529515156020880152868601949094526060860192909252608085015260a084015260c0830152519081900360e00190f35b34801561077957600080fd5b5061048e600160a060020a03600435811690602435166044356118de565b3480156107a357600080fd5b5061048e600160a060020a036004351661191a565b3480156107c457600080fd5b5061038861192d565b3480156107d957600080fd5b506107e26119a2565b6040805161ffff9485168152928416602084015292168183015290519081900360600190f35b610388611b77565b34801561081c57600080fd5b5061043e600160a060020a0360043516611c2e565b34801561083d57600080fd5b5061048e600160a060020a0360043516611c34565b34801561085e57600080fd5b5061048e611c4f565b34801561087357600080fd5b5061057a611c7a565b34801561088857600080fd5b50610891611cbe565b60408051908101839052600160a060020a038216606082015260808082528551908201528451819060208083019160a084019189019080838360005b838110156108e55781810151838201526020016108cd565b50505050905090810190601f1680156109125780820380516001836020036101000a031916815260200191505b50838103825286518152865160209182019188019080838360005b8381101561094557818101518382015260200161092d565b50505050905090810190601f1680156109725780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b34801561099057600080fd5b5061048e611e0d565b3480156109a557600080fd5b5061048e611e38565b3480156109ba57600080fd5b5061048e600160a060020a0360043516602435611e63565b3480156109de57600080fd5b5061039f611e93565b3480156109f357600080fd5b50610388600160a060020a0360043516611eca565b348015610a1457600080fd5b50604080516020600460443581810135838102808601850190965280855261043e958335600160a060020a0316956024803596369695606495939492019291829185019084908082843750949750611f1a9650505050505050565b61048e600160a060020a0360043516612065565b348015610a8f57600080fd5b5061043e600160a060020a0360043516602435612070565b348015610ab357600080fd5b5061057a612101565b348015610ac857600080fd5b5061043e600160a060020a03600435166024356121b6565b348015610aec57600080fd5b50610388600480359060248035916044359182019101356121c3565b348015610b1457600080fd5b5061048e612532565b348015610b2957600080fd5b50610b32612556565b60408051938452602084019290925282820152519081900360600190f35b348015610b5c57600080fd5b506103886125aa565b348015610b7157600080fd5b50610ba56004803590602480359160443591606435916084359160a4359160c4359160e4359081019101356101043561260e565b6040518082608080838360005b83811015610bca578181015183820152602001610bb2565b5050505090500191505060405180910390f35b348015610be957600080fd5b5061057a6126a2565b348015610bfe57600080fd5b50610c076126cd565b6040805161ffff9092168252519081900360200190f35b348015610c2a57600080fd5b5061057a612711565b348015610c3f57600080fd5b5061057a61273c565b348015610c5457600080fd5b5061048e612767565b348015610c6957600080fd5b50610388600160a060020a036004358116906024351660443560643560ff6084351660a43560c435612771565b348015610ca257600080fd5b5061048e600160a060020a03600435811690602435166129bc565b348015610cc957600080fd5b5061043e6129e7565b348015610cde57600080fd5b5061043e6129fa565b348015610cf357600080fd5b5061048e612a7a565b348015610d0857600080fd5b5061048e600160a060020a0360043516612a9e565b348015610d2957600080fd5b5061048e612aa9565b348015610d3e57600080fd5b50610388612b41565b348015610d5357600080fd5b5061048e612b6a565b6000610d66615950565b600080341515610dc0576040805160e560020a62461bcd02815260206004820152600c60248201527f5a45524f5f4445504f5349540000000000000000000000000000000000000000604482015290519081900360640190fd5b610dd7600080516020615af9833981519152612b95565b9250610de283612be8565b15610e37576040805160e560020a62461bcd02815260206004820152600e60248201527f5354414b494e475f504155534544000000000000000000000000000000000000604482015290519081900360640190fd5b610e4083612bf3565b15610ed857610e4e83612c05565b915034821015610ea8576040805160e560020a62461bcd02815260206004820152600b60248201527f5354414b455f4c494d4954000000000000000000000000000000000000000000604482015290519081900360640190fd5b610ed8610ebd8434850363ffffffff612c9116565b600080516020615af98339815191529063ffffffff612cd516565b610ee1346110c7565b9050610eed3382612d30565b50610f0e610f0934610efd612e1a565b9063ffffffff612e4516565b612ed3565b60408051348152600160a060020a0387166020820152815133927f96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a928290030190a2610f5a3382612f06565b949350505050565b610f8b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c7612f1b565b610f93612f8d565b610f9b612fdf565b565b60408051808201909152601781527f4c6971756964207374616b65642046617374746f6b656e000000000000000000602082015290565b610ffd7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d612f1b565b611005613035565b610f9b613087565b600080611018611e38565b9050801580159061103057508061102d6130d1565b10155b91505090565b60006110433384846130d5565b50600192915050565b600061107d6110596131f7565b611071611064613222565b859063ffffffff61325116565b9063ffffffff6132fc16565b90505b919050565b600080516020615a9983398151915281565b60006110a1613222565b905090565b60006110a16110c2600080516020615af9833981519152612b95565b612be8565b600061107d6110d4613222565b6110716110646131f7565b60006110ec84338461339c565b6110f7848484613436565b5060019392505050565b600061110b612101565b600160a060020a03166304bf2a7f836040518263ffffffff1660e01b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561116657818101518382015260200161114e565b50505050905090810190601f1680156111935780820380516001836020036101000a031916815260200191505b5092505050602060405180830381600087803b1580156111b257600080fd5b505af11580156111c6573d6000803e3d6000fd5b505050506040513d60208110156111dc57600080fd5b505192915050565b6111fb600080516020615a99833981519152612f1b565b611227610ebd838361121a600080516020615af9833981519152612b95565b919063ffffffff61345a16565b604080518381526020810183905281517fce9fddf6179affa1ea7bf36d80a6bf0284e0f3b91f4b2fa6eea2af923e7fac2d929181900390910190a15050565b7f2fc10cc8ae19568712f7a176fb4978616a610650813c9d05326c34abb62749c781565b611294600061367e565b61129c61100d565b15156112f2576040805160e560020a62461bcd02815260206004820152600f60248201527f4e4f545f494e495449414c495a45440000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0382161515611352576040805160e560020a62461bcd02815260206004820152601a60248201527f4c4f4c494b5f4c4f4341544f525f5a45524f5f41444452455353000000000000604482015290519081900360640190fd5b600160a060020a03811615156113b2576040805160e560020a62461bcd02815260206004820152601960248201527f4549503731325f535446544e5f5a45524f5f4144445245535300000000000000604482015290519081900360640190fd5b6113bd61dead6136dc565b1515611413576040805160e560020a62461bcd02815260206004820152601560248201527f494e495449414c5f484f4c4445525f4558495354530000000000000000000000604482015290519081900360640190fd5b61141d82826136f7565b5050565b601290565b600061143061273c565b600160a060020a03166332f0a3b56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b505af115801561147e573d6000803e3d6000fd5b505050506040513d602081101561149457600080fd5b5051905090565b60006114a5612711565b600160a060020a031663b8f120b3306040518263ffffffff1660e01b81526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561146a57600080fd5b6115257fe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c612f1b565b611543600080516020615a798339815191528263ffffffff61387316565b6040805182815290517fe0aacfc334457703148118055ec794ac17654c6f918d29638ba3b18003cee5ff9181900360200190a150565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b336000818152600160209081526040808320600160a060020a038716845290915281205490916110439185906115d9908663ffffffff612e4516565b6130d5565b60006110a1613877565b6000611601600080516020615a598339815191526138bb565b15905090565b61160f611e38565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156116d05760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561169557818101518382015260200161167d565b50505050905090810190601f1680156116c25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506116d96138bf565b6116e382826136f7565b61141d613926565b6116f36126a2565b600160a060020a031663e441d25f6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561172d57600080fd5b505af1158015611741573d6000803e3d6000fd5b505050506040513d602081101561175757600080fd5b5051600160a060020a0316331461176d57600080fd5b6117a961177c34610efd612b6a565b7fafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb9063ffffffff61387316565b6040805134815290517fd27f9b0c98bdee27044afa149eadcd2047d6399cb6613a45c5b87e6aca76e6b59181900360200190a1565b60006110a1612e1a565b60006117f26139ec565b600160a060020a031663563967156040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b60006110a1611848600080516020615af9833981519152612b95565b613a30565b6000806000806000806000611860615950565b611877600080516020615af9833981519152612b95565b905061188281612be8565b975061188d81612bf3565b965061189881613a30565b955080606001516001606060020a03169450806040015163ffffffff16935080602001516001606060020a03169250806000015163ffffffff1691505090919293949596565b6000806118ea8361104c565b90506118f785338361339c565b611902858585613a69565b61190e85858386613c6d565b8091505b509392505050565b600061107d611928836136dc565b61104c565b611944600080516020615a99833981519152612f1b565b61194c61100d565b1515610f93576040805160e560020a62461bcd02815260206004820152600f60248201527f4e4f545f494e495449414c495a45440000000000000000000000000000000000604482015290519081900360640190fd5b6000806000806000806000806119b66139ec565b945084600160a060020a031663271662ec6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156119f357600080fd5b505af1158015611a07573d6000803e3d6000fd5b505050506040513d6020811015611a1d57600080fd5b5051604080517f9fbb7bae0000000000000000000000000000000000000000000000000000000081529051919550600160a060020a03871691639fbb7bae916004808201926020929091908290030181600087803b158015611a7e57600080fd5b505af1158015611a92573d6000803e3d6000fd5b505050506040513d6020811015611aa857600080fd5b5051604080517fefcdcc0e000000000000000000000000000000000000000000000000000000008152815161ffff9093169550600160a060020a0388169263efcdcc0e926004808401939192918290030181600087803b158015611b0b57600080fd5b505af1158015611b1f573d6000803e3d6000fd5b505050506040513d6040811015611b3557600080fd5b5080516020909101516000985061ffff918216935016905082848302811515611b5a57fe5b04975082848202811515611b6a57fe5b0495505050505050909192565b611b7f6126a2565b600160a060020a03166369d421486040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611bb957600080fd5b505af1158015611bcd573d6000803e3d6000fd5b505050506040513d6020811015611be357600080fd5b5051600160a060020a03163314611bf957600080fd5b6040805134815290517f6e5086f7e1ab04bd826e77faae35b1bcfe31bd144623361a40ea4af51670b1c39181900360200190a1565b50600190565b600160a060020a031660009081526002602052604090205490565b60006110a17fd625496217aa6a3453eecb9c3489dc5a53e6c67b444329ea2b2cbc9ff547639b6138bb565b6000611c846126a2565b600160a060020a031663996107aa6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b606080600080611ccc612711565b600160a060020a031663f4409319306040518263ffffffff1660e01b81526004018082600160a060020a0316600160a060020a03168152602001915050600060405180830381600087803b158015611d2357600080fd5b505af1158015611d37573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526080811015611d6057600080fd5b810190808051640100000000811115611d7857600080fd5b82016020810184811115611d8b57600080fd5b8151640100000000811182820187101715611da557600080fd5b50509291906020018051640100000000811115611dc157600080fd5b82016020810184811115611dd457600080fd5b8151640100000000811182820187101715611dee57600080fd5b5050602082015160409092015194985096509450919250505090919293565b60006110a17f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a66138bb565b60006110a17febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e6138bb565b600080611e71338585613a69565b611e7a8361104c565b9050611e8833858386613c6d565b8091505b5092915050565b60408051808201909152600581527f737446544e000000000000000000000000000000000000000000000000000000602082015290565b6040805160e560020a62461bcd02815260206004820152600d60248201527f4e4f545f535550504f5254454400000000000000000000000000000000000000604482015290519081900360640190fd5b600080611f2561100d565b1515611f345760009150611912565b611f3c61273c565b9050600160a060020a0381161515611f575760009150611912565b80600160a060020a031663fdef9106863087611f7288613d0d565b60405163ffffffff861660e01b8152600160a060020a03808616600483019081529085166024830152604482018490526080606483019081528351608484015283519192909160a490910190602085019080838360005b83811015611fe1578181015183820152602001611fc9565b50505050905090810190601f16801561200e5780820380516001836020036101000a031916815260200191505b5095505050505050602060405180830381600087803b15801561203057600080fd5b505af1158015612044573d6000803e3d6000fd5b505050506040513d602081101561205a57600080fd5b505195945050505050565b600061107d82610d5c565b336000908152600160209081526040808320600160a060020a0386168452909152812054828110156120ec576040805160e560020a62461bcd02815260206004820152601460248201527f414c4c4f57414e43455f42454c4f575f5a45524f000000000000000000000000604482015290519081900360640190fd5b6110f733856115d9848763ffffffff613d1716565b60008061210c61273c565b604080517fbe00bbd80000000000000000000000000000000000000000000000000000000081527fd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb60048201527fddbcfd564f642ab5627cf68b9b7d374fb4f8a36e941a75d89c87998cef03bd6160248201529051600160a060020a03929092169163be00bbd8916044808201926020929091908290030181600087803b1580156111b257600080fd5b6000611043338484613436565b60008060008060006121d36126a2565b945084600160a060020a031663472c17766040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561221057600080fd5b505af1158015612224573d6000803e3d6000fd5b505050506040513d602081101561223a57600080fd5b5051600160a060020a0316331461229b576040805160e560020a62461bcd02815260206004820152601360248201527f4150505f415554485f44534d5f4641494c454400000000000000000000000000604482015290519081900360640190fd5b6122a36129fa565b15156122f9576040805160e560020a62461bcd02815260206004820152600f60248201527f43414e5f4e4f545f4445504f5349540000000000000000000000000000000000604482015290519081900360640190fd5b6123016139ec565b935061238d8985600160a060020a03166319c64b798b61231f612aa9565b6040518363ffffffff1660e01b81526004018083815260200182815260200192505050602060405180830381600087803b15801561235c57600080fd5b505af1158015612370573d6000803e3d6000fd5b505050506040513d602081101561238657600080fd5b5051613dab565b9250600083111561249e576123b2836901bc16d674ec8000000063ffffffff61325116565b91506123fc6123cf836123c3612e1a565b9063ffffffff613d1716565b7fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b09063ffffffff61387316565b6040805183815290517f76a397bea5768d4fca97ef47792796e35f98dc81b16c1de84e28a818e1f971089181900360200190a161244a83610efd600080516020615a798339815191526138bb565b905061246a600080516020615a798339815191528263ffffffff61387316565b6040805182815290517fe0aacfc334457703148118055ec794ac17654c6f918d29638ba3b18003cee5ff9181900360200190a15b83600160a060020a031663aa0b7db783858b8b8b6040518663ffffffff1660e01b8152600401808581526020018481526020018060200182810382528484828181526020019250808284378201915050955050505050506000604051808303818588803b15801561250e57600080fd5b505af1158015612522573d6000803e3d6000fd5b5050505050505050505050505050565b7fe6dc5d79630c61871e99d341ad72c5a052bed2fc8c79e5a4480a7cd31117576c81565b60008080612571600080516020615a798339815191526138bb565b925061258a600080516020615ab98339815191526138bb565b91506125a3600080516020615ad98339815191526138bb565b9050909192565b6125c1600080516020615a99833981519152612f1b565b6125e3610ebd6125de600080516020615af9833981519152612b95565b613dc3565b6040517f9b2a687c198898fcc32a33bbc610d478f177a73ab7352023e6cc1de5bf12a3df90600090a1565b612616615977565b61261e613dd6565b612693610120604051908101604052808d81526020018c81526020018b81526020018a8152602001898152602001888152602001878152602001868680806020026020016040519081016040528093929190818152602001838360200280828437505050928452505050602001849052613e43565b9b9a5050505050505050505050565b60006110a17f9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df76138bb565b60006126d76139ec565b600160a060020a0316639fbb7bae6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b60006110a17f42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c6138bb565b60006110a17f4172f0f7d2289153072b0a6ca36959e0cbe2efc3afe50fc81636caa96338137b6138bb565b60006110a16131f7565b600080428610156127cc576040805160e560020a62461bcd02815260206004820152601060248201527f444541444c494e455f4558504952454400000000000000000000000000000000604482015290519081900360640190fd5b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98989896127f983614346565b60408051602080820197909752600160a060020a0395861681830152939094166060840152608083019190915260a082015260c08082018a90528251808303909101815260e090910191829052805190928291908401908083835b602083106128735780518252601f199092019160209182019101612854565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902091506128aa612711565b604080517f804e5eb3000000000000000000000000000000000000000000000000000000008152306004820152602481018590529051600160a060020a03929092169163804e5eb3916044808201926020929091908290030181600087803b15801561291557600080fd5b505af1158015612929573d6000803e3d6000fd5b505050506040513d602081101561293f57600080fd5b505190506129508982878787614390565b15156129a6576040805160e560020a62461bcd02815260206004820152601160248201527f494e56414c49445f5349474e4154555245000000000000000000000000000000604482015290519081900360640190fd5b6129b18989896130d5565b505050505050505050565b600160a060020a03918216600090815260016020908152604080832093909416825291909152205490565b60006000196129f4611e38565b14905090565b6000612a04614555565b600160a060020a0316632b95b7816040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612a3e57600080fd5b505af1158015612a52573d6000803e3d6000fd5b505050506040513d6020811015612a6857600080fd5b50511580156110a157506116016115e8565b7f84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de881565b600061107d826136dc565b6000806000612ab6612e1a565b9150612ac0614555565b600160a060020a0316634388484d6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612afa57600080fd5b505af1158015612b0e573d6000803e3d6000fd5b505050506040513d6020811015612b2457600080fd5b50519050808211612b36576000612b3a565b8082035b9250505090565b6110057f84ea57490227bc2be925c684e2a367071d69890b629590198f4125a018eb1de8612f1b565b60006110a17fafe016039542d12eec0183bb0b1ffc2ca45b027126a494672fba4154ee77facb6138bb565b612b9d615950565b6000612ba8836138bb565b63ffffffff600082901c811684526001606060020a03602083811c821690860152608083901c909116604085015260a09190911c16606083015250919050565b5163ffffffff161590565b606001516001606060020a0316151590565b600080600080846040015163ffffffff166000141515612c4e57846040015163ffffffff1685606001516001606060020a0316811515612c4157fe5b046001606060020a031692505b846000015163ffffffff164303915082820285602001516001606060020a0316019050612c888186606001516001606060020a0316614599565b95945050505050565b612c99615950565b6001606060020a03821115612caa57fe5b825163ffffffff161515612cba57fe5b506001606060020a031660208201524363ffffffff16815290565b61141d60a082606001516001606060020a0316901b6080836040015163ffffffff16901b602084602001516001606060020a0316901b6000856000015163ffffffff16901b171717836000191661387390919063ffffffff16565b6000600160a060020a0383161515612d92576040805160e560020a62461bcd02815260206004820152601160248201527f4d494e545f544f5f5a45524f5f41444452000000000000000000000000000000604482015290519081900360640190fd5b612d9e82610efd6131f7565b9050612dd07fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e8263ffffffff61387316565b600160a060020a038316600090815260208190526040902054612df9908363ffffffff612e4516565b600160a060020a039093166000908152602081905260409020929092555090565b60006110a17fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b06138bb565b60408051808201909152601181527f4d4154485f4144445f4f564552464c4f57000000000000000000000000000000602082015260009083830190848210156119125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b612f037fed310af23f61f96daefbcd140b306c0bdbf8c178398299741687b90e794772b08263ffffffff61387316565b50565b61141d600083612f158461104c565b84613c6d565b604080516000815260208101909152612f379033908390611f1a565b1515612f03576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b612f956145aa565b612fb4600080516020615a59833981519152600163ffffffff61387316565b6040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f990600090a1565b61300a610ebd6000612ffe600080516020615af9833981519152612b95565b9063ffffffff61461616565b6040517fedaeeae9aed70c4545d3ab0065713261c9cee8d6cf5c8b07f52f0a65fd91efda90600090a1565b61303d613dd6565b61305c600080516020615a59833981519152600063ffffffff61387316565b6040517f7acc84e34091ae817647a4c49116f5cc07f319078ba80f8f5fde37ea7e25cbd690600090a1565b6130a6610ebd6001612ffe600080516020615af9833981519152612b95565b6040517f26d1807b479eaba249c1214b82e4b65bbb0cc73ee8a17901324b1ef1b5904e4990600090a1565b4390565b600160a060020a0383161515613135576040805160e560020a62461bcd02815260206004820152601660248201527f415050524f56455f46524f4d5f5a45524f5f4144445200000000000000000000604482015290519081900360640190fd5b600160a060020a0382161515613195576040805160e560020a62461bcd02815260206004820152601460248201527f415050524f56455f544f5f5a45524f5f41444452000000000000000000000000604482015290519081900360640190fd5b600160a060020a03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b60006110a17fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e6138bb565b60006110a161322f61463b565b610efd613249600080516020615ad98339815191526138bb565b610efd612e1a565b6000808315156132645760009150611e8c565b5082820282848281151561327457fe5b60408051808201909152601181527f4d4154485f4d554c5f4f564552464c4f57000000000000000000000000000000602082015292919004146119125760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b60408051808201909152600d81527f4d4154485f4449565f5a45524f00000000000000000000000000000000000000602082015260009081908184116133875760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b50828481151561339357fe5b04949350505050565b600160a060020a0380841660009081526001602090815260408083209386168352929052205460001981146134305781811015613423576040805160e560020a62461bcd02815260206004820152601260248201527f414c4c4f57414e43455f45584345454445440000000000000000000000000000604482015290519081900360640190fd5b61343084848484036130d5565b50505050565b6000613441826110c7565b905061344e848483613a69565b61343084848484613c6d565b613462615950565b8215156134b9576040805160e560020a62461bcd02815260206004820152601460248201527f5a45524f5f4d41585f5354414b455f4c494d4954000000000000000000000000604482015290519081900360640190fd5b6001606060020a03831115613518576040805160e560020a62461bcd02815260206004820152601960248201527f544f4f5f4c415247455f4d41585f5354414b455f4c494d495400000000000000604482015290519081900360640190fd5b81831015613570576040805160e560020a62461bcd02815260206004820152601860248201527f544f4f5f4c415247455f4c494d49545f494e4352454153450000000000000000604482015290519081900360640190fd5b81158061358c575063ffffffff828481151561358857fe5b0411155b15156135e2576040805160e560020a62461bcd02815260206004820152601860248201527f544f4f5f534d414c4c5f4c494d49545f494e4352454153450000000000000000604482015290519081900360640190fd5b835163ffffffff161580613601575060608401516001606060020a0316155b80613618575083602001516001606060020a031683105b1561362d576001606060020a03831660208501525b81151561363b576000613648565b818381151561364657fe5b045b63ffffffff90811660408601526001606060020a0384166060860152845116156136765763ffffffff431684525b509192915050565b613686611e0d565b8114612f03576040805160e560020a62461bcd02815260206004820152601b60248201527f554e45585045435445445f434f4e54524143545f56455253494f4e0000000000604482015290519081900360640190fd5b600160a060020a031660009081526020819052604090205490565b6137016002614697565b6137317f9ef78dff90f100ea94042bd00ccb978430524befc391d3e510b5f55ff3166df78363ffffffff61387316565b61373a816146fd565b61383382600160a060020a03166337d5fe996040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561377857600080fd5b505af115801561378c573d6000803e3d6000fd5b505050506040513d60208110156137a257600080fd5b5051604080517f27810b6e0000000000000000000000000000000000000000000000000000000081529051600160a060020a038616916327810b6e9160048083019260209291908290030181600087803b1580156137ff57600080fd5b505af1158015613813573d6000803e3d6000fd5b505050506040513d602081101561382957600080fd5b50516000196130d5565b60408051600160a060020a038416815290517f66a24487f11bf1960b5e3706756c6b00545a8f3ad8e0ec08a4ae7934288a21ca9181900360200190a15050565b9055565b60006138816126a2565b600160a060020a03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b5490565b30318015156138ca57fe5b6138d26131f7565b1515612f03576138e181612ed3565b6040805182815260006020820152815161dead927f96a25c8ce0baabc1fdefd93e9ed25d8e092a3332f3aa9a41722b5697231d1d1a928290030190a2612f0381614834565b61392e611e38565b60408051808201909152601881527f494e49545f414c52454144595f494e495449414c495a45440000000000000000602082015290156139b35760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b50610f9b6139bf6130d1565b7febb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e9063ffffffff61387316565b60006139f66126a2565b600160a060020a031663ef6c064c6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b6000613a3b82612be8565b15613a4857506000611080565b613a5182612bf3565b1515613a605750600019611080565b61107d82612c05565b6000600160a060020a0384161515613acb576040805160e560020a62461bcd02815260206004820152601760248201527f5452414e534645525f46524f4d5f5a45524f5f41444452000000000000000000604482015290519081900360640190fd5b600160a060020a0383161515613b2b576040805160e560020a62461bcd02815260206004820152601560248201527f5452414e534645525f544f5f5a45524f5f414444520000000000000000000000604482015290519081900360640190fd5b600160a060020a038316301415613b8c576040805160e560020a62461bcd02815260206004820152601a60248201527f5452414e534645525f544f5f535446544e5f434f4e5452414354000000000000604482015290519081900360640190fd5b613b94613dd6565b50600160a060020a03831660009081526020819052604090205480821115613c06576040805160e560020a62461bcd02815260206004820152601060248201527f42414c414e43455f455843454544454400000000000000000000000000000000604482015290519081900360640190fd5b613c16818363ffffffff613d1716565b600160a060020a038086166000908152602081905260408082209390935590851681522054613c4b908363ffffffff612e4516565b600160a060020a03909316600090815260208190526040902092909255505050565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a382600160a060020a031684600160a060020a03167f9d9c909296d9c674451c0c24f02cb64981eb3b727f99865939192f880a755dcb836040518082815260200191505060405180910390a350505050565b5490565b8051602002815290565b60408051808201909152601281527f4d4154485f5355425f554e444552464c4f5700000000000000000000000000006020820152600090819084841115613da35760405160e560020a62461bcd0281526004018080602001828103825283818151815260200191508051906020019080838360008381101561169557818101518382015260200161167d565b505050900390565b6000818310613dba5781613dbc565b825b9392505050565b613dcb615950565b506000606082015290565b613ded600080516020615a598339815191526138bb565b1515610f9b576040805160e560020a62461bcd02815260206004820152601360248201527f434f4e54524143545f49535f53544f5050454400000000000000000000000000604482015290519081900360640190fd5b613e4b615977565b613e53615996565b613e5b6159d2565b600080600080613e6961484d565b8051909650600160a060020a03163314613ecd576040805160e560020a62461bcd02815260206004820152600f60248201527f4150505f415554485f4641494c45440000000000000000000000000000000000604482015290519081900360640190fd5b8751421015613f26576040805160e560020a62461bcd02815260206004820152601860248201527f494e56414c49445f5245504f52545f54494d455354414d500000000000000000604482015290519081900360640190fd5b613f2e613222565b6040860152613f3b6131f7565b6060860152613f57600080516020615ab98339815191526138bb565b808652885160408a015160608b0151613f7193919061491e565b6020860152613f81868987614abc565b60e0880151511561403257613f968689614b7b565b60a08701819052608087019190915260001015614032578560600151600160a060020a0316634611492887608001518760a001516040518363ffffffff1660e01b81526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561401957600080fd5b505af115801561402d573d6000803e3d6000fd5b505050505b8560400151600160a060020a031663b8498a398660400151876060015188602001518c606001518d608001518e60a001518f60c001518d608001518e60a001516040518a63ffffffff1660e01b8152600401808a81526020018981526020018881526020018781526020018681526020018581526020018481526020018381526020018281526020019950505050505050505050608060405180830381600087803b1580156140e057600080fd5b505af11580156140f4573d6000803e3d6000fd5b505050506040513d608081101561410a57600080fd5b5080516020820151604083015160609093015160e0808a019190915260c0890193909352918a01516101008b01516080890151929750929550614154928992889288929091614d5a565b87600001517f89e36cef94d4462d02672198dd9e1388f7cb6b21474a0449465d192e687b397486602001518a60600151878761418e612e1a565b6040805195865260208601949094528484019290925260608401526080830152519081900360a00190a260008560e00151111561423f578560600151600160a060020a031663636e6b668660e001516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561421357600080fd5b505af1158015614227573d6000803e3d6000fd5b5050505061423d86606001518660e00151614f05565b505b61424f85896060015186866150af565b61010086015260c0860151614267908990879061510c565b60e08a01515191935091501561431d578560400151600160a060020a03166363e56b9f828488608001516142ac8a60c001518b60e00151613d1790919063ffffffff16565b8d61010001516040518663ffffffff1660e01b81526004018086815260200185815260200184815260200183815260200182815260200195505050505050600060405180830381600087803b15801561430457600080fd5b505af1158015614318573d6000803e3d6000fd5b505050505b604080516080810182529182526020820192909252908101929092526060820152949350505050565b600160a060020a03811660009081526002602052604090205461437081600163ffffffff612e4516565b600160a060020a0390921660009081526002602052604090209190915590565b600060608060006143a08961524c565b156145265760408051602080820189905281830188905260ff8a1660f81b606083015282516041818403018152606183018452608583018c815260a58401948552815160c585015281519197507f1626ba7e00000000000000000000000000000000000000000000000000000000948d9489949293919260e5909101919085019080838360005b8381101561443f578181015183820152602001614427565b50505050905090810190601f16801561446c5780820380516001836020036101000a031916815260200191505b509350505050604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509150604051602081016040526020818451602086018d5afa60203d146001821416156144fa57815192505b50507f1626ba7e0000000000000000000000000000000000000000000000000000000081149350614549565b88600160a060020a031661453c89898989615254565b600160a060020a03161493505b50505095945050505050565b600061455f6126a2565b600160a060020a03166337d5fe996040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561146a57600080fd5b808210918202600192909203020190565b6145c1600080516020615a598339815191526138bb565b15610f9b576040805160e560020a62461bcd02815260206004820152601260248201527f434f4e54524143545f49535f4143544956450000000000000000000000000000604482015290519081900360640190fd5b61461e615950565b81614629574361462c565b60005b63ffffffff1683525090919050565b60008080614656600080516020615a798339815191526138bb565b915061466f600080516020615ab98339815191526138bb565b90508082101561467b57fe5b612b3a8183036901bc16d674ec8000000063ffffffff61325116565b6146c77f4dd0f6662ba1d6b081f08b350f5e9a6a7b15cf586926ba66f753594928fa64a68263ffffffff61387316565b6040805182815290517ffddcded6b4f4730c226821172046b48372d3cd963c159701ae1b7c3bcac541bb9181900360200190a150565b600160a060020a038116151561475d576040805160e560020a62461bcd02815260206004820152601060248201527f5a45524f5f454950373132535446544e00000000000000000000000000000000604482015290519081900360640190fd5b6000614767612711565b600160a060020a0316146147c5576040805160e560020a62461bcd02815260206004820152601760248201527f454950373132535446544e5f414c52454144595f534554000000000000000000604482015290519081900360640190fd5b6147f57f42b2d95e1ce15ce63bf9a8d9f6312cf44b23415c977ffa3b884333422af8941c8263ffffffff61387316565b60408051600160a060020a038316815290517f410358095ccd195bc4f9aa535a730aeb19f5588ce07e9dfcee5de815241b8cc49181900360200190a150565b61484061dead82612d30565b50612f0361dead82612f06565b614855615996565b61485d6126a2565b600160a060020a031663e1078b8c6040518163ffffffff1660e01b815260040160e060405180830381600087803b15801561489757600080fd5b505af11580156148ab573d6000803e3d6000fd5b505050506040513d60e08110156148c157600080fd5b50805160208083015160408085015160608087015160808089015160a0808b015160c09b8c0151600160a060020a039081169c8e019c909c528b16908c01528916908a015287169088015285169086015283169084015216815290565b60008080614939600080516020615a798339815191526138bb565b915081851115614993576040805160e560020a62461bcd02815260206004820152601760248201527f5245504f525445445f4d4f52455f4445504f5349544544000000000000000000604482015290519081900360640190fd5b858510156149eb576040805160e560020a62461bcd02815260206004820152601860248201527f5245504f525445445f4c4553535f56414c494441544f52530000000000000000604482015290519081900360640190fd5b85851115614a1157614a11600080516020615ab98339815191528663ffffffff61387316565b50848403614a2c600080516020615ad98339815191526138bb565b9250614a58614a4b826901bc16d674ec8000000063ffffffff61325116565b849063ffffffff612e4516565b9250614a78600080516020615ad98339815191528563ffffffff61387316565b6040805187815260208101879052815189927f1252331d4f3ee8a9f0a3484c4c2fb059c70a047b5dc5482a3ee6415f742d9f2e928290030190a25050949350505050565b8260400151600160a060020a0316638024cca183602001518360200151856060015186608001518760a001518860c0015188600001518a604001516040518963ffffffff1660e01b81526004018089815260200188815260200187815260200186815260200185815260200184815260200183815260200182815260200198505050505050505050600060405180830381600087803b158015614b5e57600080fd5b505af1158015614b72573d6000803e3d6000fd5b50505050505050565b60008060008460800151905080600160a060020a031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381600087803b158015614bc257600080fd5b505af1158015614bd6573d6000803e3d6000fd5b505050506040513d6020811015614bec57600080fd5b50511515614d52578460400151600160a060020a0316636a84f2fd8560e0015160018760e001515103815181101515614c2157fe5b9060200190602002015186600001516040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015614c6d57600080fd5b505af1158015614c81573d6000803e3d6000fd5b5050505080600160a060020a031663a52e9c9f8560e001518661010001516040518363ffffffff1660e01b81526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b83811015614cf4578181015183820152602001614cdc565b5050505090500193505050506040805180830381600087803b158015614d1957600080fd5b505af1158015614d2d573d6000803e3d6000fd5b505050506040513d6040811015614d4357600080fd5b50805160209091015190935091505b509250929050565b6000806000861115614ddd578760200151600160a060020a0316639342c8f4876040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b158015614db057600080fd5b505af1158015614dc4573d6000803e3d6000fd5b505050506040513d6020811015614dda57600080fd5b50505b6000871115614e49578760a00151600160a060020a0316633194528a886040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015614e3057600080fd5b505af1158015614e44573d6000803e3d6000fd5b505050505b6000831115614edc578760800151915081600160a060020a031663b6013cef84876001895103815181101515614e7b57fe5b90602001906020020151876040518463ffffffff1660e01b815260040180838152602001828152602001925050506000604051808303818588803b158015614ec257600080fd5b505af1158015614ed6573d6000803e3d6000fd5b50505050505b614ef0836123c389610efd8a610efd612e1a565b9050614efb81612ed3565b5050505050505050565b6000808080600160a060020a0386161515614f6a576040805160e560020a62461bcd02815260206004820152601360248201527f4255524e5f46524f4d5f5a45524f5f4144445200000000000000000000000000604482015290519081900360640190fd5b600160a060020a038616600090815260208190526040902054925082851115614fdd576040805160e560020a62461bcd02815260206004820152601060248201527f42414c414e43455f455843454544454400000000000000000000000000000000604482015290519081900360640190fd5b614fe68561104c565b9150614ff4856123c36131f7565b93506150267fe3b4b636e601189b5f4c6742edf2538ac12bb61ed03e6da26949d69838fa447e8563ffffffff61387316565b615036838663ffffffff613d1716565b600160a060020a0387166000908152602081905260409020556150588561104c565b60408051848152602081018390528082018890529051919250600160a060020a038816917f8b2a1e1ad5e0578c3dd82494156e985dade827a87c573b5c1c7716a32162ad649181900360600190a250505092915050565b600080806150c3868663ffffffff612e4516565b91508660200151821115615102578660200151820390506150ff876040015188606001516150fa8785612e4590919063ffffffff16565b6153be565b92505b5050949350505050565b6000806151176131f7565b9150615121613222565b9050600160a060020a038316156151d65782600160a060020a03166389136ec0866000015187602001518760600151886040015187878b61010001516040518863ffffffff1660e01b815260040180888152602001878152602001868152602001858152602001848152602001838152602001828152602001975050505050505050600060405180830381600087803b1580156151bd57600080fd5b505af11580156151d1573d6000803e3d6000fd5b505050505b84516020808701516060808801516040808a01516101008b0151825195865295850192909252838101919091529082018690526080820185905260a082019290925290517fff08c3ef606d198e316ef5b822193c489965899eb4e3c248cea1a4626c3eda509181900360c00190a2935093915050565b6000903b1190565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083821c11156152f7576040805160e560020a62461bcd02815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b60408051600080825260208083018085528a905260ff8916838501526060830188905260808301879052925160019360a0808501949193601f19840193928390039091019190865af1158015615351573d6000803e3d6000fd5b5050604051601f190151915050600160a060020a0381161515612c88576040805160e560020a62461bcd02815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015290519081900360640190fd5b60006153c8615a1f565b600080606060006153d76155ad565b94509450600085606001516001606060020a031611156155a157615401898863ffffffff612e4516565b925061546f61543f61542987606001516001606060020a03168a61325190919063ffffffff16565b60808801516123c390879063ffffffff61325116565b6110718a61546389606001516001606060020a03168c61325190919063ffffffff16565b9063ffffffff61325116565b955061547b3087612d30565b5061549d8560000151866040015187606001516001606060020a031689615809565b90925090506154ba6154b5878363ffffffff613d1716565b61592f565b83600160a060020a031663af1240978660200151846040518363ffffffff1660e01b8152600401808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561552257818101518382015260200161550a565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015615561578181015183820152602001615549565b50505050905001945050505050600060405180830381600087803b15801561558857600080fd5b505af115801561559c573d6000803e3d6000fd5b505050505b50505050509392505050565b6155b5615a1f565b60006155bf6139ec565b905080600160a060020a031663ba21ccae6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156155fc57600080fd5b505af1158015615610573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260a081101561563957600080fd5b81019080805164010000000081111561565157600080fd5b8201602081018481111561566457600080fd5b815185602082028301116401000000008211171561568157600080fd5b5050929190602001805164010000000081111561569d57600080fd5b820160208101848111156156b057600080fd5b81518560208202830111640100000000821117156156cd57600080fd5b505092919060200180516401000000008111156156e957600080fd5b820160208101848111156156fc57600080fd5b815185602082028301116401000000008211171561571957600080fd5b505060208083015160409384015160808b01526001606060020a031660608a0152918801819052908701939093525050818452519051146157a4576040805160e560020a62461bcd02815260206004820152601660248201527f57524f4e475f524543495049454e54535f494e50555400000000000000000000604482015290519081900360640190fd5b60408201515160208301515114615805576040805160e560020a62461bcd02815260206004820152601660248201527f57524f4e475f4d4f44554c455f4944535f494e50555400000000000000000000604482015290519081900360640190fd5b9091565b60606000806000875160405190808252806020026020018201604052801561583b578160200160208202803883390190505b5093505b8751821015615924576000878381518110151561585857fe5b906020019060200201516001606060020a03161115615919576158a686611071898581518110151561588657fe5b6020908102909101015188906001606060020a031663ffffffff61325116565b90508084838151811015156158b757fe5b6020908102909101015287516158e59030908a90859081106158d557fe5b9060200190602002015183613a69565b61590688838151811015156158f657fe5b9060200190602002015182612f06565b615916838263ffffffff612e4516565b92505b81600101915061583f565b505094509492505050565b6000615939613877565b9050615946308284613a69565b61141d8183612f06565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6080604051908101604052806004906020820280388339509192915050565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b610120604051908101604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60a06040519081016040528060608152602001606081526020016060815260200160006001606060020a031681526020016000815250905600644132c4ddd5bb6f0655d5fe2870dcec7870e6be4758890f366b83441f9fdecee6e35175eb53fc006520a2a9c3e9711a7c00de6ff2c32dd31df8c5a24cac1b5ca42eee1333c0758ba72be38e728b6dadb32ea767de5b4ddbaea1dae85b1b051f9f70001d82b6ef54e9d3725b46581c3eb9ee3aa02b941b6aa54d678a9ca35b10a66d35f054e68143c18f32c990ed5cb972bb68a68f500cd2dd3a16bbf3686483a3678de4a579be090bed1177e0a24f77cc29d181ac22fd7688aca344d8938015a165627a7a72305820fc8f14f7a142e4c98d60a27e11f0cbbc1de56fc9b01e1d56c2be1abc205cd2550029
<script src="{@file}"> </script>