false
false
0

Contract Address Details

0xc5738334b972745067fFa666040fdeADc66Cb925

Contract Name
FtsoRewardManager
Creator
0x493044–a7586e at 0xe84e8a–4397cf
Balance
4,339,726.027397260273972602 SGB
Tokens
Fetching tokens...
Transactions
1,852,494 Transactions
Transfers
11 Transfers
Gas Used
613,072,256,491
Last Balance Update
58318663
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
FtsoRewardManager




Optimization enabled
true
Compiler version
v0.7.6+commit.7338295f




Optimization runs
200
EVM Version
default




Verified at
2021-09-22T13:29:08.191765Z

Constructor Arguments

000000000000000000000000493044fbbaa7f9f78379864fa88accaff6a7586e000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000007d0

Arg [0] (address) : 0x493044fbbaa7f9f78379864fa88accaff6a7586e
Arg [1] (uint256) : 3
Arg [2] (uint256) : 2000

              

Contract source code

// Sources flattened with hardhat v2.3.0 https://hardhat.org

// File contracts/userInterfaces/IFtsoRewardManager.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

interface IFtsoRewardManager {

    event RewardClaimed(
        address indexed dataProvider,
        address indexed whoClaimed,
        address indexed sentTo,
        uint256 rewardEpoch, 
        uint256 amount
    );
    
    event RewardsDistributed(
        address indexed ftso,
        uint256 epochId,
        address[] addresses,
        uint256[] rewards
    );

    event FeePercentageChanged(
        address indexed dataProvider,
        uint256 value,
        uint256 validFromEpoch
    );

    event RewardClaimsExpired(
        uint256 rewardEpochId
    );    

    /**
     * @notice Allows a percentage delegator to claim rewards.
     * @notice This function is intended to be used to claim rewards in case of delegation by percentage.
     * @param _recipient            address to transfer funds to
     * @param _rewardEpochs         array of reward epoch numbers to claim for
     * @return _rewardAmount        amount of total claimed rewards
     * @dev Reverts if `msg.sender` is delegating by amount
     */
    function claimReward(address payable _recipient, uint256[] memory _rewardEpochs)
        external returns (uint256 _rewardAmount);

    /**
     * @notice Allows the sender to claim the rewards from specified data providers.
     * @notice This function is intended to be used to claim rewards in case of delegation by amount.
     * @param _recipient            address to transfer funds to
     * @param _rewardEpochs         array of reward epoch numbers to claim for
     * @param _dataProviders        array of addresses representing data providers to claim the reward from
     * @return _rewardAmount        amount of total claimed rewards
     * @dev Function can be used by a percentage delegator but is more gas consuming than `claimReward`.
     */
    function claimRewardFromDataProviders(
        address payable _recipient,
        uint256[] memory _rewardEpochs,
        address[] memory _dataProviders
    )
        external
        returns (uint256 _rewardAmount);

    /**
     * @notice Allows data provider to set (or update last) fee percentage.
     * @param _feePercentageBIPS    number representing fee percentage in BIPS
     * @return _validFromEpoch      reward epoch number when the setting becomes effective.
     */
    function setDataProviderFeePercentage(uint256 _feePercentageBIPS)
        external returns (uint256 _validFromEpoch);

    /**
     * @notice Returns the current fee percentage of `_dataProvider`
     * @param _dataProvider         address representing data provider
     */
    function getDataProviderCurrentFeePercentage(address _dataProvider)
        external view returns (uint256 _feePercentageBIPS);

    /**
     * @notice Returns the scheduled fee percentage changes of `_dataProvider`
     * @param _dataProvider         address representing data provider
     * @return _feePercentageBIPS   positional array of fee percentages in BIPS
     * @return _validFromEpoch      positional array of block numbers the fee setings are effective from
     * @return _fixed               positional array of boolean values indicating if settings are subjected to change
     */
    function getDataProviderScheduledFeePercentageChanges(address _dataProvider) external view 
        returns (
            uint256[] memory _feePercentageBIPS,
            uint256[] memory _validFromEpoch,
            bool[] memory _fixed
        );

    /**
     * @notice Returns information on epoch reward
     * @param _rewardEpoch          reward epoch number
     * @return _totalReward         number representing the total epoch reward
     * @return _claimedReward       number representing the amount of total epoch reward that has been claimed
     */
    function getEpochReward(uint256 _rewardEpoch) external view
        returns (uint256 _totalReward, uint256 _claimedReward);

    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch`
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @return _dataProviders       positional array of addresses representing data providers
     * @return _rewardAmounts       positional array of reward amounts
     * @return _claimed             positional array of boolean values indicating if reward is claimed
     * @return _claimable           boolean value indicating if rewards are claimable
     * @dev Reverts when queried with `_beneficary` delegating by amount
     */
    function getStateOfRewards(
        address _beneficiary,
        uint256 _rewardEpoch
    )
        external view 
        returns (
            address[] memory _dataProviders,
            uint256[] memory _rewardAmounts,
            bool[] memory _claimed,
            bool _claimable
        );

    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch` from `_dataProviders`
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @param _dataProviders        positional array of addresses representing data providers
     * @return _rewardAmounts       positional array of reward amounts
     * @return _claimed             positional array of boolean values indicating if reward is claimed
     * @return _claimable           boolean value indicating if rewards are claimable
     */
    function getStateOfRewardsFromDataProviders(
        address _beneficiary,
        uint256 _rewardEpoch,
        address[] memory _dataProviders
    )
        external view
        returns (
            uint256[] memory _rewardAmounts,
            bool[] memory _claimed,
            bool _claimable
        );

    /**
     * @notice Returns the start and the end of the reward epoch range for which the reward is claimable
     * @param _startEpochId         the oldest epoch id that allows reward claiming
     * @param _endEpochId           the newest epoch id that allows reward claiming
     */
    function getEpochsWithClaimableRewards() external view 
        returns (
            uint256 _startEpochId,
            uint256 _endEpochId
        );

    /**
     * @notice Returns the array of claimable epoch ids for which the reward has not yet been claimed
     * @param _beneficiary          address of reward beneficiary
     * @return _epochIds            array of epoch ids
     * @dev Reverts when queried with `_beneficary` delegating by amount
     */
    function getEpochsWithUnclaimedRewards(address _beneficiary) external view returns (
        uint256[] memory _epochIds
    );

    /**
     * @notice Returns the information on claimed reward of `_dataProvider` for `_rewardEpoch` by `_claimer`
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing the data provider
     * @param _claimer              address representing the claimer
     * @return _claimed             boolean indicating if reward has been claimed
     * @return _amount              number representing the claimed amount
     */
    function getClaimedReward(
        uint256 _rewardEpoch,
        address _dataProvider,
        address _claimer
    )
        external view
        returns (
            bool _claimed,
            uint256 _amount
        );

    /**
     * @notice Return reward epoch that will expire, when new reward epoch will start
     * @return Reward epoch id that will expire next
     */
    function getRewardEpochToExpireNext() external view returns (uint256);
}


// File contracts/tokenPools/interface/IIFtsoRewardManager.sol

// 
pragma solidity 0.7.6;

interface IIFtsoRewardManager is IFtsoRewardManager {

    event DailyAuthorizedInflationSet(uint256 authorizedAmountWei);
    event InflationReceived(uint256 amountReceivedWei);

    function activate() external;
    function deactivate() external;
    function closeExpiredRewardEpoch(uint256 _rewardEpochId) external;

    function distributeRewards(
        address[] memory addresses,
        uint256[] memory weights,
        uint256 totalWeight,
        uint256 epochId,
        address ftso,
        uint256 priceEpochDurationSeconds,
        uint256 currentRewardEpoch,
        uint256 priceEpochEndTime,
        uint256 votePowerBlock
    ) external;

    /**
     * @notice Returns the information on unclaimed reward of `_dataProvider` for `_rewardEpoch`
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing the data provider
     * @return _amount              number representing the unclaimed amount
     * @return _weight              number representing the share that has not yet been claimed
     */
    function getUnclaimedReward(
        uint256 _rewardEpoch,
        address _dataProvider
    )
        external view
        returns (
            uint256 _amount,
            uint256 _weight
        );
}


// File contracts/tokenPools/interface/IITokenPool.sol

// 
pragma solidity 0.7.6;

interface IITokenPool {

    /**
     * @notice Return token pool supply data
     * @return _foundationAllocatedFundsWei     Foundation allocated funds (wei)
     * @return _totalInflationAuthorizedWei     Total inflation authorized amount (wei)
     * @return _totalClaimedWei                 Total claimed amount (wei)
     */
    function getTokenPoolSupplyData() external view returns (
        uint256 _foundationAllocatedFundsWei,
        uint256 _totalInflationAuthorizedWei,
        uint256 _totalClaimedWei
    );
}


// File contracts/genesis/interface/IFtsoGenesis.sol

// 
pragma solidity 0.7.6;


interface IFtsoGenesis {
    
    /**
     * @notice Submits price hash for current epoch - only price submitter
     * @param _sender               Sender address
     * @param _epochId              Target epoch id to which hashes are submitted
     * @param _hash                 Hashed price and random number
     * @notice Emits PriceHashSubmitted event. 
     */
    function submitPriceHashSubmitter(address _sender, uint256 _epochId, bytes32 _hash) external;

    /**
     * @notice Reveals submitted price during epoch reveal period - only price submitter
     * @param _voter                Voter address
     * @param _epochId              Id of the epoch in which the price hash was submitted
     * @param _price                Submitted price in USD
     * @param _random               Submitted random number
     * @notice The hash of _price and _random must be equal to the submitted hash
     * @notice Emits PriceRevealed event
     */
    function revealPriceSubmitter(
        address _voter,
        uint256 _epochId,
        uint256 _price,
        uint256 _random,
        uint256 _wNatVP
    ) external;

    /**
     * @notice Get (and cache) wNat vote power for specified voter and given epoch id
     * @param _voter                Voter address
     * @param _epochId              Id of the epoch in which the price hash was submitted
     * @return wNat vote power
     */
    function wNatVotePowerCached(address _voter, uint256 _epochId) external returns (uint256);
}


// File contracts/userInterfaces/IFtso.sol

// 
pragma solidity 0.7.6;

interface IFtso {
    enum PriceFinalizationType {
        // initial state
        NOT_FINALIZED,
        // median calculation used to decide price
        WEIGHTED_MEDIAN,
        // low turnout - price decided from average of trusted addresses
        TRUSTED_ADDRESSES,
        // low turnout + no votes from trusted addresses - price copied from previous epoch
        PREVIOUS_PRICE_COPIED,
        // price decided from average of trusted addresses - triggered due to an exception
        TRUSTED_ADDRESSES_EXCEPTION,
        // previous price copied - triggered due to an exception
        PREVIOUS_PRICE_COPIED_EXCEPTION
    }

    // events
    event PriceHashSubmitted(
        address indexed submitter, uint256 indexed epochId, bytes32 hash, uint256 timestamp
    );

    event PriceRevealed(
        address indexed voter, uint256 indexed epochId, uint256 price, uint256 random, uint256 timestamp,
        uint256 votePowerNat, uint256 votePowerAsset
    );

    event PriceFinalized(
        uint256 indexed epochId, uint256 price, bool rewardedFtso,
        uint256 lowRewardPrice, uint256 highRewardPrice, PriceFinalizationType finalizationType,
        uint256 timestamp
    );

    event PriceEpochInitializedOnFtso(
        uint256 indexed epochId, uint256 endTime, uint256 timestamp
    );

    event LowTurnout(
        uint256 indexed epochId,
        uint256 natTurnout,
        uint256 lowNatTurnoutThresholdBIPS,
        uint256 timestamp
    );

    /**
     * @notice Returns if FTSO is active
     */
    function active() external view returns (bool);

    /**
     * @notice Returns the FTSO symbol
     */
    function symbol() external view returns (string memory);

    /**
     * @notice Returns current epoch id
     */
    function getCurrentEpochId() external view returns (uint256);

    /**
     * @notice Returns id of the epoch which was opened for price submission at the specified timestamp
     * @param _timestamp            Timestamp as seconds from unix epoch
     */
    function getEpochId(uint256 _timestamp) external view returns (uint256);

    /**
     * @notice Returns random number of the specified epoch
     * @param _epochId              Id of the epoch
     */
    function getRandom(uint256 _epochId) external view returns (uint256);
    
    /**
     * @notice Returns asset price consented in specific epoch
     * @param _epochId              Id of the epoch
     * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS
     */
    function getEpochPrice(uint256 _epochId) external view returns (uint256);

    /**
     * @notice Returns current epoch data
     * @return _epochId                 Current epoch id
     * @return _epochSubmitEndTime      End time of the current epoch price submission as seconds from unix epoch
     * @return _epochRevealEndTime      End time of the current epoch price reveal as seconds from unix epoch
     * @return _votePowerBlock          Vote power block for the current epoch
     * @return _fallbackMode            Current epoch in fallback mode - only votes from trusted addresses will be used
     * @dev half-closed intervals - end time not included
     */
    function getPriceEpochData() external view returns (
        uint256 _epochId,
        uint256 _epochSubmitEndTime,
        uint256 _epochRevealEndTime,
        uint256 _votePowerBlock,
        bool _fallbackMode
    );

    /**
     * @notice Returns current epoch data
     * @return _firstEpochStartTime         First epoch start time
     * @return _submitPeriod                Submit period in seconds
     * @return _revealPeriod                Reveal period in seconds
     */
    function getPriceEpochConfiguration() external view returns (
        uint256 _firstEpochStartTime,
        uint256 _submitPeriod,
        uint256 _revealPeriod
    );
    
    /**
     * @notice Returns asset price submitted by voter in specific epoch
     * @param _epochId              Id of the epoch
     * @param _voter                Address of the voter
     * @return Price in USD multiplied by ASSET_PRICE_USD_DECIMALS
     */
    function getEpochPriceForVoter(uint256 _epochId, address _voter) external view returns (uint256);

    /**
     * @notice Returns current asset price
     * @return _price               Price in USD multiplied by ASSET_PRICE_USD_DECIMALS
     * @return _timestamp           Time when price was updated for the last time
     */
    function getCurrentPrice() external view returns (uint256 _price, uint256 _timestamp);

    /**
     * @notice Returns current random number
     */
    function getCurrentRandom() external view returns (uint256);
}


// File @openzeppelin/contracts/token/ERC20/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}


// File contracts/userInterfaces/IGovernanceVotePower.sol

// 
pragma solidity 0.7.6;

interface IGovernanceVotePower {
    /**
     * @notice Delegate all governance vote power of `msg.sender` to `_to`.
     * @param _to The address of the recipient
     **/
    function delegate(address _to) external;

    /**
     * @notice Undelegate all governance vote power that `msg.sender` has delegated.
     **/
    function undelegate() external;

    /**
    * @notice Get the governance vote power of `_who` at block `_blockNumber`
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Governance vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256);
}


// File contracts/userInterfaces/IVPContractEvents.sol

// 
pragma solidity 0.7.6;

interface IVPContractEvents {
    /**
     * Event triggered when an account delegates or undelegates another account. 
     * Definition: `votePowerFromTo(from, to)` is `changed` from `priorVotePower` to `newVotePower`.
     * For undelegation, `newVotePower` is 0.
     *
     * Note: the event is always emitted from VPToken's `writeVotePowerContract`.
     */
    event Delegate(address indexed from, address indexed to, uint256 priorVotePower, uint256 newVotePower);
    
    /**
     * Event triggered only when account `delegator` revokes delegation to `delegatee`
     * for a single block in the past (typically the current vote block).
     *
     * Note: the event is always emitted from VPToken's `writeVotePowerContract` and/or `readVotePowerContract`.
     */
    event Revoke(address indexed delegator, address indexed delegatee, uint256 votePower, uint256 blockNumber);
}


// File contracts/userInterfaces/IVPToken.sol

// 
pragma solidity 0.7.6;



interface IVPToken is IERC20 {
    /**
     * @notice Delegate by percentage `_bips` of voting power to `_to` from `msg.sender`.
     * @param _to The address of the recipient
     * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent).
     *   Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`).
     **/
    function delegate(address _to, uint256 _bips) external;
    
    /**
     * @notice Explicitly delegate `_amount` of voting power to `_to` from `msg.sender`.
     * @param _to The address of the recipient
     * @param _amount An explicit vote power amount to be delegated.
     *   Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`).
     **/    
    function delegateExplicit(address _to, uint _amount) external;

    /**
    * @notice Revoke all delegation from sender to `_who` at given block. 
    *    Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`.
    *    Block `_blockNumber` must be in the past. 
    *    This method should be used only to prevent rogue delegate voting in the current voting block.
    *    To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit.
    * @param _who Address of the delegatee
    * @param _blockNumber The block number at which to revoke delegation.
    */
    function revokeDelegationAt(address _who, uint _blockNumber) external;
    
    /**
     * @notice Undelegate all voting power for delegates of `msg.sender`
     *    Can only be used with percentage delegation.
     *    Does not reset delegation mode back to NOTSET.
     **/
    function undelegateAll() external;
    
    /**
     * @notice Undelegate all explicit vote power by amount delegates for `msg.sender`.
     *    Can only be used with explicit delegation.
     *    Does not reset delegation mode back to NOTSET.
     * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, 
     *   so the caller must supply them.
     * @return The amount still delegated (in case the list of delegates was incomplete).
     */
    function undelegateAllExplicit(address[] memory _delegateAddresses) external returns (uint256);


    /**
     * @dev Should be compatible with ERC20 method
     */
    function name() external view returns (string memory);

    /**
     * @dev Should be compatible with ERC20 method
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Should be compatible with ERC20 method
     */
    function decimals() external view returns (uint8);
    

    /**
     * @notice Total amount of tokens at a specific `_blockNumber`.
     * @param _blockNumber The block number when the totalSupply is queried
     * @return The total amount of tokens at `_blockNumber`
     **/
    function totalSupplyAt(uint _blockNumber) external view returns(uint256);

    /**
     * @dev Queries the token balance of `_owner` at a specific `_blockNumber`.
     * @param _owner The address from which the balance will be retrieved.
     * @param _blockNumber The block number when the balance is queried.
     * @return The balance at `_blockNumber`.
     **/
    function balanceOfAt(address _owner, uint _blockNumber) external view returns (uint256);

    
    /**
     * @notice Get the current total vote power.
     * @return The current total vote power (sum of all accounts' vote powers).
     */
    function totalVotePower() external view returns(uint256);
    
    /**
    * @notice Get the total vote power at block `_blockNumber`
    * @param _blockNumber The block number at which to fetch.
    * @return The total vote power at the block  (sum of all accounts' vote powers).
    */
    function totalVotePowerAt(uint _blockNumber) external view returns(uint256);

    /**
     * @notice Get the current vote power of `_owner`.
     * @param _owner The address to get voting power.
     * @return Current vote power of `_owner`.
     */
    function votePowerOf(address _owner) external view returns(uint256);
    
    /**
    * @notice Get the vote power of `_owner` at block `_blockNumber`
    * @param _owner The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_owner` at `_blockNumber`.
    */
    function votePowerOfAt(address _owner, uint256 _blockNumber) external view returns(uint256);


    /**
     * @notice Get the delegation mode for '_who'. This mode determines whether vote power is
     *  allocated by percentage or by explicit value. Once the delegation mode is set, 
     *  it never changes, even if all delegations are removed.
     * @param _who The address to get delegation mode.
     * @return delegation mode: 0 = NOTSET, 1 = PERCENTAGE, 2 = AMOUNT (i.e. explicit)
     */
    function delegationModeOf(address _who) external view returns(uint256);
        
    /**
    * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @return The delegated vote power.
    */
    function votePowerFromTo(address _from, address _to) external view returns(uint256);
    
    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _blockNumber The block number at which to fetch.
    * @return The delegated vote power.
    */
    function votePowerFromToAt(address _from, address _to, uint _blockNumber) external view returns(uint256);
    
    /**
     * @notice Compute the current undelegated vote power of `_owner`
     * @param _owner The address to get undelegated voting power.
     * @return The unallocated vote power of `_owner`
     */
    function undelegatedVotePowerOf(address _owner) external view returns(uint256);
    
    /**
     * @notice Get the undelegated vote power of `_owner` at given block.
     * @param _owner The address to get undelegated voting power.
     * @param _blockNumber The block number at which to fetch.
     * @return The undelegated vote power of `_owner` (= owner's own balance minus all delegations from owner)
     */
    function undelegatedVotePowerOfAt(address _owner, uint256 _blockNumber) external view returns(uint256);
    
    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `_bips` of `_who`. Returned in two separate positional arrays.
    * @param _who The address to get delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOf(address _who)
        external view 
        returns (
            address[] memory _delegateAddresses,
            uint256[] memory _bips,
            uint256 _count, 
            uint256 _delegationMode
        );
        
    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `pcts` of `_who`. Returned in two separate positional arrays.
    * @param _who The address to get delegations.
    * @param _blockNumber The block for which we want to know the delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOfAt(address _who, uint256 _blockNumber)
        external view 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips, 
            uint256 _count, 
            uint256 _delegationMode
        );

    /**
     * Returns VPContract used for readonly operations (view methods).
     * The only non-view method that might be called on it is `revokeDelegationAt`.
     *
     * @notice `readVotePowerContract` is almost always equal to `writeVotePowerContract`
     * except during upgrade from one VPContract to a new version (which should happen
     * rarely or never and will be anounced before).
     *
     * @notice You shouldn't call any methods on VPContract directly, all are exposed
     * via VPToken (and state changing methods are forbidden from direct calls). 
     * This is the reason why this method returns `IVPContractEvents` - it should only be used
     * for listening to events (`Revoke` only).
     */
    function readVotePowerContract() external view returns (IVPContractEvents);

    /**
     * Returns VPContract used for state changing operations (non-view methods).
     * The only non-view method that might be called on it is `revokeDelegationAt`.
     *
     * @notice `writeVotePowerContract` is almost always equal to `readVotePowerContract`
     * except during upgrade from one VPContract to a new version (which should happen
     * rarely or never and will be anounced before). In the case of upgrade,
     * `writeVotePowerContract` will be replaced first to establish delegations, and
     * after some perio (e.g. after a reward epoch ends) `readVotePowerContract` will be set equal to it.
     *
     * @notice You shouldn't call any methods on VPContract directly, all are exposed
     * via VPToken (and state changing methods are forbidden from direct calls). 
     * This is the reason why this method returns `IVPContractEvents` - it should only be used
     * for listening to events (`Delegate` and `Revoke` only).
     */
    function writeVotePowerContract() external view returns (IVPContractEvents);
    
    /**
     * When set, allows token owners to participate in governance voting
     * and delegate governance vote power.
     */
    function governanceVotePower() external view returns (IGovernanceVotePower);
}


// File contracts/token/interface/IICleanable.sol

// 
pragma solidity 0.7.6;

interface IICleanable {
    /**
     * Set the contract that is allowed to call history cleaning methods.
     */
    function setCleanerContract(address _cleanerContract) external;
    
    /**
     * Set the cleanup block number.
     * Historic data for the blocks before `cleanupBlockNumber` can be erased,
     * history before that block should never be used since it can be inconsistent.
     * In particular, cleanup block number must be before current vote power block.
     * @param _blockNumber The new cleanup block number.
     */
    function setCleanupBlockNumber(uint256 _blockNumber) external;
    
    /**
     * Set the contract that is allowed to set cleanupBlockNumber.
     * Usually this will be an instance of CleanupBlockNumberManager.
     */
    function setCleanupBlockNumberManager(address _cleanupBlockNumberManager) external;
    
    /**
     * Get the current cleanup block number.
     */
    function cleanupBlockNumber() external view returns (uint256);
}


// File contracts/token/interface/IIVPContract.sol

// 
pragma solidity 0.7.6;



interface IIVPContract is IICleanable, IVPContractEvents {
    /**
     * Update vote powers when tokens are transfered.
     * Also update delegated vote powers for percentage delegation
     * and check for enough funds for explicit delegations.
     **/
    function updateAtTokenTransfer(
        address _from, 
        address _to, 
        uint256 _fromBalance,
        uint256 _toBalance,
        uint256 _amount
    ) external;

    /**
     * @notice Delegate `_bips` percentage of voting power to `_to` from `_from`
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _balance The delegator's current balance
     * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent).
     *   Not cummulative - every call resets the delegation value (and value of 0 revokes delegation).
     **/
    function delegate(
        address _from, 
        address _to, 
        uint256 _balance, 
        uint256 _bips
    ) external;
    
    /**
     * @notice Explicitly delegate `_amount` of voting power to `_to` from `msg.sender`.
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _balance The delegator's current balance
     * @param _amount An explicit vote power amount to be delegated.
     *   Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`).
     **/    
    function delegateExplicit(
        address _from, 
        address _to, 
        uint256 _balance, 
        uint _amount
    ) external;    

    /**
     * @notice Revoke all delegation from sender to `_who` at given block. 
     *    Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`.
     *    Block `_blockNumber` must be in the past. 
     *    This method should be used only to prevent rogue delegate voting in the current voting block.
     *    To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit.
     * @param _from The address of the delegator
     * @param _who Address of the delegatee
     * @param _balance The delegator's current balance
     * @param _blockNumber The block number at which to revoke delegation.
     **/
    function revokeDelegationAt(
        address _from, 
        address _who, 
        uint256 _balance,
        uint _blockNumber
    ) external;
    
        /**
     * @notice Undelegate all voting power for delegates of `msg.sender`
     *    Can only be used with percentage delegation.
     *    Does not reset delegation mode back to NOTSET.
     * @param _from The address of the delegator
     **/
    function undelegateAll(
        address _from,
        uint256 _balance
    ) external;
    
    /**
     * @notice Undelegate all explicit vote power by amount delegates for `msg.sender`.
     *    Can only be used with explicit delegation.
     *    Does not reset delegation mode back to NOTSET.
     * @param _from The address of the delegator
     * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, 
     *   so the caller must supply them.
     * @return The amount still delegated (in case the list of delegates was incomplete).
     */
    function undelegateAllExplicit(
        address _from, 
        address[] memory _delegateAddresses
    ) external returns (uint256);
    
    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    *   Reads/updates cache and upholds revocations.
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAtCached(address _who, uint256 _blockNumber) external returns(uint256);
    
    /**
     * @notice Get the current vote power of `_who`.
     * @param _who The address to get voting power.
     * @return Current vote power of `_who`.
     */
    function votePowerOf(address _who) external view returns(uint256);
    
    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256);

    /**
     * Return vote powers for several addresses in a batch.
     * @param _owners The list of addresses to fetch vote power of.
     * @param _blockNumber The block number at which to fetch.
     * @return A list of vote powers.
     */    
    function batchVotePowerOfAt(
        address[] memory _owners, 
        uint256 _blockNumber
    )
        external view returns(uint256[] memory);

    /**
    * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _balance The delegator's current balance
    * @return The delegated vote power.
    */
    function votePowerFromTo(
        address _from, 
        address _to, 
        uint256 _balance
    ) external view returns(uint256);
    
    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _balance The delegator's current balance
    * @param _blockNumber The block number at which to fetch.
    * @return The delegated vote power.
    */
    function votePowerFromToAt(
        address _from, 
        address _to, 
        uint256 _balance,
        uint _blockNumber
    ) external view returns(uint256);

    /**
     * @notice Compute the current undelegated vote power of `_owner`
     * @param _owner The address to get undelegated voting power.
     * @param _balance Owner's current balance
     * @return The unallocated vote power of `_owner`
     */
    function undelegatedVotePowerOf(
        address _owner,
        uint256 _balance
    ) external view returns(uint256);

    /**
     * @notice Get the undelegated vote power of `_owner` at given block.
     * @param _owner The address to get undelegated voting power.
     * @param _blockNumber The block number at which to fetch.
     * @return The undelegated vote power of `_owner` (= owner's own balance minus all delegations from owner)
     */
    function undelegatedVotePowerOfAt(
        address _owner, 
        uint256 _balance,
        uint256 _blockNumber
    ) external view returns(uint256);

    /**
     * @notice Get the delegation mode for '_who'. This mode determines whether vote power is
     *  allocated by percentage or by explicit value.
     * @param _who The address to get delegation mode.
     * @return Delegation mode (NOTSET=0, PERCENTAGE=1, AMOUNT=2))
     */
    function delegationModeOf(address _who) external view returns (uint256);
    
    /**
    * @notice Get the vote power delegation `_delegateAddresses` 
    *  and `pcts` of an `_owner`. Returned in two separate positional arrays.
    * @param _owner The address to get delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOf(
        address _owner
    )
        external view 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        );

    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `pcts` of an `_owner`. Returned in two separate positional arrays.
    * @param _owner The address to get delegations.
    * @param _blockNumber The block for which we want to know the delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOfAt(
        address _owner,
        uint256 _blockNumber
    )
        external view 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        );

    /**
     * The VPToken (or some other contract) that owns this VPContract.
     * All state changing methods may be called only from this address.
     * This is because original msg.sender is sent in `_from` parameter
     * and we must be sure that it cannot be faked by directly calling VPContract.
     * Owner token is also used in case of replacement to recover vote powers from balances.
     */
    function ownerToken() external view returns (IVPToken);
    
    /**
     * Return true if this IIVPContract is configured to be used as a replacement for other contract.
     * It means that vote powers are not necessarily correct at the initialization, therefore
     * every method that reads vote power must check whether it is initialized for that address and block.
     */
    function isReplacement() external view returns (bool);
}


// File contracts/token/interface/IIGovernanceVotePower.sol

// 
pragma solidity 0.7.6;


interface IIGovernanceVotePower is IGovernanceVotePower {
    /**
     * Update vote powers when tokens are transfered.
     **/
    function updateAtTokenTransfer(
        address _from, 
        address _to, 
        uint256 _fromBalance,
        uint256 _toBalance,
        uint256 _amount
    ) external;
    
    /**
     * Set the cleanup block number.
     * Historic data for the blocks before `cleanupBlockNumber` can be erased,
     * history before that block should never be used since it can be inconsistent.
     * In particular, cleanup block number must be before current vote power block.
     * @param _blockNumber The new cleanup block number.
     */
    function setCleanupBlockNumber(uint256 _blockNumber) external;
    
    /**
     * Set the contract that is allowed to call history cleaning methods.
     */
    function setCleanerContract(address _cleanerContract) external;
        
   /**
    * @notice Get the token that this governance vote power contract belongs to.
    */
    function ownerToken() external view returns(IVPToken);
}


// File contracts/token/interface/IIVPToken.sol

// 
pragma solidity 0.7.6;





interface IIVPToken is IVPToken, IICleanable {
    /**
     * Sets new governance vote power contract that allows token owners to participate in governance voting
     * and delegate governance vote power. 
     */
    function setGovernanceVotePower(IIGovernanceVotePower _governanceVotePower) external;
    
    /**
    * @notice Get the total vote power at block `_blockNumber` using cache.
    *   It tries to read the cached value and if not found, reads the actual value and stores it in cache.
    *   Can only be used if `_blockNumber` is in the past, otherwise reverts.    
    * @param _blockNumber The block number at which to fetch.
    * @return The total vote power at the block (sum of all accounts' vote powers).
    */
    function totalVotePowerAtCached(uint256 _blockNumber) external returns(uint256);
    
    /**
    * @notice Get the vote power of `_owner` at block `_blockNumber` using cache.
    *   It tries to read the cached value and if not found, reads the actual value and stores it in cache.
    *   Can only be used if _blockNumber is in the past, otherwise reverts.    
    * @param _owner The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_owner` at `_blockNumber`.
    */
    function votePowerOfAtCached(address _owner, uint256 _blockNumber) external returns(uint256);

    /**
     * Return vote powers for several addresses in a batch.
     * @param _owners The list of addresses to fetch vote power of.
     * @param _blockNumber The block number at which to fetch.
     * @return A list of vote powers.
     */    
    function batchVotePowerOfAt(
        address[] memory _owners, 
        uint256 _blockNumber
    ) external view returns(uint256[] memory);
}


// File contracts/ftso/interface/IIFtso.sol

// 
pragma solidity 0.7.6;



interface IIFtso is IFtso, IFtsoGenesis {

    /// function finalizePriceReveal
    /// called by reward manager only on correct timing.
    /// if price reveal period for epoch x ended. finalize.
    /// iterate list of price submissions
    /// find weighted median
    /// find adjucant 50% of price submissions.
    /// Allocate reward for any price submission which is same as a "winning" submission
    function finalizePriceEpoch(uint256 _epochId, bool _returnRewardData) external
        returns(
            address[] memory _eligibleAddresses,
            uint256[] memory _natWeights,
            uint256 _totalNatWeight
        );

    function averageFinalizePriceEpoch(uint256 _epochId) external;

    function forceFinalizePriceEpoch(uint256 _epochId) external;

    // activateFtso will be called by ftso manager once ftso is added 
    // before this is done, FTSO can't run
    function activateFtso(
        uint256 _firstEpochStartTs,
        uint256 _epochPeriod,
        uint256 _revealPeriod
    ) external;

    function deactivateFtso() external;

    // update initial price and timestamp - only if not active
    function updateInitialPrice(uint256 _initialPriceUSD, uint256 _initialPriceTimestamp) external;

    function configureEpochs(
        uint256 _maxVotePowerNatThresholdFraction,
        uint256 _maxVotePowerAssetThresholdFraction,
        uint256 _lowAssetUSDThreshold,
        uint256 _highAssetUSDThreshold,
        uint256 _highAssetTurnoutThresholdBIPS,
        uint256 _lowNatTurnoutThresholdBIPS,
        address[] memory _trustedAddresses
    ) external;

    function setAsset(IIVPToken _asset) external;

    function setAssetFtsos(IIFtso[] memory _assetFtsos) external;

    // current vote power block will update per reward epoch. 
    // the FTSO doesn't have notion of reward epochs.
    // reward manager only can set this data. 
    function setVotePowerBlock(uint256 _blockNumber) external;

    function initializeCurrentEpochStateForReveal(uint256 _circulatingSupplyNat, bool _fallbackMode) external;
  
    /**
     * @notice Returns the FTSO asset
     * @dev Asset is null in case of multi-asset FTSO
     */
    function getAsset() external view returns (IIVPToken);

    /**
     * @notice Returns the Asset FTSOs
     * @dev AssetFtsos is not null only in case of multi-asset FTSO
     */
    function getAssetFtsos() external view returns (IIFtso[] memory);

    /**
     * @notice Returns current configuration of epoch state
     * @return _maxVotePowerNatThresholdFraction        High threshold for native token vote power per voter
     * @return _maxVotePowerAssetThresholdFraction      High threshold for asset vote power per voter
     * @return _lowAssetUSDThreshold            Threshold for low asset vote power
     * @return _highAssetUSDThreshold           Threshold for high asset vote power
     * @return _highAssetTurnoutThresholdBIPS   Threshold for high asset turnout
     * @return _lowNatTurnoutThresholdBIPS      Threshold for low nat turnout
     * @return _trustedAddresses                Trusted addresses - use their prices if low nat turnout is not achieved
     */
    function epochsConfiguration() external view 
        returns (
            uint256 _maxVotePowerNatThresholdFraction,
            uint256 _maxVotePowerAssetThresholdFraction,
            uint256 _lowAssetUSDThreshold,
            uint256 _highAssetUSDThreshold,
            uint256 _highAssetTurnoutThresholdBIPS,
            uint256 _lowNatTurnoutThresholdBIPS,
            address[] memory _trustedAddresses
        );

    /**
     * @notice Returns parameters necessary for approximately replicating vote weighting.
     * @return _assets                  the list of Assets that are accounted in vote
     * @return _assetMultipliers        weight of each asset in (multiasset) ftso, mutiplied by TERA
     * @return _totalVotePowerNat       total native token vote power at block
     * @return _totalVotePowerAsset     total combined asset vote power at block
     * @return _assetWeightRatio        ratio of combined asset vp vs. native token vp (in BIPS)
     * @return _votePowerBlock          vote powewr block for given epoch
     */
    function getVoteWeightingParameters() external view 
        returns (
            IIVPToken[] memory _assets,
            uint256[] memory _assetMultipliers,
            uint256 _totalVotePowerNat,
            uint256 _totalVotePowerAsset,
            uint256 _assetWeightRatio,
            uint256 _votePowerBlock
        );

    function wNat() external view returns (IIVPToken);
}


// File contracts/genesis/interface/IFtsoRegistryGenesis.sol

// 
pragma solidity 0.7.6;

interface IFtsoRegistryGenesis {

    function getFtsos(uint256[] memory _indices) external view returns(IFtsoGenesis[] memory _ftsos);
}


// File contracts/userInterfaces/IPriceSubmitter.sol

// 
pragma solidity 0.7.6;


interface IPriceSubmitter {
    /**
     * Event emitted when price hashes were submitted through PriceSubmitter.
     * @param submitter the address of the sender
     * @param epochId current price epoch id
     * @param ftsos array of ftsos that correspond to the indexes in call
     * @param hashes the submitted hashes
     * @param timestamp current block timestamp
     */
    event PriceHashesSubmitted(
        address indexed submitter,
        uint256 indexed epochId,
        IFtsoGenesis[] ftsos,
        bytes32[] hashes,
        uint256 timestamp
    );

    /**
     * Event emitted when prices were revealed through PriceSubmitter.
     * @param voter the address of the sender
     * @param epochId id of the epoch in which the price hash was submitted
     * @param ftsos array of ftsos that correspond to the indexes in the call
     * @param prices the submitted prices
     * @param timestamp current block timestamp
     */
    event PricesRevealed(
        address indexed voter,
        uint256 indexed epochId,
        IFtsoGenesis[] ftsos,
        uint256[] prices,
        uint256[] randoms,
        uint256 timestamp
    );
    
    /**
     * @notice Submits price hashes for current epoch
     * @param _epochId              Target epoch id to which hashes are submitted
     * @param _ftsoIndices          List of ftso indices
     * @param _hashes               List of hashed price and random number
     * @notice Emits PriceHashesSubmitted event
     */
    function submitPriceHashes(
        uint256 _epochId,
        uint256[] memory _ftsoIndices,
        bytes32[] memory _hashes
    ) external;

    /**
     * @notice Reveals submitted prices during epoch reveal period
     * @param _epochId              Id of the epoch in which the price hashes was submitted
     * @param _ftsoIndices          List of ftso indices
     * @param _prices               List of submitted prices in USD
     * @param _randoms              List of submitted random numbers
     * @notice The hash of _price and _random must be equal to the submitted hash
     * @notice Emits PricesRevealed event
     */
    function revealPrices(
        uint256 _epochId,
        uint256[] memory _ftsoIndices,
        uint256[] memory _prices,
        uint256[] memory _randoms
    ) external;

    /**
     * Returns bitmap of all ftso's for which `_voter` is allowed to submit prices/hashes.
     * If voter is allowed to vote for ftso at index (see *_FTSO_INDEX), the corrsponding
     * bit in the result will be 1.
     */    
    function voterWhitelistBitmap(address _voter) external view returns (uint256);

    function getVoterWhitelister() external view returns (address);
    function getFtsoRegistry() external view returns (IFtsoRegistryGenesis);
    function getFtsoManager() external view returns (address);
}


// File contracts/userInterfaces/IFtsoManager.sol

// 
pragma solidity 0.7.6;


interface IFtsoManager {

    event FtsoAdded(IIFtso ftso, bool add);
    event FallbackMode(bool fallbackMode);
    event FtsoFallbackMode(IIFtso ftso, bool fallbackMode);
    event RewardEpochFinalized(uint256 votepowerBlock, uint256 startBlock);
    event PriceEpochFinalized(address chosenFtso, uint256 rewardEpochId);
    event InitializingCurrentEpochStateForRevealFailed(IIFtso ftso, uint256 epochId);
    event FinalizingPriceEpochFailed(IIFtso ftso, uint256 epochId, IFtso.PriceFinalizationType failingType);
    event DistributingRewardsFailed(address ftso, uint256 epochId);

    function active() external view returns (bool);
    
    function getPriceSubmitter() external view returns (IPriceSubmitter);

    function getCurrentRewardEpoch() external view returns (uint256);

    function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view returns (uint256);
    
    function getCurrentPriceEpochData() external view 
        returns (
            uint256 _priceEpochId,
            uint256 _priceEpochStartTimestamp,
            uint256 _priceEpochEndTimestamp,
            uint256 _priceEpochRevealEndTimestamp,
            uint256 _currentTimestamp
        );

    function getFtsos() external view returns (IIFtso[] memory _ftsos);

    function getPriceEpochConfiguration() external view 
        returns (
            uint256 _firstPriceEpochStartTs,
            uint256 _priceEpochDurationSeconds,
            uint256 _revealEpochDurationSeconds
        );

    function getFallbackMode() external view 
        returns (
            bool _fallbackMode,
            IIFtso[] memory _ftsos,
            bool[] memory _ftsoInFallbackMode
        );
}


// File contracts/ftso/interface/IIFtsoManager.sol

// 
pragma solidity 0.7.6;



interface IIFtsoManager is IFtsoManager {

    event ClosingExpiredRewardEpochFailed(uint256 _rewardEpoch);
    event CleanupBlockNumberManagerUnset();
    event CleanupBlockNumberManagerFailedForBlock(uint256 blockNumber);

    function activate() external;

    function setGovernanceParameters(
        uint256 _maxVotePowerNatThresholdFraction,
        uint256 _maxVotePowerAssetThresholdFraction,
        uint256 _lowAssetUSDThreshold,
        uint256 _highAssetUSDThreshold,
        uint256 _highAssetTurnoutThresholdBIPS,
        uint256 _lowNatTurnoutThresholdBIPS,
        uint256 _rewardExpiryOffsetSeconds,
        address[] memory _trustedAddresses
    ) external;

    function addFtso(IIFtso _ftso) external;

    function removeFtso(IIFtso _ftso) external;

    function replaceFtso(
        IIFtso _ftsoToRemove,
        IIFtso _ftsoToAdd,
        bool copyCurrentPrice,
        bool copyAssetOrAssetFtsos
    ) external;

    function setFtsoAsset(IIFtso _ftso, IIVPToken _asset) external;

    function setFtsoAssetFtsos(IIFtso _ftso, IIFtso[] memory _assetFtsos) external;

    function setFallbackMode(bool _fallbackMode) external;

    function setFtsoFallbackMode(IIFtso _ftso, bool _fallbackMode) external;
}


// File contracts/governance/implementation/GovernedBase.sol

// 
pragma solidity 0.7.6;


/**
 * @title Governed Base
 * @notice This abstract base class defines behaviors for a governed contract.
 * @dev This class is abstract so that specific behaviors can be defined for the constructor.
 *   Contracts should not be left ungoverned, but not all contract will have a constructor
 *   (for example those pre-defined in genesis).
 **/
abstract contract GovernedBase {
    address public governance;
    address public proposedGovernance;
    bool private initialised;

    event GovernanceProposed(address proposedGovernance);
    event GovernanceUpdated (address oldGovernance, address newGoveranance);

    modifier onlyGovernance () {
        require (msg.sender == governance, "only governance");
        _;
    }

    constructor(address _governance) {
        if (_governance != address(0)) {
            initialise(_governance);
        }
    }

    /**
     * @notice First of a two step process for turning over governance to another address.
     * @param _governance The address to propose to receive governance role.
     * @dev Must hold governance to propose another address.
     */
    function proposeGovernance(address _governance) external onlyGovernance {
        proposedGovernance = _governance;
        emit GovernanceProposed(_governance);
    }

    /**
     * @notice Once proposed, claimant can claim the governance role as the second of a two-step process.
     */
    function claimGovernance() external {
        require(msg.sender == proposedGovernance, "not claimaint");

        emit GovernanceUpdated(governance, proposedGovernance);
        governance = proposedGovernance;
        proposedGovernance = address(0);
    }

    /**
     * @notice In a one-step process, turn over governance to another address.
     * @dev Must hold governance to transfer.
     */
    function transferGovernance(address _governance) external onlyGovernance {
        emit GovernanceUpdated(governance, _governance);
        governance = _governance;
        proposedGovernance = address(0);
    }

    /**
     * @notice Initialize the governance address if not first initialized.
     */
    function initialise(address _governance) public virtual {
        require(initialised == false, "initialised != false");

        initialised = true;
        emit GovernanceUpdated(governance, _governance);
        governance = _governance;
        proposedGovernance = address(0);
    }
}


// File contracts/governance/implementation/Governed.sol

// 
pragma solidity 0.7.6;

/**
 * @title Governed
 * @dev For deployed, governed contracts, enforce a non-zero address at create time.
 **/
contract Governed is GovernedBase {
    constructor(address _governance) GovernedBase(_governance) {
        require(_governance != address(0), "_governance zero");
    }
}


// File contracts/inflation/interface/IIInflationReceiver.sol

// 
pragma solidity 0.7.6;

interface IIInflationReceiver {
    /**
     * Notify the receiver that it is entitled to receive `_toAuthorizeWei` inflation amount.
     * @param _toAuthorizeWei the amount of inflation that can be awarded in the coming day
     */
    function setDailyAuthorizedInflation(uint256 _toAuthorizeWei) external;
    
    /**
     * Receive native tokens from inflation.
     */
    function receiveInflation() external payable;

    /**
     * Inflation receivers have a reference to the Inflation contract.
     */
    function getInflationAddress() external returns(address);
}


// File @openzeppelin/contracts/math/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}


// File @openzeppelin/contracts/math/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}


// File contracts/token/lib/CheckPointHistory.sol

// 
pragma solidity 0.7.6;


/**
 * @title Check Point History library
 * @notice A contract to manage checkpoints as of a given block.
 * @dev Store value history by block number with detachable state.
 **/
library CheckPointHistory {
    using SafeMath for uint256;

    /**
     * @dev `CheckPoint` is the structure that attaches a block number to a
     *  given value; the block number attached is the one that last changed the
     *  value
     **/
    struct CheckPoint {
        // `fromBlock` is the block number that the value was generated from
        uint256 fromBlock;
        // `value` is the amount of tokens at a specific block number
        uint256 value;
    }

    struct CheckPointHistoryState {
        // `checkpoints` is an array that tracks values at non-contiguous block numbers
        CheckPoint[] checkpoints;
        // `checkpoints` before `startIndex` have been deleted
        // INVARIANT: checkpoints.length == 0 || startIndex < checkpoints.length      (strict!)
        uint256 startIndex;
    }

    /**
     * @notice Binary search of _checkpoints array.
     * @param _checkpoints An array of CheckPoint to search.
     * @param _startIndex Smallest possible index to be returned.
     * @param _blockNumber The block number to search for.
     */
    function _indexOfGreatestBlockLessThan(
        CheckPoint[] storage _checkpoints, 
        uint256 _startIndex,
        uint256 _blockNumber
    )
        private view 
        returns (uint256 index)
    {
        // Binary search of the value by given block number in the array
        uint256 min = _startIndex;
        uint256 max = _checkpoints.length.sub(1);
        while (max > min) {
            uint256 mid = (max.add(min).add(1)).div(2);
            if (_checkpoints[mid].fromBlock <= _blockNumber) {
                min = mid;
            } else {
                max = mid.sub(1);
            }
        }
        return min;
    }

    /**
     * @notice Queries the value at a specific `_blockNumber`
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _blockNumber The block number of the value active at that time
     * @return _value The value at `_blockNumber`     
     **/
    function valueAt(
        CheckPointHistoryState storage _self, 
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _value)
    {
        uint256 historyCount = _self.checkpoints.length;

        // No _checkpoints, return 0
        if (historyCount == 0) return 0;

        // Shortcut for the actual value (extra optimized for current block, to save one storage read)
        // historyCount - 1 is safe, since historyCount != 0
        if (_blockNumber >= block.number || _blockNumber >= _self.checkpoints[historyCount - 1].fromBlock) {
            return _self.checkpoints[historyCount - 1].value;
        }
        
        // guard values at start    
        uint256 startIndex = _self.startIndex;
        if (_blockNumber < _self.checkpoints[startIndex].fromBlock) {
            // reading data before `startIndex` is only safe before first cleanup
            require(startIndex == 0, "CheckPointHistory: reading from cleaned-up block");
            return 0;
        }

        // Find the block with number less than or equal to block given
        uint256 index = _indexOfGreatestBlockLessThan(_self.checkpoints, startIndex, _blockNumber);

        return _self.checkpoints[index].value;
    }

    /**
     * @notice Queries the value at `block.number`
     * @param _self A CheckPointHistoryState instance to manage.
     * @return _value The value at `block.number`
     **/
    function valueAtNow(CheckPointHistoryState storage _self) internal view returns (uint256 _value) {
        return valueAt(_self, block.number);
    }

    /**
     * @notice Writes the value at the current block.
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _value Value to write.
     **/
    function writeValue(
        CheckPointHistoryState storage _self, 
        uint256 _value
    )
        internal
    {
        uint256 historyCount = _self.checkpoints.length;
        if (historyCount == 0) {
            // checkpoints array empty, push new CheckPoint
            _self.checkpoints.push(CheckPoint({fromBlock: block.number, value: _value}));
        } else {
            // historyCount - 1 is safe, since historyCount != 0
            CheckPoint storage lastCheckpoint = _self.checkpoints[historyCount - 1];
            uint256 lastBlock = lastCheckpoint.fromBlock;
            // slither-disable-next-line incorrect-equality
            if (block.number == lastBlock) {
                // If last check point is the current block, just update
                lastCheckpoint.value = _value;
            } else {
                // we should never have future blocks in history
                assert (block.number > lastBlock);
                // push new CheckPoint
                _self.checkpoints.push(CheckPoint({fromBlock: block.number, value: _value}));
            }
        }
    }
    
    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        CheckPointHistoryState storage _self, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns (uint256)
    {
        if (_cleanupBlockNumber == 0) return 0;   // optimization for when cleaning is not enabled
        uint256 length = _self.checkpoints.length;
        if (length == 0) return 0;
        uint256 startIndex = _self.startIndex;
        // length - 1 is safe, since length != 0 (check above)
        uint256 endIndex = Math.min(startIndex.add(_count), length - 1);    // last element can never be deleted
        uint256 index = startIndex;
        // we can delete `checkpoint[index]` while the next checkpoint is at `_cleanupBlockNumber` or before
        while (index < endIndex && _self.checkpoints[index + 1].fromBlock <= _cleanupBlockNumber) {
            delete _self.checkpoints[index];
            index++;
        }
        if (index > startIndex) {   // index is the first not deleted index
            _self.startIndex = index;
        }
        return index - startIndex;  // safe: index >= startIndex at start and then increases
    }
}


// File contracts/token/lib/CheckPointsByAddress.sol

// 
pragma solidity 0.7.6;


/**
 * @title Check Points By Address library
 * @notice A contract to manage checkpoint history for a collection of addresses.
 * @dev Store value history by address, and then by block number.
 **/
library CheckPointsByAddress {
    using SafeMath for uint256;
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;

    struct CheckPointsByAddressState {
        // `historyByAddress` is the map that stores the check point history of each address
        mapping(address => CheckPointHistory.CheckPointHistoryState) historyByAddress;
    }

    /**
    /**
     * @notice Send `amount` value to `to` address from `from` address.
     * @param _self A CheckPointsByAddressState instance to manage.
     * @param _from Address of the history of from values 
     * @param _to Address of the history of to values 
     * @param _amount The amount of value to be transferred
     **/
    function transmit(
        CheckPointsByAddressState storage _self, 
        address _from, 
        address _to, 
        uint256 _amount
    )
        internal
    {
        // Shortcut
        if (_amount == 0) return;

        // Both from and to can never be zero
        assert(!(_from == address(0) && _to == address(0)));

        // Update transferer value
        if (_from != address(0)) {
            // Compute the new from balance
            uint256 newValueFrom = valueOfAtNow(_self, _from).sub(_amount);
            writeValue(_self, _from, newValueFrom);
        }

        // Update transferee value
        if (_to != address(0)) {
            // Compute the new to balance
            uint256 newValueTo = valueOfAtNow(_self, _to).add(_amount);
            writeValue(_self, _to, newValueTo);
        }
    }

    /**
     * @notice Queries the value of `_owner` at a specific `_blockNumber`.
     * @param _self A CheckPointsByAddressState instance to manage.
     * @param _owner The address from which the value will be retrieved.
     * @param _blockNumber The block number to query for the then current value.
     * @return The value at `_blockNumber` for `_owner`.
     **/
    function valueOfAt(
        CheckPointsByAddressState storage _self, 
        address _owner, 
        uint256 _blockNumber
    )
        internal view
        returns (uint256)
    {
        // Get history for _owner
        CheckPointHistory.CheckPointHistoryState storage history = _self.historyByAddress[_owner];
        // Return value at given block
        return history.valueAt(_blockNumber);
    }

    /**
     * @notice Get the value of the `_owner` at the current `block.number`.
     * @param _self A CheckPointsByAddressState instance to manage.
     * @param _owner The address of the value is being requested.
     * @return The value of `_owner` at the current block.
     **/
    function valueOfAtNow(CheckPointsByAddressState storage _self, address _owner) internal view returns (uint256) {
        return valueOfAt(_self, _owner, block.number);
    }

    /**
     * @notice Writes the `value` at the current block number for `_owner`.
     * @param _self A CheckPointsByAddressState instance to manage.
     * @param _owner The address of `_owner` to write.
     * @param _value The value to write.
     * @dev Sender must be the owner of the contract.
     **/
    function writeValue(
        CheckPointsByAddressState storage _self, 
        address _owner, 
        uint256 _value
    )
        internal
    {
        // Get history for _owner
        CheckPointHistory.CheckPointHistoryState storage history = _self.historyByAddress[_owner];
        // Write the value
        history.writeValue(_value);
    }
    
    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        CheckPointsByAddressState storage _self, 
        address _owner, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns (uint256)
    {
        if (_owner != address(0)) {
            return _self.historyByAddress[_owner].cleanupOldCheckpoints(_count, _cleanupBlockNumber);
        }
        return 0;
    }
}


// File contracts/token/lib/CheckPointHistoryCache.sol

// 
pragma solidity 0.7.6;


library CheckPointHistoryCache {
    using SafeMath for uint256;
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;
    
    struct CacheState {
        // mapping blockNumber => (value + 1)
        mapping(uint256 => uint256) cache;
    }
    
    function valueAt(
        CacheState storage _self,
        CheckPointHistory.CheckPointHistoryState storage _checkPointHistory,
        uint256 _blockNumber
    )
        internal returns (uint256 _value, bool _cacheCreated)
    {
        // is it in cache?
        uint256 cachedValue = _self.cache[_blockNumber];
        if (cachedValue != 0) {
            return (cachedValue - 1, false);    // safe, cachedValue != 0
        }
        // read from _checkPointHistory
        uint256 historyValue = _checkPointHistory.valueAt(_blockNumber);
        _self.cache[_blockNumber] = historyValue.add(1);  // store to cache (add 1 to differentiate from empty)
        return (historyValue, true);
    }
    
    function deleteAt(
        CacheState storage _self,
        uint256 _blockNumber
    )
        internal returns (uint256 _deleted)
    {
        if (_self.cache[_blockNumber] != 0) {
            _self.cache[_blockNumber] = 0;
            return 1;
        }
        return 0;
    }
}


// File contracts/token/implementation/CheckPointable.sol

// 
pragma solidity 0.7.6;




/**
 * @title Check Pointable ERC20 Behavior
 * @notice ERC20 behavior which adds balance check point features.
 **/
abstract contract CheckPointable {
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;
    using CheckPointsByAddress for CheckPointsByAddress.CheckPointsByAddressState;
    using CheckPointHistoryCache for CheckPointHistoryCache.CacheState;
    using SafeMath for uint256;

    // The number of history cleanup steps executed for every write operation.
    // It is more than 1 to make as certain as possible that all history gets cleaned eventually.
    uint256 private constant CLEANUP_COUNT = 2;
    
    // Private member variables
    CheckPointsByAddress.CheckPointsByAddressState private balanceHistory;
    CheckPointHistory.CheckPointHistoryState private totalSupply;
    CheckPointHistoryCache.CacheState private totalSupplyCache;

    // Historic data for the blocks before `cleanupBlockNumber` can be erased,
    // history before that block should never be used since it can be inconsistent.
    uint256 private cleanupBlockNumber;
    
    // Address of the contract that is allowed to call methods for history cleaning.
    address private cleanerContract;
    
    /**
     * Emitted when a total supply cache entry is created.
     * Allows history cleaners to track total supply cache cleanup opportunities off-chain.
     */
    event CreatedTotalSupplyCache(uint256 _blockNumber);
    
    // Most cleanup opportunities can be deduced from standard event 
    // Transfer(from, to, amount):
    //   - balance history for `from` (if nonzero) and `to` (if nonzero)
    //   - total supply history when either `from` or `to` is zero
    
    modifier notBeforeCleanupBlock(uint256 _blockNumber) {
        require(_blockNumber >= cleanupBlockNumber, "CheckPointable: reading from cleaned-up block");
        _;
    }
    
    modifier onlyCleaner {
        require(msg.sender == cleanerContract, "Only cleaner contract");
        _;
    }
    
    /**
     * @dev Queries the token balance of `_owner` at a specific `_blockNumber`.
     * @param _owner The address from which the balance will be retrieved.
     * @param _blockNumber The block number when the balance is queried.
     * @return _balance The balance at `_blockNumber`.
     **/
    function balanceOfAt(address _owner, uint256 _blockNumber)
        public virtual view 
        notBeforeCleanupBlock(_blockNumber) 
        returns (uint256 _balance)
    {
        return balanceHistory.valueOfAt(_owner, _blockNumber);
    }

    /**
     * @notice Burn current token `amount` for `owner` of checkpoints at current block.
     * @param _owner The address of the owner to burn tokens.
     * @param _amount The amount to burn.
     */
    function _burnForAtNow(address _owner, uint256 _amount) internal virtual {
        uint256 newBalance = balanceOfAt(_owner, block.number).sub(_amount, "Burn too big for owner");
        balanceHistory.writeValue(_owner, newBalance);
        balanceHistory.cleanupOldCheckpoints(_owner, CLEANUP_COUNT, cleanupBlockNumber);
        totalSupply.writeValue(totalSupplyAt(block.number).sub(_amount, "Burn too big for total supply"));
        totalSupply.cleanupOldCheckpoints(CLEANUP_COUNT, cleanupBlockNumber);
    }

    /**
     * @notice Mint current token `amount` for `owner` of checkpoints at current block.
     * @param _owner The address of the owner to burn tokens.
     * @param _amount The amount to burn.
     */
    function _mintForAtNow(address _owner, uint256 _amount) internal virtual {
        uint256 newBalance = balanceOfAt(_owner, block.number).add(_amount);
        balanceHistory.writeValue(_owner, newBalance);
        balanceHistory.cleanupOldCheckpoints(_owner, CLEANUP_COUNT, cleanupBlockNumber);
        totalSupply.writeValue(totalSupplyAt(block.number).add(_amount));
        totalSupply.cleanupOldCheckpoints(CLEANUP_COUNT, cleanupBlockNumber);
    }

    /**
     * @notice Total amount of tokens at a specific `_blockNumber`.
     * @param _blockNumber The block number when the _totalSupply is queried
     * @return _totalSupply The total amount of tokens at `_blockNumber`
     **/
    function totalSupplyAt(uint256 _blockNumber)
        public virtual view 
        notBeforeCleanupBlock(_blockNumber)
        returns(uint256 _totalSupply)
    {
        return totalSupply.valueAt(_blockNumber);
    }

    /**
     * @notice Total amount of tokens at a specific `_blockNumber`.
     * @param _blockNumber The block number when the _totalSupply is queried
     * @return _totalSupply The total amount of tokens at `_blockNumber`
     **/
    function _totalSupplyAtCached(uint256 _blockNumber) internal 
        notBeforeCleanupBlock(_blockNumber)
        returns(uint256 _totalSupply)
    {
        // use cache only for the past (the value will never change)
        require(_blockNumber < block.number, "Can only be used for past blocks");
        (uint256 value, bool cacheCreated) = totalSupplyCache.valueAt(totalSupply, _blockNumber);
        if (cacheCreated) emit CreatedTotalSupplyCache(_blockNumber);
        return value;
    }

    /**
     * @notice Transmit token `_amount` `_from` address `_to` address of checkpoints at current block.
     * @param _from The address of the sender.
     * @param _to The address of the receiver.
     * @param _amount The amount to transmit.
     */
    function _transmitAtNow(address _from, address _to, uint256 _amount) internal virtual {
        balanceHistory.transmit(_from, _to, _amount);
        balanceHistory.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
        balanceHistory.cleanupOldCheckpoints(_to, CLEANUP_COUNT, cleanupBlockNumber);
    }
    
    /**
     * Set the cleanup block number.
     */
    function _setCleanupBlockNumber(uint256 _blockNumber) internal {
        require(_blockNumber >= cleanupBlockNumber, "Cleanup block number must never decrease");
        require(_blockNumber < block.number, "Cleanup block must be in the past");
        cleanupBlockNumber = _blockNumber;
    }

    /**
     * Get the cleanup block number.
     */
    function _cleanupBlockNumber() internal view returns (uint256) {
        return cleanupBlockNumber;
    }
    
    /**
     * @notice Update history at token transfer, the CheckPointable part of `_beforeTokenTransfer` hook.
     * @param _from The address of the sender.
     * @param _to The address of the receiver.
     * @param _amount The amount to transmit.
     */
    function _updateBalanceHistoryAtTransfer(address _from, address _to, uint256 _amount) internal virtual {
        if (_from == address(0)) {
            // mint checkpoint balance data for transferee
            _mintForAtNow(_to, _amount);
        } else if (_to == address(0)) {
            // burn checkpoint data for transferer
            _burnForAtNow(_from, _amount);
        } else {
            // transfer checkpoint balance data
            _transmitAtNow(_from, _to, _amount);
        }
    }

    // history cleanup methods

    /**
     * Set the contract that is allowed to call history cleaning methods.
     */
    function _setCleanerContract(address _cleanerContract) internal {
        cleanerContract = _cleanerContract;
    }

    /**
     * Delete balance checkpoints that expired (i.e. are before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _owner balance owner account address
     * @param _count maximum number of checkpoints to delete
     * @return the number of checkpoints deleted
     */    
    function balanceHistoryCleanup(address _owner, uint256 _count) external onlyCleaner returns (uint256) {
        return balanceHistory.cleanupOldCheckpoints(_owner, _count, cleanupBlockNumber);
    }
    
    /**
     * Delete total supply checkpoints that expired (i.e. are before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _count maximum number of checkpoints to delete
     * @return the number of checkpoints deleted
     */    
    function totalSupplyHistoryCleanup(uint256 _count) external onlyCleaner returns (uint256) {
        return totalSupply.cleanupOldCheckpoints(_count, cleanupBlockNumber);
    }
    
    /**
     * Delete total supply cache entry that expired (i.e. is before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _blockNumber the block number for which total supply value was cached
     * @return the number of cache entries deleted (always 0 or 1)
     */    
    function totalSupplyCacheCleanup(uint256 _blockNumber) external onlyCleaner returns (uint256) {
        require(_blockNumber < cleanupBlockNumber, "No cleanup after cleanup block");
        return totalSupplyCache.deleteAt(_blockNumber);
    }
}


// File @openzeppelin/contracts/utils/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}


// File @openzeppelin/contracts/token/ERC20/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;



/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * 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) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

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

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}


// File contracts/utils/implementation/SafePct.sol

// 

pragma solidity 0.7.6;

/**
 * @dev Compute percentages safely without phantom overflows.
 *
 * Intermediate operations can overflow even when the result will always
 * fit into computed type. Developers usually
 * assume that overflows raise errors. `SafePct` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafePct {
    using SafeMath for uint256;
    /**
     * Requirements:
     *
     * - intermediate operations must revert on overflow
     */
    function mulDiv(uint256 x, uint256 y, uint256 z) internal pure returns (uint256) {
        require(z > 0, "Division by zero");

        if (x == 0) return 0;
        uint256 xy = x * y;
        if (xy / x == y) { // no overflow happened - same as in SafeMath mul
            return xy / z;
        }

        //slither-disable-next-line divide-before-multiply
        uint256 a = x / z;
        uint256 b = x % z; // x = a * z + b

        //slither-disable-next-line divide-before-multiply
        uint256 c = y / z;
        uint256 d = y % z; // y = c * z + d

        return (a.mul(c).mul(z)).add(a.mul(d)).add(b.mul(c)).add(b.mul(d).div(z));
    }
}


// File contracts/token/implementation/VPToken.sol

// 
pragma solidity 0.7.6;











/**
 * @title Vote Power Token
 * @dev An ERC20 token to enable the holder to delegate voting power
 *  equal 1-1 to their balance, with history tracking by block.
 **/
contract VPToken is IIVPToken, ERC20, CheckPointable, Governed {
    using SafeMath for uint256;
    using SafePct for uint256;

    // the VPContract to use for reading vote powers and delegations
    IIVPContract private readVpContract;

    // the VPContract to use for writing vote powers and delegations
    // normally same as `readVpContract` except during switch
    // when reading happens from the old and writing goes to the new VPContract
    IIVPContract private writeVpContract;
    
    // the contract to use for governance vote power and delegation
    // here only to properly update governance vp during transfers -
    // all actual operations go directly to governance vp contract
    IIGovernanceVotePower private governanceVP;
    
    // the contract that is allowed to set cleanupBlockNumber
    // usually this will be an instance of CleanupBlockNumberManager
    address private cleanupBlockNumberManager;
    
    /**
     * When true, the argument to `setWriteVpContract` must be a vpContract
     * with `isReplacement` set to `true`. To be used for creating the correct VPContract.
     */
    bool public needsReplacementVPContract = false;

    /**
     * Event used to track history of VPToken -> VPContract / GovernanceVotePower 
     * associations (e.g. by external cleaners).
     * @param _contractType 0 = read VPContract, 1 = write VPContract, 2 = governance vote power
     * @param _oldContractAddress vote power contract address before change
     * @param _newContractAddress vote power contract address after change
     */ 
    event VotePowerContractChanged(uint256 _contractType, address _oldContractAddress, address _newContractAddress);
    
    constructor(
        address _governance,
        //slither-disable-next-line shadowing-local
        string memory _name, 
        //slither-disable-next-line shadowing-local
        string memory _symbol
    )
        Governed(_governance) ERC20(_name, _symbol) 
    {
        /* empty block */
    }
    
    /**
     * @dev Should be compatible with ERC20 method
     */
    function name() public view override(ERC20, IVPToken) returns (string memory) {
        return ERC20.name();
    }

    /**
     * @dev Should be compatible with ERC20 method
     */
    function symbol() public view override(ERC20, IVPToken) returns (string memory) {
        return ERC20.symbol();
    }

    /**
     * @dev Should be compatible with ERC20 method
     */
    function decimals() public view override(ERC20, IVPToken) returns (uint8) {
        return ERC20.decimals();
    }

    /**
     * @notice Total amount of tokens at a specific `_blockNumber`.
     * @param _blockNumber The block number when the totalSupply is queried
     * @return The total amount of tokens at `_blockNumber`
     **/
    function totalSupplyAt(uint256 _blockNumber) public view override(CheckPointable, IVPToken) returns(uint256) {
        return CheckPointable.totalSupplyAt(_blockNumber);
    }

    /**
     * @dev Queries the token balance of `_owner` at a specific `_blockNumber`.
     * @param _owner The address from which the balance will be retrieved.
     * @param _blockNumber The block number when the balance is queried.
     * @return The balance at `_blockNumber`.
     **/
    function balanceOfAt(
        address _owner, 
        uint256 _blockNumber
    )
        public view 
        override(CheckPointable, IVPToken)
        returns (uint256)
    {
        return CheckPointable.balanceOfAt(_owner, _blockNumber);
    }
    
    /**
     * @notice Delegate `_bips` of voting power to `_to` from `msg.sender`
     * @param _to The address of the recipient
     * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent).
     *   Not cummulative - every call resets the delegation value (and value of 0 revokes delegation).
     **/
    function delegate(address _to, uint256 _bips) external override {
        // Get the current balance of sender and delegate by percentage _to recipient
        _checkWriteVpContract().delegate(msg.sender, _to, balanceOf(msg.sender), _bips);
    }

    /**
     * @notice Delegate `_amount` of voting power to `_to` from `msg.sender`
     * @param _to The address of the recipient
     * @param _amount An explicit vote power amount to be delegated.
     *   Not cummulative - every call resets the delegation value (and value of 0 revokes delegation).
     **/    
    function delegateExplicit(address _to, uint256 _amount) external override {
        _checkWriteVpContract().delegateExplicit(msg.sender, _to, balanceOf(msg.sender), _amount);
    }

    /**
     * @notice Compute the current undelegated vote power of `_owner`
     * @param _owner The address to get undelegated voting power.
     * @return The unallocated vote power of `_owner`
     */
    function undelegatedVotePowerOf(address _owner) external view override returns(uint256) {
        return _checkReadVpContract().undelegatedVotePowerOf(_owner, balanceOf(_owner));
    }

    /**
     * @notice Get the undelegated vote power of `_owner` at given block.
     * @param _owner The address to get undelegated voting power.
     * @param _blockNumber The block number at which to fetch.
     * @return The unallocated vote power of `_owner`
     */
    function undelegatedVotePowerOfAt(address _owner, uint256 _blockNumber) external view override returns (uint256) {
        return _checkReadVpContract()
            .undelegatedVotePowerOfAt(_owner, balanceOfAt(_owner, _blockNumber), _blockNumber);
    }

    /**
     * @notice Undelegate all voting power for delegates of `msg.sender`
     **/
    function undelegateAll() external override {
        _checkWriteVpContract().undelegateAll(msg.sender, balanceOf(msg.sender));
    }

    /**
     * @notice Undelegate all explicit vote power by amount delegates for `msg.sender`.
     * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, 
     *   so the caller must supply them.
     * @return _remainingDelegation The amount still delegated (in case the list of delegates was incomplete).
     */
    function undelegateAllExplicit(
        address[] memory _delegateAddresses
    )
        external override 
        returns (uint256 _remainingDelegation)
    {
        return _checkWriteVpContract().undelegateAllExplicit(msg.sender, _delegateAddresses);
    }
    
    /**
    * @notice Revoke all delegation from sender to `_who` at given block. 
    *    Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`.
    *    Block `_blockNumber` must be in the past. 
    *    This method should be used only to prevent rogue delegate voting in the current voting block.
    *    To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit.
    */
    function revokeDelegationAt(address _who, uint256 _blockNumber) public override {
        IIVPContract writeVPC = writeVpContract;
        IIVPContract readVPC = readVpContract;
        if (address(writeVPC) != address(0)) {
            writeVPC.revokeDelegationAt(msg.sender, _who, balanceOfAt(msg.sender, _blockNumber), _blockNumber);
        }
        if (address(readVPC) != address(writeVPC) && address(readVPC) != address(0)) {
            try readVPC.revokeDelegationAt(msg.sender, _who, balanceOfAt(msg.sender, _blockNumber), _blockNumber) {
            } catch {
                // do nothing
            }
        }
    }

    /**
    * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @return votePower The delegated vote power.
    */
    function votePowerFromTo(
        address _from, 
        address _to
    )
        external view override 
        returns(uint256)
    {
        return _checkReadVpContract().votePowerFromTo(_from, _to, balanceOf(_from));
    }
    
    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _blockNumber The block number at which to fetch.
    * @return The delegated vote power.
    */
    function votePowerFromToAt(
        address _from, 
        address _to, 
        uint256 _blockNumber
    )
        external view override 
        returns(uint256)
    {
        return _checkReadVpContract().votePowerFromToAt(_from, _to, balanceOfAt(_from, _blockNumber), _blockNumber);
    }
    
    /**
     * @notice Get the current total vote power.
     * @return The current total vote power (sum of all accounts' vote powers).
     */
    function totalVotePower() external view override returns(uint256) {
        return totalSupply();
    }

    /**
    * @notice Get the total vote power at block `_blockNumber`
    * @param _blockNumber The block number at which to fetch.
    * @return The total vote power at the block  (sum of all accounts' vote powers).
    */
    function totalVotePowerAt(uint256 _blockNumber) external view override returns(uint256) {
        return totalSupplyAt(_blockNumber);
    }

    /**
    * @notice Get the total vote power at block `_blockNumber` using cache.
    *   It tries to read the cached value and if not found, reads the actual value and stores it in cache.
    *   Can only be used if `_blockNumber` is in the past, otherwise reverts.    
    * @param _blockNumber The block number at which to fetch.
    * @return The total vote power at the block (sum of all accounts' vote powers).
    */
    function totalVotePowerAtCached(uint256 _blockNumber) public override returns(uint256) {
        return _totalSupplyAtCached(_blockNumber);
    }
    
    /**
     * @notice Get the delegation mode for '_who'. This mode determines whether vote power is
     *  allocated by percentage or by explicit value. Once the delegation mode is set, 
     *  it never changes, even if all delegations are removed.
     * @param _who The address to get delegation mode.
     * @return delegation mode: 0 = NOTSET, 1 = PERCENTAGE, 2 = AMOUNT (i.e. explicit)
     */
    function delegationModeOf(address _who) external view override returns (uint256) {
        return _checkReadVpContract().delegationModeOf(_who);
    }

    /**
     * @notice Get the current vote power of `_owner`.
     * @param _owner The address to get voting power.
     * @return Current vote power of `_owner`.
     */
    function votePowerOf(address _owner) external view override returns(uint256) {
        return _checkReadVpContract().votePowerOf(_owner);
    }


    /**
    * @notice Get the vote power of `_owner` at block `_blockNumber`
    * @param _owner The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_owner` at `_blockNumber`.
    */
    function votePowerOfAt(address _owner, uint256 _blockNumber) external view override returns(uint256) {
        return _checkReadVpContract().votePowerOfAt(_owner, _blockNumber);
    }

    /**
     * Return vote powers for several addresses in a batch.
     * @param _owners The list of addresses to fetch vote power of.
     * @param _blockNumber The block number at which to fetch.
     * @return A list of vote powers.
     */    
    function batchVotePowerOfAt(
        address[] memory _owners, 
        uint256 _blockNumber
    )
        external view override 
        returns(uint256[] memory)
    {
        return _checkReadVpContract().batchVotePowerOfAt(_owners, _blockNumber);
    }
    
    /**
    * @notice Get the vote power of `_owner` at block `_blockNumber` using cache.
    *   It tries to read the cached value and if not found, reads the actual value and stores it in cache.
    *   Can only be used if _blockNumber is in the past, otherwise reverts.    
    * @param _owner The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_owner` at `_blockNumber`.
    */
    function votePowerOfAtCached(address _owner, uint256 _blockNumber) public override returns(uint256) {
        return _checkReadVpContract().votePowerOfAtCached(_owner, _blockNumber);
    }
    
    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `_bips` of `_who`. Returned in two separate positional arrays.
    * @param _owner The address to get delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOf(
        address _owner
    )
        external view override 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        )
    {
        return _checkReadVpContract().delegatesOf(_owner);
    }
    
    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `pcts` of `_who`. Returned in two separate positional arrays.
    * @param _owner The address to get delegations.
    * @param _blockNumber The block for which we want to know the delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOfAt(
        address _owner,
        uint256 _blockNumber
    )
        external view override 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        )
    {
        return _checkReadVpContract().delegatesOfAt(_owner, _blockNumber);
    }

    // Update vote power and balance checkpoints before balances are modified. This is implemented
    // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations.
    function _beforeTokenTransfer(
        address _from, 
        address _to, 
        uint256 _amount
    )
        internal virtual 
        override(ERC20)
    {
        require(_from != _to, "Cannot transfer to self");
        
        uint256 fromBalance = balanceOf(_from);
        uint256 toBalance = balanceOf(_to);
        
        // update vote powers
        IIVPContract vpc = writeVpContract;
        if (address(vpc) != address(0)) {
            vpc.updateAtTokenTransfer(_from, _to, fromBalance, toBalance, _amount);
        } else if (!needsReplacementVPContract) {
            // transfers without vpcontract are allowed, but after they are made
            // any added vpcontract must have isReplacement set
            needsReplacementVPContract = true;
        }
        
        // update governance vote powers
        IIGovernanceVotePower gvp = governanceVP;
        if (address(gvp) != address(0)) {
            gvp.updateAtTokenTransfer(_from, _to, fromBalance, toBalance, _amount);
        }

        // update balance history
        _updateBalanceHistoryAtTransfer(_from, _to, _amount);
    }

    /**
     * Call from governance to set read VpContract on token, e.g. 
     * `vpToken.setReadVpContract(new VPContract(vpToken))`
     * Read VPContract must be set before any of the VPToken delegation or vote power reading methods are called, 
     * otherwise they will revert.
     * NOTE: If readVpContract differs from writeVpContract all reads will be "frozen" and will not reflect
     * changes (not even revokes; they may or may not reflect balance transfers).
     * @param _vpContract Read vote power contract to be used by this token.
     */
    function setReadVpContract(IIVPContract _vpContract) external onlyGovernance {
        if (address(_vpContract) != address(0)) {
            require(address(_vpContract.ownerToken()) == address(this),
                "VPContract not owned by this token");
            // set contract's cleanup block
            _vpContract.setCleanupBlockNumber(_cleanupBlockNumber());
        }
        emit VotePowerContractChanged(0, address(readVpContract), address(_vpContract));
        readVpContract = _vpContract;
    }

    /**
     * Call from governance to set write VpContract on token, e.g. 
     * `vpToken.setWriteVpContract(new VPContract(vpToken))`
     * Write VPContract must be set before any of the VPToken delegation modifying methods are called, 
     * otherwise they will revert.
     * @param _vpContract Write vote power contract to be used by this token.
     */
    function setWriteVpContract(IIVPContract _vpContract) external onlyGovernance {
        if (address(_vpContract) != address(0)) {
            require(address(_vpContract.ownerToken()) == address(this),
                "VPContract not owned by this token");
            require(!needsReplacementVPContract || _vpContract.isReplacement(),
                "VPContract not configured for replacement");
            // set contract's cleanup block
            _vpContract.setCleanupBlockNumber(_cleanupBlockNumber());
            // once a non-null vpcontract is set, every other has to have isReplacement flag set
            needsReplacementVPContract = true;
        }
        emit VotePowerContractChanged(1, address(writeVpContract), address(_vpContract));
        writeVpContract = _vpContract;
    }
    
    /**
     * Return read vpContract, ensuring that it is not zero.
     */
    function _checkReadVpContract() internal view returns (IIVPContract) {
        IIVPContract vpc = readVpContract;
        require(address(vpc) != address(0), "Token missing read VPContract");
        return vpc;
    }

    /**
     * Return write vpContract, ensuring that it is not zero.
     */
    function _checkWriteVpContract() internal view returns (IIVPContract) {
        IIVPContract vpc = writeVpContract;
        require(address(vpc) != address(0), "Token missing write VPContract");
        return vpc;
    }
    
    /**
     * Return vpContract use for reading, may be zero.
     */
    function _getReadVpContract() internal view returns (IIVPContract) {
        return readVpContract;
    }

    /**
     * Return vpContract use for writing, may be zero.
     */
    function _getWriteVpContract() internal view returns (IIVPContract) {
        return writeVpContract;
    }

    /**
     * Returns VPContract event interface used for readonly operations (view methods).
     */
    function readVotePowerContract() external view override returns (IVPContractEvents) {
        return readVpContract;
    }

    /**
     * Returns VPContract event interface used for state changing operations (non-view methods).
     */
    function writeVotePowerContract() external view override returns (IVPContractEvents) {
        return writeVpContract;
    }

    /**
     * Set the cleanup block number.
     * Historic data for the blocks before `cleanupBlockNumber` can be erased,
     * history before that block should never be used since it can be inconsistent.
     * In particular, cleanup block number must be before current vote power block.
     * @param _blockNumber The new cleanup block number.
     */
    function setCleanupBlockNumber(uint256 _blockNumber) external override {
        require(msg.sender == address(governance) || msg.sender == cleanupBlockNumberManager, 
            "only governance or manager");
        _setCleanupBlockNumber(_blockNumber);
        if (address(readVpContract) != address(0)) {
            readVpContract.setCleanupBlockNumber(_blockNumber);
        }
        if (address(writeVpContract) != address(0) && address(writeVpContract) != address(readVpContract)) {
            writeVpContract.setCleanupBlockNumber(_blockNumber);
        }
        if (address(governanceVP) != address(0)) {
            governanceVP.setCleanupBlockNumber(_blockNumber);
        }
    }
    
    /**
     * Get the current cleanup block number.
     */
    function cleanupBlockNumber() external view override returns (uint256) {
        return _cleanupBlockNumber();
    }
    
    /**
     * Set the contract that is allowed to set cleanupBlockNumber.
     * Usually this will be an instance of CleanupBlockNumberManager.
     */
    function setCleanupBlockNumberManager(address _cleanupBlockNumberManager) external override onlyGovernance {
        cleanupBlockNumberManager = _cleanupBlockNumberManager;
    }
    
    /**
     * Set the contract that is allowed to call history cleaning methods.
     */
    function setCleanerContract(address _cleanerContract) external override onlyGovernance {
        _setCleanerContract(_cleanerContract);
        if (address(readVpContract) != address(0)) {
            readVpContract.setCleanerContract(_cleanerContract);
        }
        if (address(writeVpContract) != address(0) && address(writeVpContract) != address(readVpContract)) {
            writeVpContract.setCleanerContract(_cleanerContract);
        }
        if (address(governanceVP) != address(0)) {
            governanceVP.setCleanerContract(_cleanerContract);
        }
    }
    
    /**
     * Sets new governance vote power contract that allows token owners to participate in governance voting
     * and delegate governance vote power. 
     */
    function setGovernanceVotePower(IIGovernanceVotePower _governanceVotePower) external override onlyGovernance {
        require(address(_governanceVotePower.ownerToken()) == address(this), 
            "Governance vote power contract does not belong to this token.");
        emit VotePowerContractChanged(2, address(governanceVP), address(_governanceVotePower));
        governanceVP = _governanceVotePower;
    }

    /**
     * When set, allows token owners to participate in governance voting
     * and delegate governance vote power. 
     */
     function governanceVotePower() external view override returns (IGovernanceVotePower) {
         return governanceVP;
     }
}


// File contracts/token/lib/DelegationHistory.sol

// 
pragma solidity 0.7.6;



/**
 * @title DelegationHistory library
 * @notice A contract to manage checkpoints as of a given block.
 * @dev Store value history by block number with detachable state.
 **/
library DelegationHistory {
    using SafeMath for uint256;
    using SafePct for uint256;

    uint256 public constant MAX_DELEGATES_BY_PERCENT = 2;
    string private constant MAX_DELEGATES_MSG = "Max delegates exceeded";
    
    /**
     * @dev `CheckPoint` is the structure that attaches a block number to a
     *  given value; the block number attached is the one that last changed the
     *  value
     **/
    struct CheckPoint {
        // `fromBlock` is the block number that the value was generated from
        uint256 fromBlock;
        // the list of active delegates at this time
        address[] delegates;
        // the values delegated to the corresponding delegate at this time
        uint256[] values;
    }

    struct CheckPointHistoryState {
        // `checkpoints` is an array that tracks delegations at non-contiguous block numbers
        CheckPoint[] checkpoints;
        // `checkpoints` before `startIndex` have been deleted
        // INVARIANT: checkpoints.length == 0 || startIndex < checkpoints.length      (strict!)
        uint256 startIndex;
    }

    /**
     * @notice Queries the value at a specific `_blockNumber`
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _delegate The delegate for which we need value.
     * @param _blockNumber The block number of the value active at that time
     * @return _value The value of the `_delegate` at `_blockNumber`     
     **/
    function valueOfAt(
        CheckPointHistoryState storage _self, 
        address _delegate, 
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _value)
    {
        (bool found, uint256 index) = _findGreatestBlockLessThan(_self.checkpoints, _self.startIndex, _blockNumber);
        if (!found) return 0;

        // find the delegate and return the corresponding value
        CheckPoint storage cp = _self.checkpoints[index];
        uint256 length = cp.delegates.length;
        for (uint256 i = 0; i < length; i++) {
            if (cp.delegates[i] == _delegate) {
                return cp.values[i];
            }
        }
        return 0;   // _delegate not found
    }

    /**
     * @notice Queries the value at `block.number`
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _delegate The delegate for which we need value.
     * @return _value The value at `block.number`
     **/
    function valueOfAtNow(
        CheckPointHistoryState storage _self, 
        address _delegate
    )
        internal view
        returns (uint256 _value)
    {
        return valueOfAt(_self, _delegate, block.number);
    }

    /**
     * @notice Writes the value at the current block.
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _delegate The delegate tu update.
     * @param _value The new value to set for this delegate (value `0` deletes `_delegate` from the list).
     **/
    function writeValue(
        CheckPointHistoryState storage _self, 
        address _delegate, 
        uint256 _value
    )
        internal
    {
        uint256 historyCount = _self.checkpoints.length;
        if (historyCount == 0) {
            // checkpoints array empty, push new CheckPoint
            if (_value != 0) {
                CheckPoint storage cp = _self.checkpoints.push();
                cp.fromBlock = block.number;
                cp.delegates.push(_delegate);
                cp.values.push(_value);
            }
        } else {
            // historyCount - 1 is safe, since historyCount != 0
            CheckPoint storage lastCheckpoint = _self.checkpoints[historyCount - 1];
            uint256 lastBlock = lastCheckpoint.fromBlock;
            // slither-disable-next-line incorrect-equality
            if (block.number == lastBlock) {
                // If last check point is the current block, just update
                _updateDelegates(lastCheckpoint, _delegate, _value);
            } else {
                // we should never have future blocks in history
                assert(block.number > lastBlock); 
                // last check point block is before
                CheckPoint storage cp = _self.checkpoints.push();
                cp.fromBlock = block.number;
                _copyAndUpdateDelegates(cp, lastCheckpoint, _delegate, _value);
            }
        }
    }
    
    /**
     * Get all percentage delegations active at a time.
     * @param _self A CheckPointHistoryState instance to manage.
     * @param _blockNumber The block number to query. 
     * @return _delegates The active percentage delegates at the time. 
     * @return _values The delegates' values at the time. 
     **/
    function delegationsAt(
        CheckPointHistoryState storage _self,
        uint256 _blockNumber
    )
        internal view
        returns (
            address[] memory _delegates,
            uint256[] memory _values
        )
    {
        (bool found, uint256 index) = _findGreatestBlockLessThan(_self.checkpoints, _self.startIndex, _blockNumber);
        if (!found) {
            return (new address[](0), new uint256[](0));
        }

        // copy delegates and values to memory arrays
        // (to prevent caller updating the stored value)
        CheckPoint storage cp = _self.checkpoints[index];
        uint256 length = cp.delegates.length;
        _delegates = new address[](length);
        _values = new uint256[](length);
        for (uint256 i = 0; i < length; i++) {
            _delegates[i] = cp.delegates[i];
            _values[i] = cp.values[i];
        }
    }
    
    /**
     * Get all percentage delegations active now.
     * @param _self A CheckPointHistoryState instance to manage.
     * @return _delegates The active percentage delegates. 
     * @return _values The delegates' values. 
     **/
    function delegationsAtNow(
        CheckPointHistoryState storage _self
    )
        internal view
        returns (address[] memory _delegates, uint256[] memory _values)
    {
        return delegationsAt(_self, block.number);
    }
    
    /**
     * Get the number of delegations.
     * @param _self A CheckPointHistoryState instance to query.
     * @param _blockNumber The block number to query. 
     * @return _count Count of delegations at the time.
     **/
    function countAt(
        CheckPointHistoryState storage _self,
        uint256 _blockNumber
    )
        internal view
        returns (uint256 _count)
    {
        (bool found, uint256 index) = _findGreatestBlockLessThan(_self.checkpoints, _self.startIndex, _blockNumber);
        if (!found) return 0;
        return _self.checkpoints[index].delegates.length;
    }
    
    /**
     * Get the sum of all delegation values.
     * @param _self A CheckPointHistoryState instance to query.
     * @param _blockNumber The block number to query. 
     * @return _total Total delegation value at the time.
     **/
    function totalValueAt(
        CheckPointHistoryState storage _self, 
        uint256 _blockNumber
    )
        internal view
        returns (uint256 _total)
    {
        (bool found, uint256 index) = _findGreatestBlockLessThan(_self.checkpoints, _self.startIndex, _blockNumber);
        if (!found) return 0;
        
        CheckPoint storage cp = _self.checkpoints[index];
        uint256 length = cp.values.length;
        _total = 0;
        for (uint256 i = 0; i < length; i++) {
            _total = _total.add(cp.values[i]);
        }
    }

    /**
     * Get the sum of all delegation values.
     * @param _self A CheckPointHistoryState instance to query.
     * @return _total Total delegation value at the time.
     **/
    function totalValueAtNow(
        CheckPointHistoryState storage _self
    )
        internal view
        returns (uint256 _total)
    {
        return totalValueAt(_self, block.number);
    }

    /**
     * Get the sum of all delegation values, every one scaled by `_mul/_div`.
     * @param _self A CheckPointHistoryState instance to query.
     * @param _mul The multiplier.
     * @param _div The divisor.
     * @param _blockNumber The block number to query. 
     * @return _total Total scaled delegation value at the time.
     **/
    function scaledTotalValueAt(
        CheckPointHistoryState storage _self, 
        uint256 _mul,
        uint256 _div,
        uint256 _blockNumber
    )
        internal view
        returns (uint256 _total)
    {
        (bool found, uint256 index) = _findGreatestBlockLessThan(_self.checkpoints, _self.startIndex, _blockNumber);
        if (!found) return 0;
        
        CheckPoint storage cp = _self.checkpoints[index];
        uint256 length = cp.values.length;
        _total = 0;
        for (uint256 i = 0; i < length; i++) {
            _total = _total.add(cp.values[i].mulDiv(_mul, _div));
        }
    }

    /**
     * Clear all delegations at this moment.
     * @param _self A CheckPointHistoryState instance to manage.
     */    
    function clear(CheckPointHistoryState storage _self) internal {
        if (_self.checkpoints.length > 0) {
            // add an empty checkpoint
            CheckPoint storage cp = _self.checkpoints.push();
            cp.fromBlock = block.number;
        }
    }

    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        CheckPointHistoryState storage _self, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns (uint256)
    {
        if (_cleanupBlockNumber == 0) return 0;   // optimization for when cleaning is not enabled
        uint256 length = _self.checkpoints.length;
        if (length == 0) return 0;
        uint256 startIndex = _self.startIndex;
        // length - 1 is safe, since length != 0 (check above)
        uint256 endIndex = Math.min(startIndex.add(_count), length - 1);    // last element can never be deleted
        uint256 index = startIndex;
        // we can delete `checkpoint[index]` while the next checkpoint is at `_cleanupBlockNumber` or before
        while (index < endIndex && _self.checkpoints[index + 1].fromBlock <= _cleanupBlockNumber) {
            delete _self.checkpoints[index];
            index++;
        }
        if (index > startIndex) {   // index is the first not deleted index
            _self.startIndex = index;
        }
        return index - startIndex;  // safe: index = startIndex at start and increases in loop
    }

    /////////////////////////////////////////////////////////////////////////////////
    // helper functions for writeValueAt
    
    function _copyAndUpdateDelegates(
        CheckPoint storage _cp, 
        CheckPoint storage _orig, 
        address _delegate, 
        uint256 _value
    )
        private
    {
        uint256 length = _orig.delegates.length;
        bool updated = false;
        for (uint256 i = 0; i < length; i++) {
            address origDelegate = _orig.delegates[i];
            if (origDelegate == _delegate) {
                // copy delegate, but with new value
                _appendDelegate(_cp, origDelegate, _value, i);
                updated = true;
            } else {
                // just copy the delegate with original value
                _appendDelegate(_cp, origDelegate, _orig.values[i], i);
            }
        }
        if (!updated) {
            // delegate is not in the original list, so add it
            _appendDelegate(_cp, _delegate, _value, length);
        }
    }

    function _updateDelegates(CheckPoint storage _cp, address _delegate, uint256 _value) private {
        uint256 length = _cp.delegates.length;
        uint256 i = 0;
        while (i < length && _cp.delegates[i] != _delegate) ++i;
        if (i < length) {
            if (_value != 0) {
                _cp.values[i] = _value;
            } else {
                _deleteDelegate(_cp, i, length - 1);  // length - 1 is safe:  0 <= i < length
            }
        } else {
            _appendDelegate(_cp, _delegate, _value, length);
        }
    }
    
    function _appendDelegate(CheckPoint storage _cp, address _delegate, uint256 _value, uint256 _length) private {
        if (_value != 0) {
            require(_length < MAX_DELEGATES_BY_PERCENT, MAX_DELEGATES_MSG);
            _cp.delegates.push(_delegate);
            _cp.values.push(_value);
        }
    }
    
    function _deleteDelegate(CheckPoint storage _cp, uint256 _index, uint256 _last) private {
        if (_index < _last) {
            _cp.delegates[_index] = _cp.delegates[_last];
            _cp.values[_index] = _cp.values[_last];
        }
        _cp.delegates.pop();
        _cp.values.pop();
    }
    
    /////////////////////////////////////////////////////////////////////////////////
    // helper functions for querying
    
    /**
     * @notice Binary search of _checkpoints array.
     * @param _checkpoints An array of CheckPoint to search.
     * @param _startIndex Smallest possible index to be returned.
     * @param _blockNumber The block number to search for.
     */
    function _binarySearchGreatestBlockLessThan(
        CheckPoint[] storage _checkpoints, 
        uint256 _startIndex,
        uint256 _blockNumber
    )
        private view
        returns (uint256 _index)
    {
        // Binary search of the value by given block number in the array
        uint256 min = _startIndex;
        uint256 max = _checkpoints.length.sub(1);
        while (max > min) {
            uint256 mid = (max.add(min).add(1)).div(2);
            if (_checkpoints[mid].fromBlock <= _blockNumber) {
                min = mid;
            } else {
                max = mid.sub(1);
            }
        }
        return min;
    }

    /**
     * @notice Binary search of _checkpoints array. Extra optimized for the common case when we are 
     *   searching for the last block.
     * @param _checkpoints An array of CheckPoint to search.
     * @param _startIndex Smallest possible index to be returned.
     * @param _blockNumber The block number to search for.
     * @return _found true if value was found (only `false` if `_blockNumber` is before first 
     *   checkpoint or the checkpoint array is empty)
     * @return _index index of the newest block with number less than or equal `_blockNumber`
     */
    function _findGreatestBlockLessThan(
        CheckPoint[] storage _checkpoints, 
        uint256 _startIndex,
        uint256 _blockNumber
    )
        private view
        returns (
            bool _found,
            uint256 _index
        )
    {
        uint256 historyCount = _checkpoints.length;
        if (historyCount == 0) {
            _found = false;
        } else if (_blockNumber >= block.number || _blockNumber >= _checkpoints[historyCount - 1].fromBlock) {
            // _blockNumber >= block.number saves one storage read for reads at current block
            _found = true;
            _index = historyCount - 1;  // safe, historyCount != 0 in this branch
        } else if (_blockNumber < _checkpoints[_startIndex].fromBlock) {
            // reading data before `_startIndex` is only safe before first cleanup
            require(_startIndex == 0, "DelegationHistory: reading from cleaned-up block");
            _found = false;
        } else {
            _found = true;
            _index = _binarySearchGreatestBlockLessThan(_checkpoints, _startIndex, _blockNumber);
        }
    }
}


// File contracts/token/lib/PercentageDelegation.sol

// 
pragma solidity 0.7.6;




/**
 * @title PercentageDelegation library
 * @notice Only handles percentage delegation  
 * @notice A library to manage a group of _delegates for allocating voting power by a delegator.
 **/
library PercentageDelegation {
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;
    using DelegationHistory for DelegationHistory.CheckPointHistoryState;
    using SafeMath for uint256;
    using SafePct for uint256;

    uint256 public constant MAX_BIPS = 10000;
    string private constant MAX_BIPS_MSG = "Max delegation bips exceeded";
    
    /**
     * @dev `DelegationState` is the state structure used by this library to contain/manage
     *  a grouing of _delegates (a PercentageDelegation) for a delegator.
     */
    struct DelegationState {
        // percentages by _delegates
        DelegationHistory.CheckPointHistoryState delegation;
    }

    /**
     * @notice Add or replace an existing _delegate with allocated vote power in basis points.
     * @param _self A DelegationState instance to manage.
     * @param _delegate The address of the _delegate to add/replace
     * @param _bips Allocation of the delegation specified in basis points (1/100 of 1 percent)
     * @dev If you send a `_bips` of zero, `_delegate` will be deleted if one
     *  exists in the delegation; if zero and `_delegate` does not exist, it will not be added.
     */
    function addReplaceDelegate(
        DelegationState storage _self, 
        address _delegate, 
        uint256 _bips
    )
        internal
    {
        // Check for max delegation basis points
        assert(_bips <= MAX_BIPS);

        // Change the delegate's percentage
        _self.delegation.writeValue(_delegate, _bips);
        
        // check the total
        require(_self.delegation.totalValueAtNow() <= MAX_BIPS, MAX_BIPS_MSG);
    }

    /**
     * @notice Get the total of the explicit vote power delegation bips of all delegates at given block.
     * @param _self A DelegationState instance to manage.
     * @param _blockNumber The block to query.
     * @return _totalBips The total vote power bips delegated.
     */
    function getDelegatedTotalAt(
        DelegationState storage _self,
        uint256 _blockNumber
    )
        internal view
        returns (uint256 _totalBips)
    {
        return _self.delegation.totalValueAt(_blockNumber);
    }

    /**
     * @notice Get the total of the bips vote power delegation bips of all _delegates.
     * @param _self A DelegationState instance to manage.
     * @return _totalBips The total vote power bips delegated.
     */
    function getDelegatedTotal(
        DelegationState storage _self
    )
        internal view
        returns (uint256 _totalBips)
    {
        return _self.delegation.totalValueAtNow();
    }

    /**
     * @notice Given a _delegate address, return the bips of the vote power delegation.
     * @param _self A DelegationState instance to manage.
     * @param _delegate The delegate address to find.
     * @param _blockNumber The block to query.
     * @return _bips The percent of vote power allocated to the delegate address.
     */
    function getDelegatedValueAt(
        DelegationState storage _self, 
        address _delegate,
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _bips)
    {
        return _self.delegation.valueOfAt(_delegate, _blockNumber);
    }

    /**
     * @notice Given a delegate address, return the bips of the vote power delegation.
     * @param _self A DelegationState instance to manage.
     * @param _delegate The delegate address to find.
     * @return _bips The percent of vote power allocated to the delegate address.
     */
    function getDelegatedValue(
        DelegationState storage _self, 
        address _delegate
    )
        internal view
        returns (uint256 _bips)
    {
        return _self.delegation.valueOfAtNow(_delegate);
    }

    /**
     * @notice Returns lists of delegate addresses and corresponding values at given block.
     * @param _self A DelegationState instance to manage.
     * @param _blockNumber The block to query.
     * @return _delegates Positional array of delegation addresses.
     * @return _values Positional array of delegation percents specified in basis points (1/100 or 1 percent)
     */
    function getDelegationsAt(
        DelegationState storage _self,
        uint256 _blockNumber
    )
        internal view 
        returns (
            address[] memory _delegates,
            uint256[] memory _values
        )
    {
        return _self.delegation.delegationsAt(_blockNumber);
    }
    
    /**
     * @notice Returns lists of delegate addresses and corresponding values.
     * @param _self A DelegationState instance to manage.
     * @return _delegates Positional array of delegation addresses.
     * @return _values Positional array of delegation percents specified in basis points (1/100 or 1 percent)
     */
    function getDelegations(
        DelegationState storage _self
    )
        internal view
        returns (
            address[] memory _delegates,
            uint256[] memory _values
        ) 
    {
        return _self.delegation.delegationsAtNow();
    }
    
    /**
     * Get the number of delegations.
     * @param _self A DelegationState instance to manage.
     * @param _blockNumber The block number to query. 
     * @return _count Count of delegations at the time.
     **/
    function getCountAt(
        DelegationState storage _self,
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _count)
    {
        return _self.delegation.countAt(_blockNumber);
    }

    /**
     * Get the number of delegations.
     * @param _self A DelegationState instance to manage.
     * @return _count Count of delegations at the time.
     **/
    function getCount(
        DelegationState storage _self
    )
        internal view
        returns (uint256 _count)
    {
        return _self.delegation.countAt(block.number);
    }
    
    /**
     * @notice Get the total amount (absolute) of the vote power delegation of all delegates.
     * @param _self A DelegationState instance to manage.
     * @param _balance Owner's balance.
     * @return _totalAmount The total vote power amount delegated.
     */
    function getDelegatedTotalAmountAt(
        DelegationState storage _self, 
        uint256 _balance,
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _totalAmount)
    {
        return _self.delegation.scaledTotalValueAt(_balance, MAX_BIPS, _blockNumber);
    }
    
    /**
     * @notice Clears all delegates.
     * @param _self A DelegationState instance to manage.
     * @dev Delegation mode remains PERCENTAGE, even though the delgation is now empty.
     */
    function clear(DelegationState storage _self) internal {
        _self.delegation.clear();
    }


    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        DelegationState storage _self, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns (uint256)
    {
        return _self.delegation.cleanupOldCheckpoints(_count, _cleanupBlockNumber);
    }
}


// File contracts/token/lib/ExplicitDelegation.sol

// 
pragma solidity 0.7.6;




/**
 * @title ExplicitDelegation library
 * @notice A library to manage a group of delegates for allocating voting power by a delegator.
 **/
library ExplicitDelegation {
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;
    using CheckPointsByAddress for CheckPointsByAddress.CheckPointsByAddressState;
    using SafeMath for uint256;
    using SafePct for uint256;

    /**
     * @dev `DelegationState` is the state structure used by this library to contain/manage
     *  a grouing of delegates (a ExplicitDelegation) for a delegator.
     */
    struct DelegationState {
        CheckPointHistory.CheckPointHistoryState delegatedTotal;

        // `delegatedVotePower` is a map of delegators pointing to a map of delegates
        // containing a checkpoint history of delegated vote power balances.
        CheckPointsByAddress.CheckPointsByAddressState delegatedVotePower;
    }

    /**
     * @notice Add or replace an existing _delegate with new vote power (explicit).
     * @param _self A DelegationState instance to manage.
     * @param _delegate The address of the _delegate to add/replace
     * @param _amount Allocation of the delegation as explicit amount
     */
    function addReplaceDelegate(
        DelegationState storage _self, 
        address _delegate, 
        uint256 _amount
    )
        internal
    {
        uint256 prevAmount = _self.delegatedVotePower.valueOfAtNow(_delegate);
        uint256 newTotal = _self.delegatedTotal.valueAtNow().sub(prevAmount, "Total < 0").add(_amount);
        _self.delegatedVotePower.writeValue(_delegate, _amount);
        _self.delegatedTotal.writeValue(newTotal);
    }
    

    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        DelegationState storage _self, 
        address _owner, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns(uint256 _deleted)
    {
        _deleted = _self.delegatedTotal.cleanupOldCheckpoints(_count, _cleanupBlockNumber);
        // safe: cleanupOldCheckpoints always returns the number of deleted elements which is small, so no owerflow
        _deleted += _self.delegatedVotePower.cleanupOldCheckpoints(_owner, _count, _cleanupBlockNumber);
    }
    
    /**
     * @notice Get the _total of the explicit vote power delegation amount.
     * @param _self A DelegationState instance to manage.
     * @param _blockNumber The block to query.
     * @return _total The _total vote power amount delegated.
     */
    function getDelegatedTotalAt(
        DelegationState storage _self, uint256 _blockNumber
    )
        internal view 
        returns (uint256 _total)
    {
        return _self.delegatedTotal.valueAt(_blockNumber);
    }
    
    /**
     * @notice Get the _total of the explicit vote power delegation amount.
     * @param _self A DelegationState instance to manage.
     * @return _total The total vote power amount delegated.
     */
    function getDelegatedTotal(
        DelegationState storage _self
    )
        internal view 
        returns (uint256 _total)
    {
        return _self.delegatedTotal.valueAtNow();
    }
    
    /**
     * @notice Given a delegate address, return the explicit amount of the vote power delegation.
     * @param _self A DelegationState instance to manage.
     * @param _delegate The _delegate address to find.
     * @param _blockNumber The block to query.
     * @return _value The percent of vote power allocated to the _delegate address.
     */
    function getDelegatedValueAt(
        DelegationState storage _self, 
        address _delegate,
        uint256 _blockNumber
    )
        internal view
        returns (uint256 _value)
    {
        return _self.delegatedVotePower.valueOfAt(_delegate, _blockNumber);
    }

    /**
     * @notice Given a delegate address, return the explicit amount of the vote power delegation.
     * @param _self A DelegationState instance to manage.
     * @param _delegate The _delegate address to find.
     * @return _value The percent of vote power allocated to the _delegate address.
     */
    function getDelegatedValue(
        DelegationState storage _self, 
        address _delegate
    )
        internal view 
        returns (uint256 _value)
    {
        return _self.delegatedVotePower.valueOfAtNow(_delegate);
    }
}


// File contracts/token/lib/VotePower.sol

// 
pragma solidity 0.7.6;



/**
 * @title Vote power library
 * @notice A library to record delegate vote power balances by delegator 
 *  and delegatee.
 **/
library VotePower {
    using CheckPointHistory for CheckPointHistory.CheckPointHistoryState;
    using CheckPointsByAddress for CheckPointsByAddress.CheckPointsByAddressState;
    using SafeMath for uint256;

    /**
     * @dev `VotePowerState` is state structure used by this library to manage vote
     *  power amounts by delegator and it's delegates.
     */
    struct VotePowerState {
        // `votePowerByAddress` is the map that tracks the voting power balance
        //  of each address, by block.
        CheckPointsByAddress.CheckPointsByAddressState votePowerByAddress;
    }

    /**
     * @notice This modifier checks that both addresses are non-zero.
     * @param _delegator A delegator address.
     * @param _delegatee A delegatee address.
     */
    modifier addressesNotZero(address _delegator, address _delegatee) {
        // Both addresses cannot be zero
        assert(!(_delegator == address(0) && _delegatee == address(0)));
        _;
    }

    /**
     * @notice Burn vote power.
     * @param _self A VotePowerState instance to manage.
     * @param _owner The address of the vote power to be burned.
     * @param _amount The amount of vote power to burn.
     */
    function _burn(
        VotePowerState storage _self, 
        address _owner, 
        uint256 _amount
    )
        internal
    {
        // Shortcut
        if (_amount == 0) {
            return;
        }

        // Cannot burn the zero address
        assert(_owner != address(0));

        // Burn vote power for address
        _self.votePowerByAddress.transmit(_owner, address(0), _amount);
    }

    /**
     * @notice Delegate vote power `_amount` to `_delegatee` address from `_delegator` address.
     * @param _delegator Delegator address 
     * @param _delegatee Delegatee address
     * @param _amount The _amount of vote power to send from _delegator to _delegatee
     * @dev Amount recorded at the current block.
     **/
    function delegate(
        VotePowerState storage _self, 
        address _delegator, 
        address _delegatee,
        uint256 _amount
    )
        internal 
        addressesNotZero(_delegator, _delegatee)
    {
        // Shortcut
        if (_amount == 0) {
            return;
        }

        // Transmit vote power
        _self.votePowerByAddress.transmit(_delegator, _delegatee, _amount);
    }

    /**
     * @notice Mint vote power.
     * @param _self A VotePowerState instance to manage.
     * @param _owner The address owning the new vote power.
     * @param _amount The amount of vote power to mint.
     */
    function _mint(
        VotePowerState storage _self, 
        address _owner, 
        uint256 _amount
    )
        internal
    {
        // Shortcut
        if (_amount == 0) {
            return;
        }

        // Cannot mint the zero address
        assert(_owner != address(0));

        // Mint vote power for address
        _self.votePowerByAddress.transmit(address(0), _owner, _amount);
    }

    /**
     * @notice Transmit current vote power `_amount` from `_delegator` to `_delegatee`.
     * @param _delegator Address of delegator.
     * @param _delegatee Address of delegatee.
     * @param _amount Amount of vote power to transmit.
     */
    function transmit(
        VotePowerState storage _self, 
        address _delegator, 
        address _delegatee,
        uint256 _amount
    )
        internal
        addressesNotZero(_delegator, _delegatee)
    {
        _self.votePowerByAddress.transmit(_delegator, _delegatee, _amount);
    }

    /**
     * @notice Undelegate vote power `_amount` from `_delegatee` address 
     *  to `_delegator` address
     * @param _delegator Delegator address 
     * @param _delegatee Delegatee address
     * @param _amount The amount of vote power recovered by delegator from delegatee
     **/
    function undelegate(
        VotePowerState storage _self, 
        address _delegator, 
        address _delegatee,
        uint256 _amount
    )
        internal
        addressesNotZero(_delegator, _delegatee)
    {
        // Shortcut
        if (_amount == 0) {
            return;
        }

        // Recover vote power
        _self.votePowerByAddress.transmit(_delegatee, _delegator, _amount);
    }

    /**
     * Delete at most `_count` of the oldest checkpoints.
     * At least one checkpoint at or before `_cleanupBlockNumber` will remain 
     * (unless the history was empty to start with).
     */    
    function cleanupOldCheckpoints(
        VotePowerState storage _self, 
        address _owner, 
        uint256 _count,
        uint256 _cleanupBlockNumber
    )
        internal
        returns (uint256)
    {
        return _self.votePowerByAddress.cleanupOldCheckpoints(_owner, _count, _cleanupBlockNumber);
    }

    /**
     * @notice Get the vote power of `_who` at `_blockNumber`.
     * @param _self A VotePowerState instance to manage.
     * @param _who Address to get vote power.
     * @param _blockNumber Block number of the block to fetch vote power.
     * @return _votePower The fetched vote power.
     */
    function votePowerOfAt(
        VotePowerState storage _self, 
        address _who, 
        uint256 _blockNumber
    )
        internal view 
        returns(uint256 _votePower)
    {
        return _self.votePowerByAddress.valueOfAt(_who, _blockNumber);
    }

    /**
     * @notice Get the current vote power of `_who`.
     * @param _self A VotePowerState instance to manage.
     * @param _who Address to get vote power.
     * @return _votePower The fetched vote power.
     */
    function votePowerOfAtNow(
        VotePowerState storage _self, 
        address _who
    )
        internal view
        returns(uint256 _votePower)
    {
        return votePowerOfAt(_self, _who, block.number);
    }
}


// File contracts/token/lib/VotePowerCache.sol

// 
pragma solidity 0.7.6;


/**
 * @title Vote power library
 * @notice A library to record delegate vote power balances by delegator 
 *  and delegatee.
 **/
library VotePowerCache {
    using SafeMath for uint256;
    using VotePower for VotePower.VotePowerState;

    struct RevocationCacheRecord {
        // revoking delegation only affects cached value therefore we have to track
        // the revocation in order not to revoke twice
        // mapping delegatee => revokedValue
        mapping(address => uint256) revocations;
    }
    
    /**
     * @dev `CacheState` is state structure used by this library to manage vote
     *  power amounts by delegator and it's delegates.
     */
    struct CacheState {
        // map keccak256([address, _blockNumber]) -> (value + 1)
        mapping(bytes32 => uint256) valueCache;
        
        // map keccak256([address, _blockNumber]) -> RevocationCacheRecord
        mapping(bytes32 => RevocationCacheRecord) revocationCache;
    }

    /**
    * @notice Get the cached value at given block. If there is no cached value, original
    *    value is returned and stored to cache. Cache never gets stale, because original
    *    value can never change in a past block.
    * @param _self A VotePowerCache instance to manage.
    * @param _votePower A VotePower instance to read from if cache is empty.
    * @param _who Address to get vote power.
    * @param _blockNumber Block number of the block to fetch vote power.
    * precondition: _blockNumber < block.number
    */
    function valueOfAt(
        CacheState storage _self,
        VotePower.VotePowerState storage _votePower,
        address _who,
        uint256 _blockNumber
    )
        internal 
        returns (uint256 _value, bool _createdCache)
    {
        bytes32 key = keccak256(abi.encode(_who, _blockNumber));
        // is it in cache?
        uint256 cachedValue = _self.valueCache[key];
        if (cachedValue != 0) {
            return (cachedValue - 1, false);    // safe, cachedValue != 0
        }
        // read from _votePower
        uint256 votePowerValue = _votePower.votePowerOfAt(_who, _blockNumber);
        _writeCacheValue(_self, key, votePowerValue);
        return (votePowerValue, true);
    }

    /**
    * @notice Get the cached value at given block. If there is no cached value, original
    *    value is returned. Cache is never modified.
    * @param _self A VotePowerCache instance to manage.
    * @param _votePower A VotePower instance to read from if cache is empty.
    * @param _who Address to get vote power.
    * @param _blockNumber Block number of the block to fetch vote power.
    * precondition: _blockNumber < block.number
    */
    function valueOfAtReadonly(
        CacheState storage _self,
        VotePower.VotePowerState storage _votePower,
        address _who,
        uint256 _blockNumber
    )
        internal view 
        returns (uint256 _value)
    {
        bytes32 key = keccak256(abi.encode(_who, _blockNumber));
        // is it in cache?
        uint256 cachedValue = _self.valueCache[key];
        if (cachedValue != 0) {
            return cachedValue - 1;     // safe, cachedValue != 0
        }
        // read from _votePower
        return _votePower.votePowerOfAt(_who, _blockNumber);
    }
    
    /**
    * @notice Delete cached value for `_who` at given block.
    *   Only used for history cleanup.
    * @param _self A VotePowerCache instance to manage.
    * @param _who Address to get vote power.
    * @param _blockNumber Block number of the block to fetch vote power.
    * @return _deleted The number of cache items deleted (always 0 or 1).
    * precondition: _blockNumber < cleanupBlockNumber
    */
    function deleteValueAt(
        CacheState storage _self,
        address _who,
        uint256 _blockNumber
    )
        internal
        returns (uint256 _deleted)
    {
        bytes32 key = keccak256(abi.encode(_who, _blockNumber));
        if (_self.valueCache[key] != 0) {
            delete _self.valueCache[key];
            return 1;
        }
        return 0;
    }
    
    /**
    * @notice Revoke vote power delegation from `from` to `to` at given block.
    *   Updates cached values for the block, so they are the only vote power values respecting revocation.
    * @dev Only delegatees cached value is changed, delegator doesn't get the vote power back; so
    *   the revoked vote power is forfeit for as long as this vote power block is in use. This is needed to
    *   prevent double voting.
    * @param _self A VotePowerCache instance to manage.
    * @param _votePower A VotePower instance to read from if cache is empty.
    * @param _from The delegator.
    * @param _to The delegatee.
    * @param _revokedValue Value of delegation is not stored here, so it must be supplied by caller.
    * @param _blockNumber Block number of the block to modify.
    * precondition: _blockNumber < block.number
    */
    function revokeAt(
        CacheState storage _self,
        VotePower.VotePowerState storage _votePower,
        address _from,
        address _to,
        uint256 _revokedValue,
        uint256 _blockNumber
    )
        internal
    {
        if (_revokedValue == 0) return;
        bytes32 keyFrom = keccak256(abi.encode(_from, _blockNumber));
        if (_self.revocationCache[keyFrom].revocations[_to] != 0) {
            revert("Already revoked");
        }
        // read values and prime cacheOf
        (uint256 valueTo,) = valueOfAt(_self, _votePower, _to, _blockNumber);
        // write new values
        bytes32 keyTo = keccak256(abi.encode(_to, _blockNumber));
        _writeCacheValue(_self, keyTo, valueTo.sub(_revokedValue, "Revoked value too large"));
        // mark as revoked
        _self.revocationCache[keyFrom].revocations[_to] = _revokedValue;
    }
    
    /**
    * @notice Delete revocation from `_from` to `_to` at block `_blockNumber`.
    *   Only used for history cleanup.
    * @param _self A VotePowerCache instance to manage.
    * @param _from The delegator.
    * @param _to The delegatee.
    * @param _blockNumber Block number of the block to modify.
    * precondition: _blockNumber < cleanupBlockNumber
    */
    function deleteRevocationAt(
        CacheState storage _self,
        address _from,
        address _to,
        uint256 _blockNumber
    )
        internal
        returns (uint256 _deleted)
    {
        bytes32 keyFrom = keccak256(abi.encode(_from, _blockNumber));
        RevocationCacheRecord storage revocationRec = _self.revocationCache[keyFrom];
        uint256 value = revocationRec.revocations[_to];
        if (value != 0) {
            delete revocationRec.revocations[_to];
            return 1;
        }
        return 0;
    }

    /**
    * @notice Returns true if `from` has revoked vote pover delgation of `to` in block `_blockNumber`.
    * @param _self A VotePowerCache instance to manage.
    * @param _from The delegator.
    * @param _to The delegatee.
    * @param _blockNumber Block number of the block to fetch result.
    * precondition: _blockNumber < block.number
    */
    function revokedFromToAt(
        CacheState storage _self,
        address _from,
        address _to,
        uint256 _blockNumber
    )
        internal view
        returns (bool revoked)
    {
        bytes32 keyFrom = keccak256(abi.encode(_from, _blockNumber));
        return _self.revocationCache[keyFrom].revocations[_to] != 0;
    }
    
    function _writeCacheValue(CacheState storage _self, bytes32 _key, uint256 _value) private {
        // store to cacheOf (add 1 to differentiate from empty)
        _self.valueCache[_key] = _value.add(1);
    }
}


// File contracts/token/implementation/Delegatable.sol

// 
pragma solidity 0.7.6;







/**
 * @title Delegateable ERC20 behavior
 * @notice An ERC20 Delegateable behavior to delegate voting power
 *  of a token to delegates. This contract orchestrates interaction between
 *  managing a delegation and the vote power allocations that result.
 **/
contract Delegatable is IVPContractEvents {
    using PercentageDelegation for PercentageDelegation.DelegationState;
    using ExplicitDelegation for ExplicitDelegation.DelegationState;
    using SafeMath for uint256;
    using SafePct for uint256;
    using VotePower for VotePower.VotePowerState;
    using VotePowerCache for VotePowerCache.CacheState;

    enum DelegationMode { 
        NOTSET, 
        PERCENTAGE, 
        AMOUNT
    }

    // The number of history cleanup steps executed for every write operation.
    // It is more than 1 to make as certain as possible that all history gets cleaned eventually.
    uint256 private constant CLEANUP_COUNT = 2;
    
    string constant private UNDELEGATED_VP_TOO_SMALL_MSG = 
        "Undelegated vote power too small";

    // Map that tracks delegation mode of each address.
    mapping(address => DelegationMode) private delegationModes;

    // `percentageDelegations` is the map that tracks the percentage voting power delegation of each address.
    // Explicit delegations are tracked directly through votePower.
    mapping(address => PercentageDelegation.DelegationState) private percentageDelegations;
    
    mapping(address => ExplicitDelegation.DelegationState) private explicitDelegations;

    // `votePower` tracks all voting power balances
    VotePower.VotePowerState private votePower;

    // `votePower` tracks all voting power balances
    VotePowerCache.CacheState private votePowerCache;

    // Historic data for the blocks before `cleanupBlockNumber` can be erased,
    // history before that block should never be used since it can be inconsistent.
    uint256 private cleanupBlockNumber;
    
    // Address of the contract that is allowed to call methods for history cleaning.
    address private cleanerContract;
    
    /**
     * Emitted when a vote power cache entry is created.
     * Allows history cleaners to track vote power cache cleanup opportunities off-chain.
     */
    event CreatedVotePowerCache(address _owner, uint256 _blockNumber);
    
    // Most history cleanup opportunities can be deduced from standard events:
    // Transfer(from, to, amount):
    //  - vote power checkpoints for `from` (if nonzero) and `to` (if nonzero)
    //  - vote power checkpoints for percentage delegatees of `from` and `to` are also created,
    //    but they don't have to be checked since Delegate events are also emitted in case of 
    //    percentage delegation vote power change due to delegators balance change
    //  - Note: Transfer event is emitted from VPToken but vote power checkpoint delegationModes
    //    must be called on its writeVotePowerContract
    // Delegate(from, to, priorVP, newVP):
    //  - vote power checkpoints for `from` and `to`
    //  - percentage delegation checkpoint for `from` (if `from` uses percentage delegation mode)
    //  - explicit delegation checkpoint from `from` to `to` (if `from` uses explicit delegation mode)
    // Revoke(from, to, vp, block):
    //  - vote power cache for `from` and `to` at `block`
    //  - revocation cache block from `from` to `to` at `block`
    
    /**
     * Reading from history is not allowed before `cleanupBlockNumber`, since data before that
     * might have been deleted and is thus unreliable.
     */    
    modifier notBeforeCleanupBlock(uint256 _blockNumber) {
        require(_blockNumber >= cleanupBlockNumber, "Delegatable: reading from cleaned-up block");
        _;
    }
    
    /**
     * History cleaning methods can be called only from the cleaner address.
     */
    modifier onlyCleaner {
        require(msg.sender == cleanerContract, "Only cleaner contract");
        _;
    }

    /**
     * @notice (Un)Allocate `_owner` vote power of `_amount` across owner delegate
     *  vote power percentages.
     * @param _owner The address of the vote power owner.
     * @param _priorBalance The owner's balance before change.
     * @param _newBalance The owner's balance after change.
     */
    function _allocateVotePower(address _owner, uint256 _priorBalance, uint256 _newBalance) private {
        // Only proceed if we have a delegation by percentage
        if (delegationModes[_owner] == DelegationMode.PERCENTAGE) {
            // Get the voting delegation for the _owner
            PercentageDelegation.DelegationState storage delegation = percentageDelegations[_owner];
            // Iterate over the delegates
            (address[] memory delegates, uint256[] memory bipses) = delegation.getDelegations();
            for (uint256 i = 0; i < delegates.length; i++) {
                address delegatee = delegates[i];
                // Compute the delegated vote power for the delegatee
                uint256 priorValue = _priorBalance.mulDiv(bipses[i], PercentageDelegation.MAX_BIPS);
                uint256 newValue = _newBalance.mulDiv(bipses[i], PercentageDelegation.MAX_BIPS);
                // Compute new voting power
                if (newValue > priorValue) {
                    // increase (subtraction is safe as newValue > priorValue)
                    votePower.delegate(_owner, delegatee, newValue - priorValue);
                } else {
                    // decrease (subtraction is safe as newValue < priorValue)
                    votePower.undelegate(_owner, delegatee, priorValue - newValue);
                }
                votePower.cleanupOldCheckpoints(_owner, CLEANUP_COUNT, cleanupBlockNumber);
                votePower.cleanupOldCheckpoints(delegatee, CLEANUP_COUNT, cleanupBlockNumber);
                
                emit Delegate(_owner, delegatee, priorValue, newValue);
            }
        }
    }

    /**
     * @notice Burn `_amount` of vote power for `_owner`.
     * @param _owner The address of the _owner vote power to burn.
     * @param _ownerCurrentBalance The current token balance of the owner (which is their allocatable vote power).
     * @param _amount The amount of vote power to burn.
     */
    function _burnVotePower(address _owner, uint256 _ownerCurrentBalance, uint256 _amount) internal {
        // for PERCENTAGE delegation: reduce owner vote power allocations
        // revert with the same error as ERC20 in case transfer exceeds balance
        uint256 newOwnerBalance = _ownerCurrentBalance.sub(_amount, "ERC20: transfer amount exceeds balance");
        _allocateVotePower(_owner, _ownerCurrentBalance, newOwnerBalance);
        // for AMOUNT delegation: is there enough unallocated VP _to burn if explicitly delegated?
        require(_isTransmittable(_owner, _ownerCurrentBalance, _amount), UNDELEGATED_VP_TOO_SMALL_MSG);
        // burn vote power
        votePower._burn(_owner, _amount);
        votePower.cleanupOldCheckpoints(_owner, CLEANUP_COUNT, cleanupBlockNumber);
    }

    /**
     * @notice Get whether `_owner` current delegation can be delegated by percentage.
     * @param _owner Address of delegation to check.
     * @return True if delegation can be delegated by percentage.
     */
    function _canDelegateByPct(address _owner) internal view returns(bool) {
        // Get the delegation mode.
        DelegationMode delegationMode = delegationModes[_owner];
        // Return true if delegation is safe _to store percents, which can also
        // apply if there is not delegation mode set.
        return delegationMode == DelegationMode.NOTSET || delegationMode == DelegationMode.PERCENTAGE;
    }

    /**
     * @notice Get whether `_owner` current delegation can be delegated by amount.
     * @param _owner Address of delegation to check.
     * @return True if delegation can be delegated by amount.
     */
    function _canDelegateByAmount(address _owner) internal view returns(bool) {
        // Get the delegation mode.
        DelegationMode delegationMode = delegationModes[_owner];
        // Return true if delegation is safe to store explicit amounts, which can also
        // apply if there is not delegation mode set.
        return delegationMode == DelegationMode.NOTSET || delegationMode == DelegationMode.AMOUNT;
    }

    /**
     * @notice Delegate `_amount` of voting power to `_to` from `_from`
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _senderCurrentBalance The senders current balance (not their voting power)
     * @param _amount The amount of voting power to be delegated
     **/
    function _delegateByAmount(
        address _from, 
        address _to, 
        uint256 _senderCurrentBalance, 
        uint256 _amount
    )
        internal virtual 
    {
        require (_to != address(0), "Cannot delegate to zero");
        require (_to != _from, "Cannot delegate to self");
        require (_canDelegateByAmount(_from), "Cannot delegate by amount");
        
        // Get the vote power delegation for the sender
        ExplicitDelegation.DelegationState storage delegation = explicitDelegations[_from];
        
        // the prior value
        uint256 priorAmount = delegation.getDelegatedValue(_to);
        
        // Delegate new power
        if (_amount < priorAmount) {
            // Prior amount is greater, just reduce the delegated amount.
            // subtraction is safe since _amount < priorAmount
            votePower.undelegate(_from, _to, priorAmount - _amount);
        } else {
            // Is there enough undelegated vote power?
            uint256 availableAmount = _undelegatedVotePowerOf(_from, _senderCurrentBalance).add(priorAmount);
            require(availableAmount >= _amount, UNDELEGATED_VP_TOO_SMALL_MSG);
            // Increase the delegated amount of vote power.
            // subtraction is safe since _amount >= priorAmount
            votePower.delegate(_from, _to, _amount - priorAmount);
        }
        votePower.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
        votePower.cleanupOldCheckpoints(_to, CLEANUP_COUNT, cleanupBlockNumber);
        
        // Add/replace delegate
        delegation.addReplaceDelegate(_to, _amount);
        delegation.cleanupOldCheckpoints(_to, CLEANUP_COUNT, cleanupBlockNumber);

        // update mode if needed
        if (delegationModes[_from] != DelegationMode.AMOUNT) {
            delegationModes[_from] = DelegationMode.AMOUNT;
        }
        
        // emit event for delegation change
        emit Delegate(_from, _to, priorAmount, _amount);
    }

    /**
     * @notice Delegate `_bips` of voting power to `_to` from `_from`
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _senderCurrentBalance The senders current balance (not their voting power)
     * @param _bips The percentage of voting power in basis points (1/100 of 1 percent) to be delegated
     **/
    function _delegateByPercentage(
        address _from, 
        address _to, 
        uint256 _senderCurrentBalance, 
        uint256 _bips
    )
        internal virtual
    {
        require (_to != address(0), "Cannot delegate to zero");
        require (_to != _from, "Cannot delegate to self");
        require (_canDelegateByPct(_from), "Cannot delegate by percentage");
        
        // Get the vote power delegation for the sender
        PercentageDelegation.DelegationState storage delegation = percentageDelegations[_from];

        // Get prior percent for delegate if exists
        uint256 priorBips = delegation.getDelegatedValue(_to);
        uint256 reverseVotePower = 0;
        uint256 newVotePower = 0;

        // Add/replace delegate
        delegation.addReplaceDelegate(_to, _bips);
        delegation.cleanupOldCheckpoints(CLEANUP_COUNT, cleanupBlockNumber);
        
        // First, back out old voting power percentage, if not zero
        if (priorBips != 0) {
            reverseVotePower = _senderCurrentBalance.mulDiv(priorBips, PercentageDelegation.MAX_BIPS);
        }

        // Calculate the new vote power
        if (_bips != 0) {
            newVotePower = _senderCurrentBalance.mulDiv(_bips, PercentageDelegation.MAX_BIPS);
        }

        // Delegate new power
        if (newVotePower < reverseVotePower) {
            // subtraction is safe since newVotePower < reverseVotePower
            votePower.undelegate(_from, _to, reverseVotePower - newVotePower);
        } else {
            // subtraction is safe since newVotePower >= reverseVotePower
            votePower.delegate(_from, _to, newVotePower - reverseVotePower);
        }
        votePower.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
        votePower.cleanupOldCheckpoints(_to, CLEANUP_COUNT, cleanupBlockNumber);
        
        // update mode if needed
        if (delegationModes[_from] != DelegationMode.PERCENTAGE) {
            delegationModes[_from] = DelegationMode.PERCENTAGE;
        }

        // emit event for delegation change
        emit Delegate(_from, _to, reverseVotePower, newVotePower);
    }

    /**
     * @notice Get the delegation mode for '_who'. This mode determines whether vote power is
     *  allocated by percentage or by explicit value.
     * @param _who The address to get delegation mode.
     * @return Delegation mode
     */
    function _delegationModeOf(address _who) internal view returns (DelegationMode) {
        return delegationModes[_who];
    }

    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `_bips` of an `_owner`. Returned in two separate positional arrays.
    * @param _owner The address to get delegations.
    * @param _blockNumber The block for which we want to know the delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    */
    function _percentageDelegatesOfAt(
        address _owner,
        uint256 _blockNumber
    )
        internal view
        notBeforeCleanupBlock(_blockNumber) 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips
        )
    {
        PercentageDelegation.DelegationState storage delegation = percentageDelegations[_owner];
        address[] memory allDelegateAddresses;
        uint256[] memory allBips;
        (allDelegateAddresses, allBips) = delegation.getDelegationsAt(_blockNumber);
        // delete revoked addresses
        for (uint256 i = 0; i < allDelegateAddresses.length; i++) {
            if (votePowerCache.revokedFromToAt(_owner, allDelegateAddresses[i], _blockNumber)) {
                allBips[i] = 0;
            }
        }
        uint256 length = 0;
        for (uint256 i = 0; i < allDelegateAddresses.length; i++) {
            if (allBips[i] != 0) length++;
        }
        _delegateAddresses = new address[](length);
        _bips = new uint256[](length);
        uint256 destIndex = 0;
        for (uint256 i = 0; i < allDelegateAddresses.length; i++) {
            if (allBips[i] != 0) {
                _delegateAddresses[destIndex] = allDelegateAddresses[i];
                _bips[destIndex] = allBips[i];
                destIndex++;
            }
        }
    }

    /**
     * @notice Checks if enough undelegated vote power exists to allow a token
     *  transfer to occur if vote power is explicitly delegated.
     * @param _owner The address of transmittable vote power to check.
     * @param _ownerCurrentBalance The current balance of `_owner`.
     * @param _amount The amount to check.
     * @return True is `_amount` is transmittable.
     */
    function _isTransmittable(
        address _owner, 
        uint256 _ownerCurrentBalance, 
        uint256 _amount
    )
        private view returns(bool)
    {
        // Only proceed if we have a delegation by _amount
        if (delegationModes[_owner] == DelegationMode.AMOUNT) {
            // Return true if there is enough vote power _to cover the transfer
            return _undelegatedVotePowerOf(_owner, _ownerCurrentBalance) >= _amount;
        } else {
            // Not delegated by _amount, so transfer always allowed
            return true;
        }
    }

    /**
     * @notice Mint `_amount` of vote power for `_owner`.
     * @param _owner The address of the owner to receive new vote power.
     * @param _amount The amount of vote power to mint.
     */
    function _mintVotePower(address _owner, uint256 _ownerCurrentBalance, uint256 _amount) internal {
        votePower._mint(_owner, _amount);
        votePower.cleanupOldCheckpoints(_owner, CLEANUP_COUNT, cleanupBlockNumber);
        // Allocate newly minted vote power over delegates
        _allocateVotePower(_owner, _ownerCurrentBalance, _ownerCurrentBalance.add(_amount));
    }
    
    /**
    * @notice Revoke the vote power of `_to` at block `_blockNumber`
    * @param _from The address of the delegator
    * @param _to The delegatee address of vote power to revoke.
    * @param _senderBalanceAt The sender's balance at the block to be revoked.
    * @param _blockNumber The block number at which to revoke.
    */
    function _revokeDelegationAt(
        address _from, 
        address _to, 
        uint256 _senderBalanceAt, 
        uint256 _blockNumber
    )
        internal 
        notBeforeCleanupBlock(_blockNumber)
    {
        require(_blockNumber < block.number, "Revoke is only for the past, use undelegate for the present");
        
        // Get amount revoked
        uint256 votePowerRevoked = _votePowerFromToAtNoRevokeCheck(_from, _to, _senderBalanceAt, _blockNumber);
        
        // Revoke vote power
        votePowerCache.revokeAt(votePower, _from, _to, votePowerRevoked, _blockNumber);

        // Emit revoke event
        emit Revoke(_from, _to, votePowerRevoked, _blockNumber);
    }

    /**
    * @notice Transmit `_amount` of vote power `_from` address `_to` address.
    * @param _from The address of the sender.
    * @param _to The address of the receiver.
    * @param _fromCurrentBalance The current token balance of the transmitter.
    * @param _toCurrentBalance The current token balance of the receiver.
    * @param _amount The amount of vote power to transmit.
    */
    function _transmitVotePower(
        address _from, 
        address _to, 
        uint256 _fromCurrentBalance, 
        uint256 _toCurrentBalance,
        uint256 _amount
    )
        internal
    {
        // for PERCENTAGE delegation: reduce sender vote power allocations
        // revert with the same error as ERC20 in case transfer exceeds balance
        uint256 newFromBalance = _fromCurrentBalance.sub(_amount, "ERC20: transfer amount exceeds balance");
        _allocateVotePower(_from, _fromCurrentBalance, newFromBalance);
        // for AMOUNT delegation: transmit vote power _to receiver
        require(_isTransmittable(_from, _fromCurrentBalance, _amount), UNDELEGATED_VP_TOO_SMALL_MSG);
        votePower.transmit(_from, _to, _amount);
        votePower.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
        votePower.cleanupOldCheckpoints(_to, CLEANUP_COUNT, cleanupBlockNumber);
        // Allocate receivers new vote power according _to their delegates
        _allocateVotePower(_to, _toCurrentBalance, _toCurrentBalance.add(_amount));
    }

    /**
     * @notice Undelegate all vote power by percentage for `delegation` of `_who`.
     * @param _from The address of the delegator
     * @param _senderCurrentBalance The current balance of message sender.
     * precondition: delegationModes[_who] == DelegationMode.PERCENTAGE
     */
    function _undelegateAllByPercentage(address _from, uint256 _senderCurrentBalance) internal {
        DelegationMode delegationMode = delegationModes[_from];
        if (delegationMode == DelegationMode.NOTSET) return;
        require(delegationMode == DelegationMode.PERCENTAGE,
            "undelegateAll can only be used in percentage delegation mode");
            
        PercentageDelegation.DelegationState storage delegation = percentageDelegations[_from];
        
        // Iterate over the delegates
        (address[] memory delegates, uint256[] memory _bips) = delegation.getDelegations();
        for (uint256 i = 0; i < delegates.length; i++) {
            address to = delegates[i];
            // Compute vote power to be reversed for the delegate
            uint256 reverseVotePower = _senderCurrentBalance.mulDiv(_bips[i], PercentageDelegation.MAX_BIPS);
            // Transmit vote power back to _owner
            votePower.undelegate(_from, to, reverseVotePower);
            votePower.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
            votePower.cleanupOldCheckpoints(to, CLEANUP_COUNT, cleanupBlockNumber);
            // Emit vote power reversal event
            emit Delegate(_from, to, reverseVotePower, 0);
        }

        // Clear delegates
        delegation.clear();
        delegation.cleanupOldCheckpoints(CLEANUP_COUNT, cleanupBlockNumber);
    }

    /**
     * @notice Undelegate all vote power by amount delegates for `_from`.
     * @param _from The address of the delegator
     * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, 
     *   so the caller must supply them.
     */
    function _undelegateAllByAmount(
        address _from,
        address[] memory _delegateAddresses
    ) 
        internal 
        returns (uint256 _remainingDelegation)
    {
        DelegationMode delegationMode = delegationModes[_from];
        if (delegationMode == DelegationMode.NOTSET) return 0;
        require(delegationMode == DelegationMode.AMOUNT,
            "undelegateAllExplicit can only be used in explicit delegation mode");
            
        ExplicitDelegation.DelegationState storage delegation = explicitDelegations[_from];
        
        // Iterate over the delegates
        for (uint256 i = 0; i < _delegateAddresses.length; i++) {
            address to = _delegateAddresses[i];
            // Compute vote power _to be reversed for the delegate
            uint256 reverseVotePower = delegation.getDelegatedValue(to);
            if (reverseVotePower == 0) continue;
            // Transmit vote power back _to _owner
            votePower.undelegate(_from, to, reverseVotePower);
            votePower.cleanupOldCheckpoints(_from, CLEANUP_COUNT, cleanupBlockNumber);
            votePower.cleanupOldCheckpoints(to, CLEANUP_COUNT, cleanupBlockNumber);
            // change delagation
            delegation.addReplaceDelegate(to, 0);
            delegation.cleanupOldCheckpoints(to, CLEANUP_COUNT, cleanupBlockNumber);
            // Emit vote power reversal event
            emit Delegate(_from, to, reverseVotePower, 0);
        }
        
        return delegation.getDelegatedTotal();
    }
    
    /**
     * @notice Check if the `_owner` has made any delegations.
     * @param _owner The address of owner to get delegated vote power.
     * @return The total delegated vote power at block.
     */
    function _hasAnyDelegations(address _owner) internal view returns(bool) {
        DelegationMode delegationMode = delegationModes[_owner];
        if (delegationMode == DelegationMode.NOTSET) {
            return false;
        } else if (delegationMode == DelegationMode.AMOUNT) {
            return explicitDelegations[_owner].getDelegatedTotal() > 0;
        } else { // delegationMode == DelegationMode.PERCENTAGE
            return percentageDelegations[_owner].getCount() > 0;
        }
    }

    /**
     * @notice Get the total delegated vote power of `_owner` at some block.
     * @param _owner The address of owner to get delegated vote power.
     * @param _ownerBalanceAt The balance of the owner at that block (not their vote power).
     * @param _blockNumber The block number at which to fetch.
     * @return _votePower The total delegated vote power at block.
     */
    function _delegatedVotePowerOfAt(
        address _owner, 
        uint256 _ownerBalanceAt,
        uint256 _blockNumber
    ) 
        internal view
        notBeforeCleanupBlock(_blockNumber)
        returns(uint256 _votePower)
    {
        // Get the vote power delegation for the _owner
        DelegationMode delegationMode = delegationModes[_owner];
        if (delegationMode == DelegationMode.NOTSET) {
            return 0;
        } else if (delegationMode == DelegationMode.AMOUNT) {
            return explicitDelegations[_owner].getDelegatedTotalAt(_blockNumber);
        } else { // delegationMode == DelegationMode.PERCENTAGE
            return percentageDelegations[_owner].getDelegatedTotalAmountAt(_ownerBalanceAt, _blockNumber);
        }
    }

    /**
     * @notice Get the undelegated vote power of `_owner` at some block.
     * @param _owner The address of owner to get undelegated vote power.
     * @param _ownerBalanceAt The balance of the owner at that block (not their vote power).
     * @param _blockNumber The block number at which to fetch.
     * @return _votePower The undelegated vote power at block.
     */
    function _undelegatedVotePowerOfAt(
        address _owner, 
        uint256 _ownerBalanceAt,
        uint256 _blockNumber
    ) 
        internal view 
        notBeforeCleanupBlock(_blockNumber) 
        returns(uint256 _votePower)
    {
        // Return the current balance less delegations or zero if negative
        uint256 delegated = _delegatedVotePowerOfAt(_owner, _ownerBalanceAt, _blockNumber);
        bool overflow;
        uint256 result;
        (overflow, result) = _ownerBalanceAt.trySub(delegated);
        return result;
    }

    /**
     * @notice Get the undelegated vote power of `_owner`.
     * @param _owner The address of owner to get undelegated vote power.
     * @param _ownerCurrentBalance The current balance of the owner (not their vote power).
     * @return _votePower The undelegated vote power.
     */
    function _undelegatedVotePowerOf(
        address _owner, 
        uint256 _ownerCurrentBalance
    ) 
        internal view 
        returns(uint256 _votePower)
    {
        return _undelegatedVotePowerOfAt(_owner, _ownerCurrentBalance, block.number);
    }
    
    /**
    * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @return _votePower The delegated vote power.
    */
    function _votePowerFromTo(
        address _from, 
        address _to, 
        uint256 _currentFromBalance
    ) 
        internal view 
        returns(uint256 _votePower)
    {
        // no need for revocation check at current block
        return _votePowerFromToAtNoRevokeCheck(_from, _to, _currentFromBalance, block.number);
    }

    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _fromBalanceAt From's balance at the block `_blockNumber`.
    * @param _blockNumber The block number at which to fetch.
    * @return _votePower The delegated vote power.
    */
    function _votePowerFromToAt(
        address _from, 
        address _to, 
        uint256 _fromBalanceAt, 
        uint256 _blockNumber
    ) 
        internal view 
        notBeforeCleanupBlock(_blockNumber) 
        returns(uint256 _votePower) 
    {
        // if revoked, return 0
        if (votePowerCache.revokedFromToAt(_from, _to, _blockNumber)) return 0;
        return _votePowerFromToAtNoRevokeCheck(_from, _to, _fromBalanceAt, _blockNumber);
    }

    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    *   Private use only - ignores revocations.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _fromBalanceAt From's balance at the block `_blockNumber`.
    * @param _blockNumber The block number at which to fetch.
    * @return _votePower The delegated vote power.
    */
    function _votePowerFromToAtNoRevokeCheck(
        address _from, 
        address _to, 
        uint256 _fromBalanceAt, 
        uint256 _blockNumber
    )
        private view 
        returns(uint256 _votePower)
    {
        // assumed: notBeforeCleanupBlock(_blockNumber)
        DelegationMode delegationMode = delegationModes[_from];
        if (delegationMode == DelegationMode.NOTSET) {
            return 0;
        } else if (delegationMode == DelegationMode.PERCENTAGE) {
            uint256 _bips = percentageDelegations[_from].getDelegatedValueAt(_to, _blockNumber);
            return _fromBalanceAt.mulDiv(_bips, PercentageDelegation.MAX_BIPS);
        } else { // delegationMode == DelegationMode.AMOUNT
            return explicitDelegations[_from].getDelegatedValueAt(_to, _blockNumber);
        }
    }

    /**
     * @notice Get the current vote power of `_who`.
     * @param _who The address to get voting power.
     * @return Current vote power of `_who`.
     */
    function _votePowerOf(address _who) internal view returns(uint256) {
        return votePower.votePowerOfAtNow(_who);
    }

    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function _votePowerOfAt(
        address _who, 
        uint256 _blockNumber
    )
        internal view 
        notBeforeCleanupBlock(_blockNumber) 
        returns(uint256)
    {
        // read cached value for past blocks to respect revocations (and possibly get a cache speedup)
        if (_blockNumber < block.number) {
            return votePowerCache.valueOfAtReadonly(votePower, _who, _blockNumber);
        } else {
            return votePower.votePowerOfAtNow(_who);
        }
    }

    /**
     * Return vote powers for several addresses in a batch.
     * Only works for past blocks.
     * @param _owners The list of addresses to fetch vote power of.
     * @param _blockNumber The block number at which to fetch.
     * @return _votePowers A list of vote powers corresponding to _owners.
     */    
    function _batchVotePowerOfAt(
        address[] memory _owners, 
        uint256 _blockNumber
    ) 
        internal view 
        notBeforeCleanupBlock(_blockNumber) 
        returns(uint256[] memory _votePowers)
    {
        require(_blockNumber < block.number, "Can only be used for past blocks");
        _votePowers = new uint256[](_owners.length);
        for (uint256 i = 0; i < _owners.length; i++) {
            // read through cache, much faster if it has been set
            _votePowers[i] = votePowerCache.valueOfAtReadonly(votePower, _owners[i], _blockNumber);
        }
    }
    
    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    *   Reads/updates cache and upholds revocations.
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function _votePowerOfAtCached(
        address _who, 
        uint256 _blockNumber
    )
        internal 
        notBeforeCleanupBlock(_blockNumber)
        returns(uint256) 
    {
        require(_blockNumber < block.number, "Can only be used for past blocks");
        (uint256 vp, bool createdCache) = votePowerCache.valueOfAt(votePower, _who, _blockNumber);
        if (createdCache) emit CreatedVotePowerCache(_who, _blockNumber);
        return vp;
    }
    
    /**
     * Set the cleanup block number.
     */
    function _setCleanupBlockNumber(uint256 _blockNumber) internal {
        require(_blockNumber >= cleanupBlockNumber, "Cleanup block number must never decrease");
        require(_blockNumber < block.number, "Cleanup block must be in the past");
        cleanupBlockNumber = _blockNumber;
    }

    /**
     * Get the cleanup block number.
     */
    function _cleanupBlockNumber() internal view returns (uint256) {
        return cleanupBlockNumber;
    }
    
    /**
     * Set the contract that is allowed to call history cleaning methods.
     */
    function _setCleanerContract(address _cleanerContract) internal {
        cleanerContract = _cleanerContract;
    }
    
    // history cleanup methods
    
    /**
     * Delete vote power checkpoints that expired (i.e. are before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _owner vote power owner account address
     * @param _count maximum number of checkpoints to delete
     * @return the number of checkpoints deleted
     */    
    function votePowerHistoryCleanup(address _owner, uint256 _count) external onlyCleaner returns (uint256) {
        return votePower.cleanupOldCheckpoints(_owner, _count, cleanupBlockNumber);
    }

    /**
     * Delete vote power cache entry that expired (i.e. is before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _owner vote power owner account address
     * @param _blockNumber the block number for which total supply value was cached
     * @return the number of cache entries deleted (always 0 or 1)
     */    
    function votePowerCacheCleanup(address _owner, uint256 _blockNumber) external onlyCleaner returns (uint256) {
        require(_blockNumber < cleanupBlockNumber, "No cleanup after cleanup block");
        return votePowerCache.deleteValueAt(_owner, _blockNumber);
    }

    /**
     * Delete revocation entry that expired (i.e. is before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _from the delegator address
     * @param _to the delegatee address
     * @param _blockNumber the block number for which total supply value was cached
     * @return the number of revocation entries deleted (always 0 or 1)
     */    
    function revocationCleanup(
        address _from, 
        address _to, 
        uint256 _blockNumber
    )
        external onlyCleaner 
        returns (uint256)
    {
        require(_blockNumber < cleanupBlockNumber, "No cleanup after cleanup block");
        return votePowerCache.deleteRevocationAt(_from, _to, _blockNumber);
    }
    
    /**
     * Delete percentage delegation checkpoints that expired (i.e. are before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _owner balance owner account address
     * @param _count maximum number of checkpoints to delete
     * @return the number of checkpoints deleted
     */    
    function percentageDelegationHistoryCleanup(address _owner, uint256 _count)
        external onlyCleaner 
        returns (uint256)
    {
        return percentageDelegations[_owner].cleanupOldCheckpoints(_count, cleanupBlockNumber);
    }
    
    /**
     * Delete explicit delegation checkpoints that expired (i.e. are before `cleanupBlockNumber`).
     * Method can only be called from the `cleanerContract` (which may be a proxy to external cleaners).
     * @param _from the delegator address
     * @param _to the delegatee address
     * @param _count maximum number of checkpoints to delete
     * @return the number of checkpoints deleted
     */    
    function explicitDelegationHistoryCleanup(address _from, address _to, uint256 _count)
        external
        onlyCleaner 
        returns (uint256)
    {
        return explicitDelegations[_from].cleanupOldCheckpoints(_to, _count, cleanupBlockNumber);
    }
}


// File contracts/token/implementation/VPContract.sol

// 
pragma solidity 0.7.6;







contract VPContract is IIVPContract, Delegatable {
    using SafeMath for uint256;
    
    /**
     * The VPToken (or some other contract) that owns this VPContract.
     * All state changing methods may be called only from this address.
     * This is because original msg.sender is sent in `_from` parameter
     * and we must be sure that it cannot be faked by directly calling VPContract.
     * Owner token is also used in case of replacement to recover vote powers from balances.
     */
    IVPToken public immutable override ownerToken;
    
    /**
     * Return true if this IIVPContract is configured to be used as a replacement for other contract.
     * It means that vote powers are not necessarily correct at the initialization, therefore
     * every method that reads vote power must check whether it is initialized for that address and block.
     */
    bool public immutable override isReplacement;

    // the contract that is allowed to set cleanupBlockNumber
    // usually this will be an instance of CleanupBlockNumberManager
    // only set when detached from vptoken and directly registered to CleanupBlockNumberManager
    address private cleanupBlockNumberManager;
    
    // The block number when vote power for an address was first set.
    // Reading vote power before this block would return incorrect result and must revert.
    mapping (address => uint256) private votePowerInitializationBlock;

    // Vote power cache for past blocks when vote power was not initialized.
    // Reading vote power at that block would return incorrect result, so cache must be set by some other means.
    // No need for revocation info, since there can be no delegations at such block.
    mapping (bytes32 => uint256) private uninitializedVotePowerCache;
    
    string constant private ALREADY_EXPLICIT_MSG = "Already delegated explicitly";
    string constant private ALREADY_PERCENT_MSG = "Already delegated by percentage";
    
    string constant internal VOTE_POWER_NOT_INITIALIZED = "Vote power not initialized";

    /**
     * All external methods in VPContract can only be executed by the owner token.
     */
    modifier onlyOwnerToken {
        require(msg.sender == address(ownerToken), "only owner token");
        _;
    }

    /**
     * Setting cleaner contract is allowed from
     * owner token or owner token's governance (when VPContract is detached,
     * methods can no longer be called via the owner token, but the VPContract
     * still remembers the old owner token and can see its governance).
     */
    modifier onlyOwnerOrGovernance {
        require(msg.sender == address(ownerToken) || 
            msg.sender == GovernedBase(address(ownerToken)).governance(),
            "only owner or governance");
        _;
    }

    modifier onlyPercent(address sender) {
        // If a delegate cannot be added by percentage, revert.
        require(_canDelegateByPct(sender), ALREADY_EXPLICIT_MSG);
        _;
    }

    modifier onlyExplicit(address sender) {
        // If a delegate cannot be added by explicit amount, revert.
        require(_canDelegateByAmount(sender), ALREADY_PERCENT_MSG);
        _;
    }

    /**
     * Construct VPContract for given VPToken.
     */
    constructor(IVPToken _ownerToken, bool _isReplacement) {
        require(address(_ownerToken) != address(0), "VPContract must belong to a VPToken");
        ownerToken = _ownerToken;
        isReplacement = _isReplacement;
    }
    
    /**
     * Set the cleanup block number.
     * Historic data for the blocks before `cleanupBlockNumber` can be erased,
     * history before that block should never be used since it can be inconsistent.
     * In particular, cleanup block number must be before current vote power block.
     * The method can be called by the owner token, its governance or cleanupBlockNumberManager.
     * @param _blockNumber The new cleanup block number.
     */
    function setCleanupBlockNumber(uint256 _blockNumber) external override {
        require(msg.sender == address(ownerToken) || 
            msg.sender == cleanupBlockNumberManager ||
            msg.sender == GovernedBase(address(ownerToken)).governance(),
            "only owner, governance or cleanup block manager");
        _setCleanupBlockNumber(_blockNumber);
    }

    /**
     * Set the contract that is allowed to set cleanupBlockNumber.
     * Usually this will be an instance of CleanupBlockNumberManager.
     * Only to be set when detached from the owner token and directly attached to cleanup block number manager.
     */
    function setCleanupBlockNumberManager(address _cbnManager) external override onlyOwnerOrGovernance {
        cleanupBlockNumberManager = _cbnManager;
    }
    
    /**
     * Set the contract that is allowed to call history cleaning methods.
     * The method can be called by the owner token or its governance.
     */
    function setCleanerContract(address _cleanerContract) external override onlyOwnerOrGovernance {
        _setCleanerContract(_cleanerContract);
    }
    
    /**
     * Update vote powers when tokens are transfered.
     * Also update delegated vote powers for percentage delegation
     * and check for enough funds for explicit delegations.
     **/
    function updateAtTokenTransfer(
        address _from, 
        address _to, 
        uint256 _fromBalance,
        uint256 _toBalance,
        uint256 _amount
    )
        external override 
        onlyOwnerToken 
    {
        if (_from == address(0)) {
            // mint new vote power
            _initializeVotePower(_to, _toBalance);
            _mintVotePower(_to, _toBalance, _amount);
        } else if (_to == address(0)) {
            // burn vote power
            _initializeVotePower(_from, _fromBalance);
            _burnVotePower(_from, _fromBalance, _amount);
        } else {
            // transmit vote power _to receiver
            _initializeVotePower(_from, _fromBalance);
            _initializeVotePower(_to, _toBalance);
            _transmitVotePower(_from, _to, _fromBalance, _toBalance, _amount);
        }
    }

    /**
     * @notice Delegate `_bips` percentage of voting power to `_to` from `_from`
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _balance The delegator's current balance
     * @param _bips The percentage of voting power to be delegated expressed in basis points (1/100 of one percent).
     *   Not cummulative - every call resets the delegation value (and value of 0 revokes delegation).
     **/
    function delegate(
        address _from, 
        address _to, 
        uint256 _balance, 
        uint256 _bips
    )
        external override 
        onlyOwnerToken 
        onlyPercent(_from)
    {
        _initializeVotePower(_from, _balance);
        if (!_votePowerInitialized(_to)) {
            _initializeVotePower(_to, ownerToken.balanceOf(_to));
        }
        _delegateByPercentage(_from, _to, _balance, _bips);
    }
    
    /**
     * @notice Explicitly delegate `_amount` of voting power to `_to` from `_from`.
     * @param _from The address of the delegator
     * @param _to The address of the recipient
     * @param _balance The delegator's current balance
     * @param _amount An explicit vote power amount to be delegated.
     *   Not cummulative - every call resets the delegation value (and value of 0 undelegates `to`).
     **/    
    function delegateExplicit(
        address _from, 
        address _to, 
        uint256 _balance, 
        uint _amount
    )
        external override 
        onlyOwnerToken
        onlyExplicit(_from)
    {
        _initializeVotePower(_from, _balance);
        if (!_votePowerInitialized(_to)) {
            _initializeVotePower(_to, ownerToken.balanceOf(_to));
        }
        _delegateByAmount(_from, _to, _balance, _amount);
    }

    /**
     * @notice Revoke all delegation from `_from` to `_to` at given block. 
     *    Only affects the reads via `votePowerOfAtCached()` in the block `_blockNumber`.
     *    Block `_blockNumber` must be in the past. 
     *    This method should be used only to prevent rogue delegate voting in the current voting block.
     *    To stop delegating use delegate/delegateExplicit with value of 0 or undelegateAll/undelegateAllExplicit.
     * @param _from The address of the delegator
     * @param _to Address of the delegatee
     * @param _balance The delegator's current balance
     * @param _blockNumber The block number at which to revoke delegation.
     **/
    function revokeDelegationAt(
        address _from, 
        address _to, 
        uint256 _balance,
        uint _blockNumber
    )
        external override 
        onlyOwnerToken
    {
        // ASSERT: if there was a delegation, _from and _to must be initialized
        if (!isReplacement || 
            (_votePowerInitializedAt(_from, _blockNumber) && _votePowerInitializedAt(_to, _blockNumber))) {
            _revokeDelegationAt(_from, _to, _balance, _blockNumber);
        }
    }

    /**
     * @notice Undelegate all voting power for delegates of `_from`
     *    Can only be used with percentage delegation.
     *    Does not reset delegation mode back to NOTSET.
     * @param _from The address of the delegator
     **/
    function undelegateAll(
        address _from,
        uint256 _balance
    )
        external override 
        onlyOwnerToken 
        onlyPercent(_from)
    {
        if (_hasAnyDelegations(_from)) {
            // ASSERT: since there were delegations, _from and its targets must be initialized
            _undelegateAllByPercentage(_from, _balance);
        }
    }

    /**
     * @notice Undelegate all explicit vote power by amount delegates for `_from`.
     *    Can only be used with explicit delegation.
     *    Does not reset delegation mode back to NOTSET.
     * @param _from The address of the delegator
     * @param _delegateAddresses Explicit delegation does not store delegatees' addresses, 
     *   so the caller must supply them.
     * @return The amount still delegated (in case the list of delegates was incomplete).
     */
    function undelegateAllExplicit(
        address _from, 
        address[] memory _delegateAddresses
    )
        external override 
        onlyOwnerToken 
        onlyExplicit(_from) 
        returns (uint256)
    {
        if (_hasAnyDelegations(_from)) {
            // ASSERT: since there were delegations, _from and its targets must be initialized
            return _undelegateAllByAmount(_from, _delegateAddresses);
        }
        return 0;
    }
    
    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    *   Reads/updates cache and upholds revocations.
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAtCached(address _who, uint256 _blockNumber) external override returns(uint256) {
        if (!isReplacement || _votePowerInitializedAt(_who, _blockNumber)) {
            // use standard method
            return _votePowerOfAtCached(_who, _blockNumber);
        } else {
            // use uninitialized vote power cache
            bytes32 key = keccak256(abi.encode(_who, _blockNumber));
            uint256 cached = uninitializedVotePowerCache[key];
            if (cached != 0) {
                return cached - 1;  // safe, cached != 0
            }
            uint256 balance = ownerToken.balanceOfAt(_who, _blockNumber);
            uninitializedVotePowerCache[key] = balance.add(1);
            return balance;
        }
    }
    
    /**
     * Get the current cleanup block number.
     */
    function cleanupBlockNumber() external view override returns (uint256) {
        return _cleanupBlockNumber();
    }
    
    /**
     * @notice Get the current vote power of `_who`.
     * @param _who The address to get voting power.
     * @return Current vote power of `_who`.
     */
    function votePowerOf(address _who) external view override returns(uint256) {
        if (_votePowerInitialized(_who)) {
            return _votePowerOf(_who);
        } else {
            return ownerToken.balanceOf(_who);
        }
    }
    
    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAt(address _who, uint256 _blockNumber) public view override returns(uint256) {
        if (!isReplacement || _votePowerInitializedAt(_who, _blockNumber)) {
            return _votePowerOfAt(_who, _blockNumber);
        } else {
            return ownerToken.balanceOfAt(_who, _blockNumber);
        }
    }
    
    /**
     * Return vote powers for several addresses in a batch.
     * @param _owners The list of addresses to fetch vote power of.
     * @param _blockNumber The block number at which to fetch.
     * @return _votePowers A list of vote powers corresponding to _owners.
     */    
    function batchVotePowerOfAt(
        address[] memory _owners, 
        uint256 _blockNumber
    )
        external view override 
        returns(uint256[] memory _votePowers)
    {
        _votePowers = _batchVotePowerOfAt(_owners, _blockNumber);
        // zero results might not have been initialized
        if (isReplacement) {
            for (uint256 i = 0; i < _votePowers.length; i++) {
                if (_votePowers[i] == 0 && !_votePowerInitializedAt(_owners[i], _blockNumber)) {
                    _votePowers[i] = ownerToken.balanceOfAt(_owners[i], _blockNumber);
                }
            }
        }
    }
    
    /**
    * @notice Get current delegated vote power `_from` delegator delegated `_to` delegatee.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _balance The delegator's current balance
    * @return The delegated vote power.
    */
    function votePowerFromTo(
        address _from, 
        address _to, 
        uint256 _balance
    )
        external view override 
        returns (uint256)
    {
        // ASSERT: if the result is nonzero, _from and _to are initialized
        return _votePowerFromTo(_from, _to, _balance);
    }
    
    /**
    * @notice Get delegated the vote power `_from` delegator delegated `_to` delegatee at `_blockNumber`.
    * @param _from Address of delegator
    * @param _to Address of delegatee
    * @param _balance The delegator's current balance
    * @param _blockNumber The block number at which to fetch.
    * @return The delegated vote power.
    */
    function votePowerFromToAt(
        address _from, 
        address _to, 
        uint256 _balance,
        uint _blockNumber
    )
        external view override
        returns (uint256)
    {
        // ASSERT: if the result is nonzero, _from and _to were initialized at _blockNumber
        return _votePowerFromToAt(_from, _to, _balance, _blockNumber);
    }
    
    /**
     * @notice Get the delegation mode for '_who'. This mode determines whether vote power is
     *  allocated by percentage or by explicit value.
     * @param _who The address to get delegation mode.
     * @return Delegation mode (NOTSET=0, PERCENTAGE=1, AMOUNT=2))
     */
    function delegationModeOf(address _who) external view override returns (uint256) {
        return uint256(_delegationModeOf(_who));
    }

    /**
     * @notice Compute the current undelegated vote power of `_owner`
     * @param _owner The address to get undelegated voting power.
     * @param _balance Owner's current balance
     * @return The unallocated vote power of `_owner`
     */
    function undelegatedVotePowerOf(
        address _owner,
        uint256 _balance
    )
        external view override
        returns (uint256)
    {
        if (_votePowerInitialized(_owner)) {
            return _undelegatedVotePowerOf(_owner, _balance);
        } else {
            // ASSERT: there are no delegations
            return _balance;
        }
    }
    
    /**
     * @notice Get the undelegated vote power of `_owner` at given block.
     * @param _owner The address to get undelegated voting power.
     * @param _blockNumber The block number at which to fetch.
     * @return The undelegated vote power of `_owner` (= owner's own balance minus all delegations from owner)
     */
    function undelegatedVotePowerOfAt(
        address _owner, 
        uint256 _balance,
        uint256 _blockNumber
    )
        external view override
        returns (uint256)
    {
        if (_votePowerInitialized(_owner)) {
            return _undelegatedVotePowerOfAt(_owner, _balance, _blockNumber);
        } else {
            // ASSERT: there were no delegations at _blockNumber
            return _balance;
        }
    }

    /**
    * @notice Get the vote power delegation `_delegateAddresses` 
    *  and `pcts` of an `_owner`. Returned in two separate positional arrays.
    *  Also returns the count of delegates and delegation mode.
    * @param _owner The address to get delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOf(address _owner)
        external view override 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        )
    {
        // ASSERT: either _owner is initialized or there are no delegations
        return delegatesOfAt(_owner, block.number);
    }
    
    /**
    * @notice Get the vote power delegation `delegationAddresses` 
    *  and `_bips` of an `_owner`. Returned in two separate positional arrays.
    *  Also returns the count of delegates and delegation mode.
    * @param _owner The address to get delegations.
    * @param _blockNumber The block for which we want to know the delegations.
    * @return _delegateAddresses Positional array of delegation addresses.
    * @return _bips Positional array of delegation percents specified in basis points (1/100 or 1 percent)
    * @return _count The number of delegates.
    * @return _delegationMode The mode of the delegation (NOTSET=0, PERCENTAGE=1, AMOUNT=2).
    */
    function delegatesOfAt(
        address _owner,
        uint256 _blockNumber
    )
        public view override 
        returns (
            address[] memory _delegateAddresses, 
            uint256[] memory _bips,
            uint256 _count,
            uint256 _delegationMode
        )
    {
        // ASSERT: either _owner was initialized or there were no delegations
        DelegationMode mode = _delegationModeOf(_owner);
        if (mode == DelegationMode.PERCENTAGE) {
            // Get the vote power delegation for the _owner
            (_delegateAddresses, _bips) = _percentageDelegatesOfAt(_owner, _blockNumber);
        } else if (mode == DelegationMode.NOTSET) {
            _delegateAddresses = new address[](0);
            _bips = new uint256[](0);
        } else {
            revert ("delegatesOf does not work in AMOUNT delegation mode");
        }
        _count = _delegateAddresses.length;
        _delegationMode = uint256(mode);
    }

    /**
     * Initialize vote power to current balance if not initialized already.
     * @param _owner The address to initialize voting power.
     * @param _balance The owner's current balance.
     */
    function _initializeVotePower(address _owner, uint256 _balance) internal {
        if (!isReplacement) return;
        if (votePowerInitializationBlock[_owner] == 0) {
            // consistency check - no delegations should be made from or to owner before vote power is initialized
            // (that would be dangerous, because vote power would have been delegated incorrectly)
            assert(_votePowerOf(_owner) == 0 && !_hasAnyDelegations(_owner));
            _mintVotePower(_owner, 0, _balance);
            votePowerInitializationBlock[_owner] = block.number.add(1);
        }
    }
    
    /**
     * Has the vote power of `_owner` been initialized?
     * @param _owner The address to check.
     * @return true if vote power of _owner is initialized
     */
    function _votePowerInitialized(address _owner) internal view returns (bool) {
        if (!isReplacement) return true;
        return votePowerInitializationBlock[_owner] != 0;
    }

    /**
     * Was vote power of `_owner` initialized at some block?
     * @param _owner The address to check.
     * @param _blockNumber The block for which we want to check.
     * @return true if vote power of _owner was initialized at _blockNumber
     */
    function _votePowerInitializedAt(address _owner, uint256 _blockNumber) internal view returns (bool) {
        if (!isReplacement) return true;
        uint256 initblock = votePowerInitializationBlock[_owner];
        return initblock != 0 && initblock - 1 <= _blockNumber;
    }
}


// File contracts/userInterfaces/IWNat.sol

// 
pragma solidity 0.7.6;

interface IWNat {
    /**
     * @notice Deposit native token and mint WNAT ERC20.
     */
    function deposit() external payable;

    /**
     * @notice Withdraw native token and burn WNAT ERC20.
     * @param _amount The amount to withdraw.
     */
    function withdraw(uint256 _amount) external;
    
    /**
     * @notice Deposit native token from msg.sender and mint WNAT ERC20.
     * @param _recipient An address to receive minted WNAT.
     */
    function depositTo(address _recipient) external payable;
    
    /**
     * @notice Withdraw WNAT from an owner and send NAT to msg.sender given an allowance.
     * @param _owner An address spending the native tokens.
     * @param _amount The amount to spend.
     *
     * Requirements:
     *
     * - `_owner` must have a balance of at least `_amount`.
     * - the caller must have allowance for `_owners`'s tokens of at least
     * `_amount`.
     */
    function withdrawFrom(address _owner, uint256 _amount) external;
}


// File contracts/token/implementation/WNat.sol

// 
pragma solidity 0.7.6;




/**
 * @title Wrapped Native token
 * @notice Accept native token deposits and mint ERC20 WNAT (wrapped native) tokens 1-1.
 * @dev Attribution: https://rinkeby.etherscan.io/address/0xc778417e063141139fce010982780140aa0cd5ab#code 
 */
contract WNat is VPToken, IWNat {
    using SafeMath for uint256;
    event  Deposit(address indexed dst, uint amount);
    event  Withdrawal(address indexed src, uint amount);

    /**
     * Construct an ERC20 token.
     */
    constructor(address _governance, string memory _name, string memory _symbol) 
        VPToken(_governance, _name, _symbol) 
    {
    }

    receive() external payable {
        deposit();
    }

    /**
     * @notice Withdraw WNAT from an owner and send native tokens to msg.sender given an allowance.
     * @param owner An address spending the Native tokens.
     * @param amount The amount to spend.
     *
     * Requirements:
     *
     * - `owner` must have a balance of at least `amount`.
     * - the caller must have allowance for `owners`'s tokens of at least
     * `amount`.
     */
    function withdrawFrom(address owner, uint256 amount) external override {
        // Reduce senders allowance
        _approve(owner, msg.sender, allowance(owner, msg.sender).sub(amount, "allowance below zero"));
        // Burn the owners balance
        _burn(owner, amount);
        // Emit withdraw event
        emit Withdrawal(owner, amount);
        // Move value to sender (last statement, to prevent reentrancy)
        msg.sender.transfer(amount);
    }

    /**
     * @notice Deposit Native from msg.sender and mints WNAT ERC20 to recipient address.
     * @param recipient An address to receive minted WNAT.
     */
    function depositTo(address recipient) external payable override {
        require(recipient != address(0), "Cannot deposit to zero address");
        // Mint WNAT
        _mint(recipient, msg.value);
        // Emit deposit event
        emit Deposit(recipient, msg.value);
    }

    /**
     * @notice Deposit Native and mint wNat ERC20.
     */
    function deposit() public payable override {
        // Mint WNAT
        _mint(msg.sender, msg.value);
        // Emit deposit event
        emit Deposit(msg.sender, msg.value);
    }

    /**
     * @notice Withdraw Native and burn WNAT ERC20.
     * @param amount The amount to withdraw.
     */
    function withdraw(uint256 amount) external override {
        // Burn WNAT tokens
        _burn(msg.sender, amount);
        // Emit withdrawal event
        emit Withdrawal(msg.sender, amount);
        // Send Native to sender (last statement, to prevent reentrancy)
        msg.sender.transfer(amount);
    }
}


// File @openzeppelin/contracts/utils/[email protected]

// 

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}


// File contracts/tokenPools/implementation/FtsoRewardManager.sol

// 
pragma solidity 0.7.6;









/**
 * FTSORewardManager is in charge of:
 * - distributing rewards according to instructions from FTSO Manager
 * - allowing claims for rewards
 */    

//solhint-disable-next-line max-states-count
contract FtsoRewardManager is IIFtsoRewardManager, IIInflationReceiver, IITokenPool, Governed, ReentrancyGuard {
    using SafePct for uint256;
    using SafeMath for uint256;

    struct FeePercentage {          // used for storing data provider fee percentage settings
        uint16 value;               // fee percentage value (value between 0 and 1e4)
        uint240 validFromEpoch;     // id of the reward epoch from which the value is valid
    }

    struct RewardClaim {            // used for storing reward claim info
        bool claimed;               // indicates if reward has been claimed
        uint256 amount;             // amount claimed
    }

    struct RewardState {            // used for local storage of reward state
        address[] dataProviders;    // positional array of addresses representing data providers
        uint256[] weights;          // positional array of numbers representing reward weights
        uint256[] amounts;          // positional array of numbers representing reward amounts
        bool[] claimed;             // positional array of booleans indicating if reward has already been claimed
    }

    string internal constant ERR_FTSO_MANAGER_ONLY = "ftso manager only";
    string internal constant ERR_INFLATION_ONLY = "inflation only";
    string internal constant ERR_INFLATION_ZERO = "inflation zero";
    string internal constant ERR_FTSO_MANAGER_ZERO = "no ftso manager";
    string internal constant ERR_WNAT_ZERO = "no wNat";
    string internal constant ERR_OUT_OF_BALANCE = "out of balance";
    string internal constant ERR_CLAIM_FAILED = "claim failed";
    string internal constant ERR_REWARD_MANAGER_DEACTIVATED = "reward manager deactivated";
    string internal constant ERR_FEE_PERCENTAGE_INVALID = "invalid fee percentage value";
    string internal constant ERR_FEE_PERCENTAGE_UPDATE_FAILED = "fee percentage can not be updated";
    string internal constant ERR_AFTER_DAILY_CYCLE = "after daily cycle";
    string internal constant ERR_NO_CLAIMABLE_EPOCH = "no epoch with claimable rewards";
    
    uint256 constant internal MAX_BIPS = 1e4;
    uint256 constant internal ALMOST_FULL_DAY_SEC = 86399;

    bool public active;

    uint256 public immutable feePercentageUpdateOffset; // fee percentage update timelock measured in reward epochs
    uint256 public immutable defaultFeePercentage; // default value for fee percentage
        
    // id of the first epoch to expire. Closed = expired and unclaimed funds sent back
    uint256 private nextRewardEpochToExpire; 
    
    /**
     * @dev Provides a mapping of reward epoch ids to an address mapping of unclaimed rewards.
     */
    mapping(uint256 => mapping(address => uint256)) private epochProviderUnclaimedRewardWeight;
    mapping(uint256 => mapping(address => uint256)) private epochProviderUnclaimedRewardAmount;    
    mapping(uint256 => mapping(address => mapping(address => RewardClaim))) private epochProviderClaimerReward;
    mapping(uint256 => uint256) private totalRewardEpochRewards;
    mapping(uint256 => uint256) private claimedRewardEpochRewards;

    mapping(address => FeePercentage[]) private dataProviderFeePercentages;

    // Totals
    uint256 public totalAwardedWei;
    uint256 public totalClaimedWei;
    uint256 public totalExpiredWei; // rewards that were not claimed
    uint256 public totalInflationAuthorizedWei;
    uint256 public totalInflationReceivedWei;
    uint256 public totalSelfDestructReceivedWei;
    uint256 public lastInflationAuthorizationReceivedTs;
    uint256 public dailyAuthorizedInflation;

    uint256 private lastBalance;

    /// addresses
    IIFtsoManager public ftsoManager;
    address private inflation;
    WNat public wNat; 

    modifier mustBalance {
        _;
        require(address(this).balance == _getExpectedBalance(), ERR_OUT_OF_BALANCE);
    }

    modifier onlyFtsoManager () {
        require (msg.sender == address(ftsoManager), ERR_FTSO_MANAGER_ONLY);
        _;
    }

    modifier onlyIfActive() {
        require(active, ERR_REWARD_MANAGER_DEACTIVATED);
        _;
    }

    modifier onlyInflation {
        require(msg.sender == inflation, ERR_INFLATION_ONLY);
        _;
    }

    constructor(
        address _governance,
        uint256 _feePercentageUpdateOffset,
        uint256 _defaultFeePercentage
    )
        Governed(_governance)
    {
        feePercentageUpdateOffset = _feePercentageUpdateOffset;
        defaultFeePercentage = _defaultFeePercentage;
    }

    /**
     * @notice Allows a percentage delegator to claim rewards.
     * @notice This function is intended to be used to claim rewards in case of delegation by percentage.
     * @param _recipient            address to transfer funds to
     * @param _rewardEpochs         array of reward epoch numbers to claim for
     * @return _rewardAmount        amount of total claimed rewards
     * @dev Reverts if `msg.sender` is delegating by amount
     */
    function claimReward(
        address payable _recipient,
        uint256[] memory _rewardEpochs
    ) 
        external override
        onlyIfActive
        mustBalance
        nonReentrant 
        returns (uint256 _rewardAmount)
    {
        _handleSelfDestructProceeds();

        uint256 currentRewardEpoch = ftsoManager.getCurrentRewardEpoch();
                
        for (uint256 i = 0; i < _rewardEpochs.length; i++) {
            if (!_isRewardClaimable(_rewardEpochs[i], currentRewardEpoch)) {
                continue;
            }
            RewardState memory rewardState = _getStateOfRewards(msg.sender, _rewardEpochs[i], true);
            uint256 amount = _claimReward(_recipient, _rewardEpochs[i], rewardState);
            claimedRewardEpochRewards[_rewardEpochs[i]] += amount;
            _rewardAmount += amount;
        }

        _transferReward(_recipient, _rewardAmount);

        //slither-disable-next-line reentrancy-eth          // guarded by nonReentrant
        lastBalance = address(this).balance;
    }

    /**
     * @notice Allows the sender to claim the rewards from specified data providers.
     * @notice This function is intended to be used to claim rewards in case of delegation by amount.
     * @param _recipient            address to transfer funds to
     * @param _rewardEpochs         array of reward epoch numbers to claim for
     * @param _dataProviders        array of addresses representing data providers to claim the reward from
     * @return _rewardAmount        amount of total claimed rewards
     * @dev Function can be used by a percentage delegator but is more gas consuming than `claimReward`.
     */
    function claimRewardFromDataProviders(
        address payable _recipient,
        uint256[] memory _rewardEpochs,
        address[] memory _dataProviders
    )
        external override
        onlyIfActive
        mustBalance
        nonReentrant 
        returns (uint256 _rewardAmount)
    {
        _handleSelfDestructProceeds();

        uint256 currentRewardEpoch = ftsoManager.getCurrentRewardEpoch();

        for (uint256 i = 0; i < _rewardEpochs.length; i++) {
            if (!_isRewardClaimable(_rewardEpochs[i], currentRewardEpoch)) {
                continue;
            }
            RewardState memory rewardState;
            rewardState = _getStateOfRewardsFromDataProviders(msg.sender, _rewardEpochs[i], _dataProviders, true);

            uint256 amount = _claimReward(_recipient, _rewardEpochs[i], rewardState);
            claimedRewardEpochRewards[_rewardEpochs[i]] += amount;
            _rewardAmount += amount;
        }

        _transferReward(_recipient, _rewardAmount);
        
        //slither-disable-next-line reentrancy-eth      // guarded by nonReentrant
        lastBalance = address(this).balance;
    }

    /**
     * @notice Activates reward manager (allows claiming rewards)
     */
    function activate() external override onlyGovernance {
        require(inflation != address(0) && address(ftsoManager) != address(0) && address(wNat) != address(0),
            "contract addresses not set");
        active = true;
    }

    /**
     * @notice Deactivates reward manager (prevents claiming rewards)
     */
    function deactivate() external override onlyGovernance {
        active = false;
    }
   
    /**
     * @notice Sets inflation, ftsoManager and wNat addresses.
     * Only governance can call this method.
     */
    function setContractAddresses(
        address _inflation,
        IIFtsoManager _ftsoManager,
        WNat _wNat
    ) 
        external
        onlyGovernance
    {
        require(_inflation != address(0), ERR_INFLATION_ZERO);
        require(address(_ftsoManager) != address(0), ERR_FTSO_MANAGER_ZERO);
        require(address(_wNat) != address(0), ERR_WNAT_ZERO);
        inflation = _inflation;
        ftsoManager = _ftsoManager;
        wNat = _wNat;
    }

    function setDailyAuthorizedInflation(uint256 _toAuthorizeWei) external override onlyInflation {
        dailyAuthorizedInflation = _toAuthorizeWei;
        totalInflationAuthorizedWei = totalInflationAuthorizedWei.add(_toAuthorizeWei);
        lastInflationAuthorizationReceivedTs = block.timestamp;

        emit DailyAuthorizedInflationSet(_toAuthorizeWei);
    }

    function receiveInflation() external payable override mustBalance onlyInflation {
        (uint256 currentBalance, ) = _handleSelfDestructProceeds();
        totalInflationReceivedWei = totalInflationReceivedWei.add(msg.value);
        lastBalance = currentBalance;

        emit InflationReceived(msg.value);
    }

    /**
     * @notice Distributes rewards to data providers accounts, according to input parameters.
     * @dev must be called with totalWeight > 0 and addresses.length > 0
     */
    function distributeRewards(
        address[] memory _addresses,
        uint256[] memory _weights,
        uint256 _totalWeight,
        uint256 _epochId,
        address _ftso,
        uint256 _priceEpochDurationSeconds,
        uint256 _currentRewardEpoch,
        uint256 _priceEpochEndTime, // end time included in epoch
        uint256 _votePowerBlock
    )
        external override
        onlyFtsoManager
    {
        // FTSO manager should never call with bad values.
        assert (_totalWeight != 0 && _addresses.length != 0);

        uint256 totalPriceEpochReward = 
            _getDistributableFtsoInflationBalance()
            .div(_getRemainingPriceEpochCount(_priceEpochEndTime, _priceEpochDurationSeconds));

        uint256[] memory rewards = new uint256[](_addresses.length);
        rewards[0] = totalPriceEpochReward;
        _weights[0] = _totalWeight;

        uint256 i = _addresses.length - 1;
        while (true) {
            rewards[i] = rewards[0].mulDiv(_weights[i], _weights[0]);
            epochProviderUnclaimedRewardAmount[_currentRewardEpoch][_addresses[i]] += rewards[i];
            epochProviderUnclaimedRewardWeight[_currentRewardEpoch][_addresses[i]] =
                wNat.votePowerOfAt(_addresses[i], _votePowerBlock).mul(MAX_BIPS);            
            if (i == 0) {
                break;
            }
            rewards[0] -= rewards[i];
            _weights[0] -= _weights[i];
            i--;
        }

        totalRewardEpochRewards[_currentRewardEpoch] += totalPriceEpochReward;

        // Update total awarded with amount distributed
        totalAwardedWei = totalAwardedWei.add(totalPriceEpochReward);

        emit RewardsDistributed(_ftso, _epochId, _addresses, rewards);
    }

    /**
     * @notice Allows data provider to set (or update last) fee percentage.
     * @param _feePercentageBIPS    number representing fee percentage in BIPS
     * @return Returns the reward epoch number when the setting becomes effective.
     */
    function setDataProviderFeePercentage(uint256 _feePercentageBIPS) external override returns (uint256) {
        require(_feePercentageBIPS <= MAX_BIPS, ERR_FEE_PERCENTAGE_INVALID);

        uint256 rewardEpoch = ftsoManager.getCurrentRewardEpoch() + feePercentageUpdateOffset;
        FeePercentage[] storage fps = dataProviderFeePercentages[msg.sender];

        // determine whether to update the last setting or add a new one
        uint256 position = fps.length;
        if (position > 0) {
            // do not allow updating the settings in the past
            // (this can only happen if the sharing percentage epoch offset is updated)
            require(rewardEpoch >= fps[position - 1].validFromEpoch, ERR_FEE_PERCENTAGE_UPDATE_FAILED);
            
            if (rewardEpoch == fps[position - 1].validFromEpoch) {
                // update
                position = position - 1;
            }
        }
        if (position == fps.length) {
            // add
            fps.push();
        }

        // apply setting
        fps[position].value = uint16(_feePercentageBIPS);
        assert(rewardEpoch < 2**240);
        fps[position].validFromEpoch = uint240(rewardEpoch);

        emit FeePercentageChanged(msg.sender, _feePercentageBIPS, rewardEpoch);
        return rewardEpoch;
    }
    
    /**
     * @notice Collects funds from expired reward epoch and totals.
     * @dev Triggered by ftsoManager on finalization of a reward epoch.
     * Operation is irreversible: when some reward epoch is closed according to current
     * settings of parameters, it cannot be reopened even if new parameters would 
     * allow it since nextRewardEpochToExpire in ftsoManager never decreases.
     */
    function closeExpiredRewardEpoch(uint256 _rewardEpoch) external override onlyFtsoManager {
        require(nextRewardEpochToExpire == _rewardEpoch, "wrong reward epoch id");
        uint256 expiredWei = totalRewardEpochRewards[_rewardEpoch] - claimedRewardEpochRewards[_rewardEpoch];
        totalExpiredWei = totalExpiredWei.add(expiredWei);
        emit RewardClaimsExpired(_rewardEpoch);
        nextRewardEpochToExpire = _rewardEpoch + 1;
    }

    /**
     * @notice Returns information on epoch reward
     * @param _rewardEpoch          reward epoch number
     * @return _totalReward         number representing the total epoch reward
     * @return _claimedReward       number representing the amount of total epoch reward that has been claimed
     */
    function getEpochReward(
        uint256 _rewardEpoch
    )
        external view override 
        returns (uint256 _totalReward, uint256 _claimedReward) 
    {
        _totalReward = totalRewardEpochRewards[_rewardEpoch];
        _claimedReward = claimedRewardEpochRewards[_rewardEpoch];
    }

    /**
     * @notice Returns the Inflation contract address.
     * @dev Inflation receivers must have a reference to Inflation in order to receive native tokens for claiming.
     * @return The inflation address
     */
    function getInflationAddress() external view override returns(address) {
        return inflation;
    }

    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch`
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @return _dataProviders       positional array of addresses representing data providers
     * @return _rewardAmounts       positional array of reward amounts
     * @return _claimed             positional array of boolean values indicating if reward is claimed
     * @return _claimable           boolean value indicating if rewards are claimable
     * @dev Reverts when queried with `_beneficary` delegating by amount
     */
    function getStateOfRewards(
        address _beneficiary,
        uint256 _rewardEpoch
    )
        external view override 
        returns (
            address[] memory _dataProviders,
            uint256[] memory _rewardAmounts,
            bool[] memory _claimed,
            bool _claimable
        ) 
    {
        uint256 currentRewardEpoch = ftsoManager.getCurrentRewardEpoch();
        _claimable = _isRewardClaimable(_rewardEpoch, currentRewardEpoch);
        if (_claimable || _rewardEpoch == currentRewardEpoch) {
            RewardState memory rewardState = _getStateOfRewards(_beneficiary, _rewardEpoch, false);
            _dataProviders = rewardState.dataProviders;
            _rewardAmounts = rewardState.amounts;
            _claimed = rewardState.claimed;
        }
    }

    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch` from `_dataProviders`
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @param _dataProviders        positional array of addresses representing data providers
     * @return _rewardAmounts       positional array of reward amounts
     * @return _claimed             positional array of boolean values indicating if reward is claimed
     * @return _claimable           boolean value indicating if rewards are claimable
     */
    function getStateOfRewardsFromDataProviders(
        address _beneficiary,
        uint256 _rewardEpoch,
        address[] memory _dataProviders
    )
        external view override 
        returns (
            uint256[] memory _rewardAmounts,
            bool[] memory _claimed,
            bool _claimable
        )
    {
        uint256 currentRewardEpoch = ftsoManager.getCurrentRewardEpoch();
        _claimable = _isRewardClaimable(_rewardEpoch, currentRewardEpoch);
        if (_claimable || _rewardEpoch == currentRewardEpoch) {
            RewardState memory rewardState = _getStateOfRewardsFromDataProviders(
                _beneficiary,
                _rewardEpoch,
                _dataProviders,
                false
            );
            _rewardAmounts = rewardState.amounts;
            _claimed = rewardState.claimed;
        }
    }

    /**
     * @notice Returns the start and the end of the reward epoch range for which the reward is claimable
     * @return _startEpochId        the oldest epoch id that allows reward claiming
     * @return _endEpochId          the newest epoch id that allows reward claiming
     */
    function getEpochsWithClaimableRewards() external view override 
        returns (uint256 _startEpochId, uint256 _endEpochId)
    {
        (_startEpochId, _endEpochId) = _getEpochsWithClaimableRewards();
    }

    /**
     * @notice Returns the array of claimable epoch ids for which the reward has not yet been claimed
     * @param _beneficiary          address of reward beneficiary
     * @return _epochIds            array of epoch ids
     * @dev Reverts when queried with `_beneficary` delegating by amount
     */
    function getEpochsWithUnclaimedRewards(address _beneficiary) external view override 
        returns (uint256[] memory _epochIds) 
    {
        (uint256 startId, uint256 endId) = _getEpochsWithClaimableRewards();
        uint256 count = endId - startId + 1;        
        bool[] memory unclaimed = new bool[](count);
        uint256 unclaimedCount = 0;
        for (uint256 i = 0; i < count; i++) {
            RewardState memory rewardState = _getStateOfRewards(_beneficiary, startId + i, true);
            for (uint256 j = 0; j < rewardState.claimed.length; j++) {
                if (!rewardState.claimed[j] && rewardState.amounts[j] > 0) {
                    unclaimed[i] = true;
                    unclaimedCount++;
                    break;
                }
            }
        }
        _epochIds = new uint256[](unclaimedCount);
        uint256 index = 0;
        for (uint256 i = 0; i < count; i++) {
            if (unclaimed[i]) {
                _epochIds[index] = startId + i;
                index++;
            }
        }
    }

    /**
     * @notice Returns the information on unclaimed reward of `_dataProvider` for `_rewardEpoch`
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing the data provider
     * @return _amount              number representing the unclaimed amount
     * @return _weight              number representing the share that has not yet been claimed
     */
    function getUnclaimedReward(
        uint256 _rewardEpoch,
        address _dataProvider
    )
        external view override 
        returns (uint256 _amount, uint256 _weight)
    {
        _amount = epochProviderUnclaimedRewardAmount[_rewardEpoch][_dataProvider];
        _weight = epochProviderUnclaimedRewardWeight[_rewardEpoch][_dataProvider];
    }

    /**
     * @notice Returns the information on claimed reward of `_dataProvider` for `_rewardEpoch` by `_claimer`
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing the data provider
     * @param _claimer              address representing the claimer
     * @return _claimed             boolean indicating if reward has been claimed
     * @return _amount              number representing the claimed amount
     */
    function getClaimedReward(
        uint256 _rewardEpoch,
        address _dataProvider,
        address _claimer
    )
        external view override 
        returns(bool _claimed, uint256 _amount) 
    {
        RewardClaim storage rewardClaim = epochProviderClaimerReward[_rewardEpoch][_dataProvider][_claimer];
        _claimed = rewardClaim.claimed;
        _amount = rewardClaim.amount;
    }

    /**
     * @notice Returns the current fee percentage of `_dataProvider`
     * @param _dataProvider         address representing data provider
     */
    function getDataProviderCurrentFeePercentage(address _dataProvider) external view override returns (uint256) {
        return _getDataProviderFeePercentage(_dataProvider, ftsoManager.getCurrentRewardEpoch());
    }

    /**
     * @notice Returns the scheduled fee percentage changes of `_dataProvider`
     * @param _dataProvider         address representing data provider
     * @return _feePercentageBIPS   positional array of fee percentages in BIPS
     * @return _validFromEpoch      positional array of block numbers the fee setings are effective from
     * @return _fixed               positional array of boolean values indicating if settings are subjected to change
     */
    function getDataProviderScheduledFeePercentageChanges(
        address _dataProvider
    )
        external view override
        returns (
            uint256[] memory _feePercentageBIPS,
            uint256[] memory _validFromEpoch,
            bool[] memory _fixed
        ) 
    {
        FeePercentage[] storage fps = dataProviderFeePercentages[_dataProvider];
        if (fps.length > 0) {
            uint256 currentEpoch = ftsoManager.getCurrentRewardEpoch();
            uint256 position = fps.length;
            while (position > 0 && fps[position - 1].validFromEpoch > currentEpoch) {
                position--;
            }
            uint256 count = fps.length - position;
            if (count > 0) {
                _feePercentageBIPS = new uint256[](count);
                _validFromEpoch = new uint256[](count);
                _fixed = new bool[](count);
                for (uint256 i = 0; i < count; i++) {
                    _feePercentageBIPS[i] = fps[i + position].value;
                    _validFromEpoch[i] = fps[i + position].validFromEpoch;
                    _fixed[i] = (_validFromEpoch[i] - currentEpoch) != feePercentageUpdateOffset;
                }
            }
        }        
    }

    /**
     * @notice Return reward epoch that will expire, when new reward epoch is initialized
     * @return Reward epoch id that will expire next
     */
    function getRewardEpochToExpireNext() external view override returns (uint256) {
        return nextRewardEpochToExpire;
    }

    /**
     * @notice Return token pool supply data
     * @return _foundationAllocatedFundsWei     Foundation allocated funds (wei)
     * @return _totalInflationAuthorizedWei     Total inflation authorized amount (wei)
     * @return _totalClaimedWei                 Total claimed amount (wei)
     */
    function getTokenPoolSupplyData() external view override 
        returns (
            uint256 _foundationAllocatedFundsWei,
            uint256 _totalInflationAuthorizedWei,
            uint256 _totalClaimedWei
        )
    {
        return (0, totalInflationAuthorizedWei, totalClaimedWei);
    }

    function _handleSelfDestructProceeds() internal returns (uint256 _currentBalance, uint256 _expectedBalance) {
        _expectedBalance = lastBalance.add(msg.value);
        _currentBalance = address(this).balance;
        if (_currentBalance > _expectedBalance) {
            // Then assume extra were self-destruct proceeds
            totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(_currentBalance).sub(_expectedBalance);
        } else if (_currentBalance < _expectedBalance) {
            // This is a coding error
            assert(false);
        }
    }

    /**
     * @notice Claims `_rewardAmounts` for `_dataProviders`.
     * @dev Internal function that takes care of reward bookkeeping
     * @param _recipient            address representing the recipient of the reward
     * @param _rewardEpoch          reward epoch number
     * @param _rewardState          object holding reward state
     * @return Returns the total reward amount.
     */
    function _claimReward(
        address payable _recipient,
        uint256 _rewardEpoch,
        RewardState memory _rewardState
    ) 
        internal
        returns (uint256)
    {
        uint256 totalRewardAmount = 0;
        for (uint256 i = 0; i < _rewardState.dataProviders.length; i++) {
            if (_rewardState.claimed[i]) {
                continue;
            }

            address dataProvider = _rewardState.dataProviders[i];            

            uint256 rewardWeight = _rewardState.weights[i];            
            if (rewardWeight > 0) {
                epochProviderUnclaimedRewardWeight[_rewardEpoch][dataProvider] -= rewardWeight; // can not underflow
            }

            uint256 rewardAmount = _rewardState.amounts[i];
            if (rewardAmount > 0) {
                epochProviderUnclaimedRewardAmount[_rewardEpoch][dataProvider] -= rewardAmount; // can not underflow
                totalClaimedWei += rewardAmount;
                totalRewardAmount += rewardAmount;
            }

            RewardClaim storage rewardClaim = epochProviderClaimerReward[_rewardEpoch][dataProvider][msg.sender];
            rewardClaim.claimed = true;
            rewardClaim.amount = rewardAmount;

            emit RewardClaimed({
                dataProvider: dataProvider,
                whoClaimed: msg.sender,
                sentTo: _recipient,
                rewardEpoch: _rewardEpoch,
                amount: rewardAmount
            });
        }

        return totalRewardAmount;
    }

    /**
     * @notice Transfers `_rewardAmount` to `_recipient`.
     * @param _recipient            address representing the reward recipient
     * @param _rewardAmount         number representing the amount to transfer
     * @dev Uses low level call to transfer funds.
     */
    function _transferReward(address payable _recipient, uint256 _rewardAmount) internal {
        if (_rewardAmount > 0) {
            // transfer total amount (state is updated and events are emitted in _claimReward)
            /* solhint-disable avoid-low-level-calls */
            //slither-disable-next-line arbitrary-send          // amount always calculated by _claimReward
            (bool success, ) = _recipient.call{value: _rewardAmount}("");
            /* solhint-enable avoid-low-level-calls */
            require(success, ERR_CLAIM_FAILED);
        }
    }

    function _getDistributableFtsoInflationBalance() internal view returns (uint256) {
        return totalInflationAuthorizedWei
            .sub(totalAwardedWei.sub(totalExpiredWei));
    }

    function _getRemainingPriceEpochCount(
        uint256 _fromThisTs, 
        uint256 _priceEpochDurationSeconds
    )
        internal view
        returns (uint256)
    {
        // Get the end of the daily period
        uint256 dailyPeriodEndTs = lastInflationAuthorizationReceivedTs.add(ALMOST_FULL_DAY_SEC);
        require(_fromThisTs <= dailyPeriodEndTs, ERR_AFTER_DAILY_CYCLE);
        return dailyPeriodEndTs.sub(_fromThisTs).div(_priceEpochDurationSeconds) + 1;
    }
    
    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch`.
     * @dev Internal function
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @param _zeroForClaimed       boolean value that enables skipping amount computation for claimed rewards
     * @return _rewardState         object holding reward state
     * @dev Reverts when queried with `_beneficary` delegating by amount.
     */
    function _getStateOfRewards(
        address _beneficiary,
        uint256 _rewardEpoch,
        bool _zeroForClaimed
    )
        internal view 
        returns (RewardState memory _rewardState)
    {
        uint256 votePowerBlock = ftsoManager.getRewardEpochVotePowerBlock(_rewardEpoch);
        
        // setup for data provider reward
        bool dataProviderClaimed = _isRewardClaimed(_rewardEpoch, _beneficiary, _beneficiary);
        
        // gather data provider reward info
        uint256 dataProviderRewardWeight;
        RewardClaim memory dataProviderReward;
        if (dataProviderClaimed) {
            if (!_zeroForClaimed) {
                // weight is irrelevant
                dataProviderReward.amount = _getClaimedReward(_rewardEpoch, _beneficiary, _beneficiary);
            }
        } else {
            dataProviderRewardWeight = _getRewardWeightForDataProvider(_beneficiary, _rewardEpoch, votePowerBlock);
            dataProviderReward.amount = _getRewardAmount(_rewardEpoch, _beneficiary, dataProviderRewardWeight);
        }
        // flag if data is to be included
        dataProviderReward.claimed = dataProviderClaimed || dataProviderReward.amount > 0;

        // setup for delegation rewards
        address[] memory delegates;
        uint256[] memory bips;
        (delegates, bips, , ) = wNat.delegatesOfAt(_beneficiary, votePowerBlock);
        
        // reward state setup
        _rewardState.dataProviders = new address[]((dataProviderReward.claimed ? 1 : 0) + delegates.length);
        _rewardState.weights = new uint256[](_rewardState.dataProviders.length);
        _rewardState.amounts = new uint256[](_rewardState.dataProviders.length);
        _rewardState.claimed = new bool[](_rewardState.dataProviders.length);

        // data provider reward
        if (dataProviderReward.claimed) {
            _rewardState.dataProviders[0] = _beneficiary;
            _rewardState.claimed[0] = dataProviderClaimed;
            _rewardState.weights[0] = dataProviderRewardWeight;
            _rewardState.amounts[0] = dataProviderReward.amount;            
        }

        // delegation rewards
        if (delegates.length > 0) {
            uint256 delegatorBalance = wNat.balanceOfAt(_beneficiary, votePowerBlock);
            for (uint256 i = 0; i < delegates.length; i++) {
                uint256 p = (dataProviderReward.claimed ? 1 : 0) + i;
                _rewardState.dataProviders[p] = delegates[i];
                _rewardState.claimed[p] = _isRewardClaimed(_rewardEpoch, delegates[i], _beneficiary);
                if (_rewardState.claimed[p]) {
                    if (!_zeroForClaimed) {
                        // weight is irrelevant
                        _rewardState.amounts[p] = _getClaimedReward(_rewardEpoch, delegates[i], _beneficiary);
                    }
                } else {
                    _rewardState.weights[p] = _getRewardWeightForDelegator(
                        delegates[i],
                        delegatorBalance.mulDiv(bips[i], MAX_BIPS),
                        _rewardEpoch
                    );
                    _rewardState.amounts[p] = _getRewardAmount(
                        _rewardEpoch,
                        delegates[i],
                        _rewardState.weights[p]
                    );
                }
            }
        }
    }

    /**
     * @notice Returns the state of rewards for `_beneficiary` at `_rewardEpoch` from `_dataProviders`
     * @param _beneficiary          address of reward beneficiary
     * @param _rewardEpoch          reward epoch number
     * @param _dataProviders        positional array of addresses representing data providers
     * @param _zeroForClaimed       boolean value that enables skipping amount computation for claimed rewards
     * @return _rewardState         object holding reward state
     */
    function _getStateOfRewardsFromDataProviders(
        address _beneficiary,
        uint256 _rewardEpoch,
        address[] memory _dataProviders,
        bool _zeroForClaimed
    )
        internal view 
        returns (RewardState memory _rewardState) 
    {
        uint256 votePowerBlock = ftsoManager.getRewardEpochVotePowerBlock(_rewardEpoch);

        uint256 count = _dataProviders.length;
        _rewardState.dataProviders = _dataProviders;
        _rewardState.weights = new uint256[](count);
        _rewardState.amounts = new uint256[](count);
        _rewardState.claimed = new bool[](count);

        for (uint256 i = 0; i < count; i++) {
            _rewardState.claimed[i] = _isRewardClaimed(_rewardEpoch, _dataProviders[i], _beneficiary);
            if (_rewardState.claimed[i]) {
                if (!_zeroForClaimed) {
                    // weight is irrelevant
                    _rewardState.amounts[i] = _getClaimedReward(_rewardEpoch, _dataProviders[i], _beneficiary);
                }
                continue;
            }

            if (_dataProviders[i] == _beneficiary) {
                _rewardState.weights[i] = _getRewardWeightForDataProvider(
                    _beneficiary,
                    _rewardEpoch,
                    votePowerBlock
                );
            } else {
                uint256 delegatedVotePower = wNat.votePowerFromToAt(_beneficiary, _dataProviders[i], votePowerBlock);
                _rewardState.weights[i] = _getRewardWeightForDelegator(
                    _dataProviders[i],
                    delegatedVotePower,
                    _rewardEpoch
                );
            }
            _rewardState.amounts[i] = _getRewardAmount(
                _rewardEpoch,
                _dataProviders[i],
                _rewardState.weights[i]
            );
        }
    }

    /**
     * @notice Reports if rewards for `_rewardEpoch` are claimable.
     * @param _rewardEpoch          reward epoch number
     * @param _currentRewardEpoch   number of the current reward epoch
     */
    function _isRewardClaimable(uint256 _rewardEpoch, uint256 _currentRewardEpoch) internal view returns (bool) {
        if (_rewardEpoch < nextRewardEpochToExpire || _rewardEpoch >= _currentRewardEpoch) {
            // reward expired and closed or current or future
            return false;
        }
        return true;
    }

    /**
     * @notice Returns the start and the end of the reward epoch range for which the reward is claimable
     * @return _startEpochId        the oldest epoch id that allows reward claiming
     * @return _endEpochId          the newest epoch id that allows reward claiming
     */
    function _getEpochsWithClaimableRewards() internal view 
        returns (
            uint256 _startEpochId,
            uint256 _endEpochId
        ) 
    {
        _startEpochId = nextRewardEpochToExpire;
        uint256 currentRewardEpochId = ftsoManager.getCurrentRewardEpoch();
        require(currentRewardEpochId > 0, ERR_NO_CLAIMABLE_EPOCH);        
        _endEpochId = currentRewardEpochId - 1;
    }

    /**
     * @notice Reports if reward at `_rewardEpoch` for `_dataProvider` has already been claimed by `_claimer`.
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing a data provider
     * @param _claimer              address representing a reward claimer
     */
    function _isRewardClaimed(
        uint256 _rewardEpoch,
        address _dataProvider,
        address _claimer
    )
        internal view
        returns (bool)
    {
        return epochProviderClaimerReward[_rewardEpoch][_dataProvider][_claimer].claimed;
    }

    /**
     * @notice Returns the reward amount at `_rewardEpoch` for `_dataProvider` claimed by `_claimer`.
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing a data provider
     * @param _claimer              address representing a reward claimer
     */
    function _getClaimedReward(
        uint256 _rewardEpoch,
        address _dataProvider,
        address _claimer
    )
        internal view
        returns (uint256)
    {
        return epochProviderClaimerReward[_rewardEpoch][_dataProvider][_claimer].amount;
    }

    /**
     * @notice Returns the reward amount for `_dataProvider` at `_rewardEpoch`
     * @param _rewardEpoch          reward epoch number
     * @param _dataProvider         address representing a data provider     
     * @param _rewardWeight         number representing reward weight
     */
    function _getRewardAmount(
        uint256 _rewardEpoch,
        address _dataProvider,
        uint256 _rewardWeight
    )
        internal view
        returns (uint256)
    {
        if (_rewardWeight == 0) {
            return 0;
        }
        uint256 unclaimedRewardAmount = epochProviderUnclaimedRewardAmount[_rewardEpoch][_dataProvider];
        if (unclaimedRewardAmount == 0) {
            return 0;
        }
        uint256 unclaimedRewardWeight = epochProviderUnclaimedRewardWeight[_rewardEpoch][_dataProvider];
        if (_rewardWeight == unclaimedRewardWeight) {
            return unclaimedRewardAmount;
        }
        assert(_rewardWeight < unclaimedRewardWeight);
        return unclaimedRewardAmount.mulDiv(_rewardWeight, unclaimedRewardWeight);
    }

    /**
     * @notice Returns reward weight for `_dataProvider` at `_rewardEpoch`
     * @param _dataProvider         address representing a data provider
     * @param _rewardEpoch          reward epoch number
     * @param _votePowerBlock       block number used to determine the vote power for reward computation
     */
    function _getRewardWeightForDataProvider(
        address _dataProvider,
        uint256 _rewardEpoch,
        uint256 _votePowerBlock
    )
        internal view
        returns (uint256)
    {
        uint256 dataProviderVotePower = wNat.undelegatedVotePowerOfAt(_dataProvider, _votePowerBlock);
        uint256 votePower = wNat.votePowerOfAt(_dataProvider, _votePowerBlock);

        if (dataProviderVotePower == votePower) {
            // shortcut, but also handles (unlikely) zero vote power case
            return votePower.mul(MAX_BIPS);
        }
        assert(votePower > dataProviderVotePower);

        uint256 rewardWeight = 0;

        // weight share based on data provider undelagated vote power
        if (dataProviderVotePower > 0) {
            rewardWeight += dataProviderVotePower.mul(MAX_BIPS);
        }

        // weight share based on data provider fee
        uint256 feePercentageBIPS = _getDataProviderFeePercentage(_dataProvider, _rewardEpoch);
        if (feePercentageBIPS > 0) {
            rewardWeight += (votePower - dataProviderVotePower).mul(feePercentageBIPS);
        }

        return rewardWeight;
    }

    /**
     * @notice Returns reward weight at `_rewardEpoch` for delegator delegating `_delegatedVotePower` to `_delegate`.
     * @param _delegate             address representing a delegate (data provider)
     * @param _delegatedVotePower   number representing vote power delegated by delegator
     * @param _rewardEpoch          reward epoch number
     */
    function _getRewardWeightForDelegator(        
        address _delegate,
        uint256 _delegatedVotePower,
        uint256 _rewardEpoch
    )
        internal view
        returns (uint256)
    {
        if (_delegatedVotePower == 0) {
            return 0;
        }

        uint256 rewardWeight = 0;

        // reward weight determined by vote power share
        uint256 feePercentageBIPS = _getDataProviderFeePercentage(_delegate, _rewardEpoch);
        if (feePercentageBIPS < MAX_BIPS) {
            rewardWeight += _delegatedVotePower.mul(MAX_BIPS - feePercentageBIPS);
        }

        return rewardWeight;
    }

    /**
     * @notice Returns fee percentage setting for `_dataProvider` at `_rewardEpoch`.
     * @param _dataProvider         address representing a data provider
     * @param _rewardEpoch          reward epoch number
     */
    function _getDataProviderFeePercentage(
        address _dataProvider,
        uint256 _rewardEpoch
    )
        internal view
        returns (uint256)
    {
        FeePercentage[] storage fps = dataProviderFeePercentages[_dataProvider];
        uint256 index = fps.length;
        while (index > 0) {
            index--;
            if (_rewardEpoch >= fps[index].validFromEpoch) {
                return fps[index].value;
            }
        }
        return defaultFeePercentage;
    }

    function _getExpectedBalance() private view returns(uint256 _balanceExpectedWei) {
        return totalInflationReceivedWei
            .add(totalSelfDestructReceivedWei)
            .sub(totalClaimedWei);
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_governance","internalType":"address"},{"type":"uint256","name":"_feePercentageUpdateOffset","internalType":"uint256"},{"type":"uint256","name":"_defaultFeePercentage","internalType":"uint256"}]},{"type":"event","name":"DailyAuthorizedInflationSet","inputs":[{"type":"uint256","name":"authorizedAmountWei","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FeePercentageChanged","inputs":[{"type":"address","name":"dataProvider","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"validFromEpoch","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceProposed","inputs":[{"type":"address","name":"proposedGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceUpdated","inputs":[{"type":"address","name":"oldGovernance","internalType":"address","indexed":false},{"type":"address","name":"newGoveranance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"InflationReceived","inputs":[{"type":"uint256","name":"amountReceivedWei","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RewardClaimed","inputs":[{"type":"address","name":"dataProvider","internalType":"address","indexed":true},{"type":"address","name":"whoClaimed","internalType":"address","indexed":true},{"type":"address","name":"sentTo","internalType":"address","indexed":true},{"type":"uint256","name":"rewardEpoch","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RewardClaimsExpired","inputs":[{"type":"uint256","name":"rewardEpochId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RewardsDistributed","inputs":[{"type":"address","name":"ftso","internalType":"address","indexed":true},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":false},{"type":"address[]","name":"addresses","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"rewards","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"activate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"active","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimGovernance","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"_rewardAmount","internalType":"uint256"}],"name":"claimReward","inputs":[{"type":"address","name":"_recipient","internalType":"address payable"},{"type":"uint256[]","name":"_rewardEpochs","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"_rewardAmount","internalType":"uint256"}],"name":"claimRewardFromDataProviders","inputs":[{"type":"address","name":"_recipient","internalType":"address payable"},{"type":"uint256[]","name":"_rewardEpochs","internalType":"uint256[]"},{"type":"address[]","name":"_dataProviders","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"closeExpiredRewardEpoch","inputs":[{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dailyAuthorizedInflation","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deactivate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"defaultFeePercentage","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"distributeRewards","inputs":[{"type":"address[]","name":"_addresses","internalType":"address[]"},{"type":"uint256[]","name":"_weights","internalType":"uint256[]"},{"type":"uint256","name":"_totalWeight","internalType":"uint256"},{"type":"uint256","name":"_epochId","internalType":"uint256"},{"type":"address","name":"_ftso","internalType":"address"},{"type":"uint256","name":"_priceEpochDurationSeconds","internalType":"uint256"},{"type":"uint256","name":"_currentRewardEpoch","internalType":"uint256"},{"type":"uint256","name":"_priceEpochEndTime","internalType":"uint256"},{"type":"uint256","name":"_votePowerBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"feePercentageUpdateOffset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtsoManager"}],"name":"ftsoManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"_claimed","internalType":"bool"},{"type":"uint256","name":"_amount","internalType":"uint256"}],"name":"getClaimedReward","inputs":[{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"},{"type":"address","name":"_dataProvider","internalType":"address"},{"type":"address","name":"_claimer","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDataProviderCurrentFeePercentage","inputs":[{"type":"address","name":"_dataProvider","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"_feePercentageBIPS","internalType":"uint256[]"},{"type":"uint256[]","name":"_validFromEpoch","internalType":"uint256[]"},{"type":"bool[]","name":"_fixed","internalType":"bool[]"}],"name":"getDataProviderScheduledFeePercentageChanges","inputs":[{"type":"address","name":"_dataProvider","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_totalReward","internalType":"uint256"},{"type":"uint256","name":"_claimedReward","internalType":"uint256"}],"name":"getEpochReward","inputs":[{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_startEpochId","internalType":"uint256"},{"type":"uint256","name":"_endEpochId","internalType":"uint256"}],"name":"getEpochsWithClaimableRewards","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"_epochIds","internalType":"uint256[]"}],"name":"getEpochsWithUnclaimedRewards","inputs":[{"type":"address","name":"_beneficiary","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getInflationAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRewardEpochToExpireNext","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"_dataProviders","internalType":"address[]"},{"type":"uint256[]","name":"_rewardAmounts","internalType":"uint256[]"},{"type":"bool[]","name":"_claimed","internalType":"bool[]"},{"type":"bool","name":"_claimable","internalType":"bool"}],"name":"getStateOfRewards","inputs":[{"type":"address","name":"_beneficiary","internalType":"address"},{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"_rewardAmounts","internalType":"uint256[]"},{"type":"bool[]","name":"_claimed","internalType":"bool[]"},{"type":"bool","name":"_claimable","internalType":"bool"}],"name":"getStateOfRewardsFromDataProviders","inputs":[{"type":"address","name":"_beneficiary","internalType":"address"},{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"},{"type":"address[]","name":"_dataProviders","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_foundationAllocatedFundsWei","internalType":"uint256"},{"type":"uint256","name":"_totalInflationAuthorizedWei","internalType":"uint256"},{"type":"uint256","name":"_totalClaimedWei","internalType":"uint256"}],"name":"getTokenPoolSupplyData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_amount","internalType":"uint256"},{"type":"uint256","name":"_weight","internalType":"uint256"}],"name":"getUnclaimedReward","inputs":[{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"},{"type":"address","name":"_dataProvider","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governance","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialise","inputs":[{"type":"address","name":"_governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastInflationAuthorizationReceivedTs","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeGovernance","inputs":[{"type":"address","name":"_governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"proposedGovernance","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"receiveInflation","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setContractAddresses","inputs":[{"type":"address","name":"_inflation","internalType":"address"},{"type":"address","name":"_ftsoManager","internalType":"contract IIFtsoManager"},{"type":"address","name":"_wNat","internalType":"contract WNat"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDailyAuthorizedInflation","inputs":[{"type":"uint256","name":"_toAuthorizeWei","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"setDataProviderFeePercentage","inputs":[{"type":"uint256","name":"_feePercentageBIPS","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAwardedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalClaimedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalExpiredWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalInflationAuthorizedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalInflationReceivedWei","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSelfDestructReceivedWei","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferGovernance","inputs":[{"type":"address","name":"_governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract WNat"}],"name":"wNat","inputs":[]}]
              

Contract Creation Code

0x60c06040523480156200001157600080fd5b50604051620044ed380380620044ed833981810160405260608110156200003757600080fd5b508051602082015160409092015190919082806001600160a01b0381161562000065576200006581620000cb565b506001600160a01b038116620000b5576040805162461bcd60e51b815260206004820152601060248201526f5f676f7665726e616e6365207a65726f60801b604482015290519081900360640190fd5b50600160025560809190915260a05250620001af565b600154600160a01b900460ff16156200012b576040805162461bcd60e51b815260206004820152601460248201527f696e697469616c6973656420213d2066616c7365000000000000000000000000604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b60805160a05161430b620001e2600039806123b35280613b7652508061119e52806113fa5280611677525061430b6000f3fe6080604052600436106102465760003560e01c806385b4c53811610139578063b4a2043d116100b6578063d38bfff41161007a578063d38bfff414610c33578063d418634a14610c66578063d6c1dbee14610c90578063e273956314610cba578063e416b7e114610ce4578063ed39d3f814610e4957610246565b8063b4a2043d146109f5578063c373a08e14610a78578063cfbcd25f14610aab578063d0c1c39314610ade578063d20bb54214610af357610246565b8063a4472c10116100fd578063a4472c1014610690578063a5555aea146107b0578063a9b79e17146107c5578063b2af870a14610922578063b4824034146109e057610246565b806385b4c538146105925780638ab63380146105ee5780639d6a890f146106335780639edbf007146106665780639f71043e1461067b57610246565b80633e7ff857116101c75780635de8b2f31161018b5780635de8b2f31461050557806360f7ac971461051a578063657d96951461052f578063708e34ce14610568578063729936151461057d57610246565b80633e7ff8571461049c578063473252c4146104b157806351b42b00146104c65780635aa6e675146104db5780635d36b190146104f057610246565b806311a7aaaa1161020e57806311a7aaaa146102e857806316e693281461031957806316fe49c7146103435780632dafdbbf1461035857806333b7971e1461038b57610246565b806302fb0c5e1461024b5780630441218e1461027457806306201f1d146102a25780630cc2a8fe146102ac5780630f15f4c0146102d3575b600080fd5b34801561025757600080fd5b50610260610e5e565b604080519115158252519081900360200190f35b34801561028057600080fd5b50610289610e67565b6040805192835260208301919091528051918290030190f35b6102aa610e7b565b005b3480156102b857600080fd5b506102c161100f565b60408051918252519081900360200190f35b3480156102df57600080fd5b506102aa611015565b3480156102f457600080fd5b506102fd611100565b604080516001600160a01b039092168252519081900360200190f35b34801561032557600080fd5b506102c16004803603602081101561033c57600080fd5b503561110f565b34801561034f57600080fd5b506102c16113f8565b34801561036457600080fd5b5061036d61141c565b60408051938452602084019290925282820152519081900360600190f35b34801561039757600080fd5b506103be600480360360208110156103ae57600080fd5b50356001600160a01b0316611427565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156104065781810151838201526020016103ee565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561044557818101518382015260200161042d565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561048457818101518382015260200161046c565b50505050905001965050505050505060405180910390f35b3480156104a857600080fd5b506102c16116e0565b3480156104bd57600080fd5b506102c16116e6565b3480156104d257600080fd5b506102aa6116ec565b3480156104e757600080fd5b506102fd611749565b3480156104fc57600080fd5b506102aa611758565b34801561051157600080fd5b506102c161181a565b34801561052657600080fd5b506102fd611820565b34801561053b57600080fd5b506102896004803603604081101561055257600080fd5b50803590602001356001600160a01b031661182f565b34801561057457600080fd5b506102c161186b565b34801561058957600080fd5b506102c1611871565b34801561059e57600080fd5b506105d3600480360360608110156105b557600080fd5b508035906001600160a01b0360208201358116916040013516611877565b60408051921515835260208301919091528051918290030190f35b3480156105fa57600080fd5b506102aa6004803603606081101561061157600080fd5b506001600160a01b0381358116916020810135821691604090910135166118b3565b34801561063f57600080fd5b506102aa6004803603602081101561065657600080fd5b50356001600160a01b0316611aae565b34801561067257600080fd5b506102fd611b88565b34801561068757600080fd5b506102c1611b97565b34801561069c57600080fd5b506106c9600480360360408110156106b357600080fd5b506001600160a01b038135169060200135611b9d565b604051808060200180602001806020018515158152602001848103845288818151815260200191508051906020019060200280838360005b83811015610719578181015183820152602001610701565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610758578181015183820152602001610740565b50505050905001848103825286818151815260200191508051906020019060200280838360005b8381101561079757818101518382015260200161077f565b5050505090500197505050505050505060405180910390f35b3480156107bc57600080fd5b506102c1611c6c565b3480156107d157600080fd5b506102aa60048036036101208110156107e957600080fd5b810190602081018135600160201b81111561080357600080fd5b82018360208201111561081557600080fd5b803590602001918460208302840111600160201b8311171561083657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561088557600080fd5b82018360208201111561089757600080fd5b803590602001918460208302840111600160201b831117156108b857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550508235935050506020810135906001600160a01b036040820135169060608101359060808101359060a08101359060c00135611c72565b34801561092e57600080fd5b506102c16004803603604081101561094557600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561096f57600080fd5b82018360208201111561098157600080fd5b803590602001918460208302840111600160201b831117156109a257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506120fc945050505050565b3480156109ec57600080fd5b506102c16123b1565b348015610a0157600080fd5b50610a2860048036036020811015610a1857600080fd5b50356001600160a01b03166123d5565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610a64578181015183820152602001610a4c565b505050509050019250505060405180910390f35b348015610a8457600080fd5b506102aa60048036036020811015610a9b57600080fd5b50356001600160a01b031661257a565b348015610ab757600080fd5b506102c160048036036020811015610ace57600080fd5b50356001600160a01b031661261f565b348015610aea57600080fd5b506102c16126aa565b348015610aff57600080fd5b506102c160048036036060811015610b1657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610b4057600080fd5b820183602082011115610b5257600080fd5b803590602001918460208302840111600160201b83111715610b7357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610bc257600080fd5b820183602082011115610bd457600080fd5b803590602001918460208302840111600160201b83111715610bf557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506126b0945050505050565b348015610c3f57600080fd5b506102aa60048036036020811015610c5657600080fd5b50356001600160a01b0316612951565b348015610c7257600080fd5b5061028960048036036020811015610c8957600080fd5b5035612a13565b348015610c9c57600080fd5b506102aa60048036036020811015610cb357600080fd5b5035612a32565b348015610cc657600080fd5b506102aa60048036036020811015610cdd57600080fd5b5035612b6e565b348015610cf057600080fd5b50610da760048036036060811015610d0757600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b811115610d3657600080fd5b820183602082011115610d4857600080fd5b803590602001918460208302840111600160201b83111715610d6957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612c3d945050505050565b6040518080602001806020018415158152602001838103835286818151815260200191508051906020019060200280838360005b83811015610df3578181015183820152602001610ddb565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015610e32578181015183820152602001610e1a565b505050509050019550505050505060405180910390f35b348015610e5557600080fd5b506102fd612d06565b60035460ff1681565b600080610e72612d15565b90939092509050565b60155460408051808201909152600e81526d696e666c6174696f6e206f6e6c7960901b6020820152906001600160a01b03163314610f375760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610efc578181015183820152602001610ee4565b50505050905090810190601f168015610f295780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000610f42612e1c565b50600f54909150610f539034612e76565b600f5560138190556040805134815290517f95c4e29cc99bc027cfc3cd719d6fd973d5f0317061885fbb322b9b17d8d35d379181900360200190a150610f97612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b8152509061100c5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50565b600d5481565b6000546001600160a01b03163314611066576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6015546001600160a01b03161580159061108a57506014546001600160a01b031615155b80156110a057506016546001600160a01b031615155b6110f1576040805162461bcd60e51b815260206004820152601a60248201527f636f6e747261637420616464726573736573206e6f7420736574000000000000604482015290519081900360640190fd5b6003805460ff19166001179055565b6014546001600160a01b031681565b60006127108211156040518060400160405280601c81526020017f696e76616c6964206665652070657263656e746167652076616c756500000000815250906111995760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5060007f0000000000000000000000000000000000000000000000000000000000000000601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b15801561120b57600080fd5b505afa15801561121f573d6000803e3d6000fd5b505050506040513d602081101561123557600080fd5b5051336000908152600a60205260409020805492909101925090801561132d5781600182038154811061126457fe5b9060005260206000200160000160029054906101000a90046001600160f01b03166001600160f01b03168310156040518060600160405280602181526020016142b560219139906112f65760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5081600182038154811061130657fe5b6000918252602090912001546201000090046001600160f01b031683141561132d57600019015b8154811415611343578154600101825560008290525b8482828154811061135057fe5b6000918252602090912001805461ffff191661ffff92909216919091179055600160f01b831061137c57fe5b8282828154811061138957fe5b60009182526020918290200180546001600160f01b0393909316620100000261ffff9093169290921790915560408051878152918201859052805133927fd89f05622c2dcb0b4fcaa19e62fc2a2b0923955685fb7b0c641467f764244abc92908290030190a250909392505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600e54600c54600092565b6001600160a01b0381166000908152600a6020526040902080546060918291829190156116d857601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b15801561149357600080fd5b505afa1580156114a7573d6000803e3d6000fd5b505050506040513d60208110156114bd57600080fd5b505182549091505b6000811180156114fd5750818360018303815481106114e057fe5b6000918252602090912001546201000090046001600160f01b0316115b1561150b57600019016114c5565b825481900380156116d457806001600160401b038111801561152c57600080fd5b50604051908082528060200260200182016040528015611556578160200160208202803683370190505b509650806001600160401b038111801561156f57600080fd5b50604051908082528060200260200182016040528015611599578160200160208202803683370190505b509550806001600160401b03811180156115b257600080fd5b506040519080825280602002602001820160405280156115dc578160200160208202803683370190505b50945060005b818110156116d25784838201815481106115f857fe5b600091825260209091200154885161ffff9091169089908390811061161957fe5b602002602001018181525050848382018154811061163357fe5b9060005260206000200160000160029054906101000a90046001600160f01b03166001600160f01b031687828151811061166957fe5b6020026020010181815250507f0000000000000000000000000000000000000000000000000000000000000000848883815181106116a357fe5b60200260200101510314158682815181106116ba57fe5b911515602092830291909101909101526001016115e2565b505b5050505b509193909250565b60045490565b60115481565b6000546001600160a01b0316331461173d576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6003805460ff19169055565b6000546001600160a01b031681565b6001546001600160a01b031633146117a7576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600b5481565b6001546001600160a01b031681565b60008281526006602090815260408083206001600160a01b03909416808452938252808320549483526005825280832093835292905220549091565b60125481565b60105481565b60009283526007602090815260408085206001600160a01b0394851686528252808520929093168452529020805460019091015460ff90911691565b6000546001600160a01b03163314611904576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60408051808201909152600e81526d696e666c6174696f6e207a65726f60901b60208201526001600160a01b03841661197e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5060408051808201909152600f81526e373790333a39b79036b0b730b3b2b960891b60208201526001600160a01b0383166119fa5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506040805180820190915260078152661b9bc81dd3985d60ca1b60208201526001600160a01b038216611a6e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50601580546001600160a01b039485166001600160a01b031991821617909155601480549385169382169390931790925560168054919093169116179055565b600154600160a01b900460ff1615611b04576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6016546001600160a01b031681565b600c5481565b6060806060600080601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b158015611bf357600080fd5b505afa158015611c07573d6000803e3d6000fd5b505050506040513d6020811015611c1d57600080fd5b50519050611c2b8682612efb565b91508180611c3857508086145b15611c62576000611c4b88886000612f23565b805160408201516060909201519097509095509350505b5092959194509250565b600f5481565b6014546040805180820190915260118152706674736f206d616e61676572206f6e6c7960781b6020820152906001600160a01b03163314611cf45760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b508615801590611d045750885115155b611d0a57fe5b6000611d27611d1984876135f6565b611d216136a7565b906136cc565b905060008a516001600160401b0381118015611d4257600080fd5b50604051908082528060200260200182016040528015611d6c578160200160208202803683370190505b5090508181600081518110611d7d57fe5b602002602001018181525050888a600081518110611d9757fe5b60209081029190910101528a51600019015b611df98b8281518110611db857fe5b60200260200101518c600081518110611dcd57fe5b602002602001015184600081518110611de257fe5b60200260200101516137339092919063ffffffff16565b828281518110611e0557fe5b602002602001018181525050818181518110611e1d57fe5b60200260200101516006600088815260200190815260200160002060008e8481518110611e4657fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008282540192505081905550611f30612710601660009054906101000a90046001600160a01b03166001600160a01b03166392bfe6d88f8581518110611eb157fe5b6020026020010151886040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b158015611efe57600080fd5b505afa158015611f12573d6000803e3d6000fd5b505050506040513d6020811015611f2857600080fd5b505190613839565b6005600088815260200190815260200160002060008e8481518110611f5157fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055508060001415611f8b57611ffa565b818181518110611f9757fe5b602002602001015182600081518110611fac57fe5b6020026020010181815103915081815250508a8181518110611fca57fe5b60200260200101518b600081518110611fdf57fe5b60209081029190910101805191909103905260001901611da9565b6000868152600860205260409020805484019055600b5461201b9084612e76565b600b81905550876001600160a01b03167f8b2bc56c62594afde5b520e83e1ca19ebd071798db21382e328014f47b31ce578a8e85604051808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015612099578181015183820152602001612081565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156120d85781810151838201526020016120c0565b505050509050019550505050505060405180910390a2505050505050505050505050565b60035460408051808201909152601a81527f726577617264206d616e61676572206465616374697661746564000000000000602082015260009160ff166121845760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506002805414156121dc576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600280556121e8612e1c565b5050601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b15801561222f57600080fd5b505afa158015612243573d6000803e3d6000fd5b505050506040513d602081101561225957600080fd5b5051905060005b83518110156123185761228684828151811061227857fe5b602002602001015183612efb565b61228f57612310565b60006122b0338684815181106122a157fe5b60200260200101516001612f23565b905060006122d2878785815181106122c457fe5b602002602001015184613892565b905080600960008886815181106122e557fe5b6020026020010151815260200190815260200160002060008282540192505081905550808501945050505b600101612260565b506123238483613a1e565b50476013556001600255612335612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b815250906123aa5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60606000806123e2612d15565b90925090506001828203016000816001600160401b038111801561240557600080fd5b5060405190808252806020026020018201604052801561242f578160200160208202803683370190505b5090506000805b838110156124dc57600061244e898389016001612f23565b905060005b8160600151518110156124d2578160600151818151811061247057fe5b602002602001015115801561249c575060008260400151828151811061249257fe5b6020026020010151115b156124ca5760018584815181106124af57fe5b911515602092830291909101909101526001909301926124d2565b600101612453565b5050600101612436565b50806001600160401b03811180156124f357600080fd5b5060405190808252806020026020018201604052801561251d578160200160208202803683370190505b5095506000805b8481101561256e5783818151811061253857fe5b6020026020010151156125665780870188838151811061255457fe5b60209081029190910101526001909101905b600101612524565b50505050505050919050565b6000546001600160a01b031633146125cb576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b60006126a482601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b15801561267357600080fd5b505afa158015612687573d6000803e3d6000fd5b505050506040513d602081101561269d57600080fd5b5051613af1565b92915050565b600e5481565b60035460408051808201909152601a81527f726577617264206d616e61676572206465616374697661746564000000000000602082015260009160ff166127385760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50600280541415612790576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002805561279c612e1c565b5050601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b1580156127e357600080fd5b505afa1580156127f7573d6000803e3d6000fd5b505050506040513d602081101561280d57600080fd5b5051905060005b84518110156128b75761282c85828151811061227857fe5b612835576128af565b61283d614254565b61285d3387848151811061284d57fe5b6020026020010151876001613b9d565b90506000612871888885815181106122c457fe5b9050806009600089868151811061288457fe5b6020026020010151815260200190815260200160002060008282540192505081905550808501945050505b600101612814565b506128c28583613a1e565b504760135560016002556128d4612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b815250906129495760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b509392505050565b6000546001600160a01b031633146129a2576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6000908152600860209081526040808320546009909252909120549091565b6014546040805180820190915260118152706674736f206d616e61676572206f6e6c7960781b6020820152906001600160a01b03163314612ab45760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b508060045414612b03576040805162461bcd60e51b81526020600482015260156024820152741ddc9bdb99c81c995dd85c9908195c1bd8da081a59605a1b604482015290519081900360640190fd5b600081815260096020908152604080832054600890925290912054600d5491900390612b2f9082612e76565b600d556040805183815290517f5d05c64f281304391697cf987812e1a736413a062a9bdf39af4102209eb6fa589181900360200190a150600101600455565b60155460408051808201909152600e81526d696e666c6174696f6e206f6e6c7960901b6020820152906001600160a01b03163314612bed5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506012819055600e54612c009082612e76565b600e55426011556040805182815290517f187f32a0f765499f15b3bb52ed0aebf6015059f230f2ace7e701e60a476695959181900360200190a150565b606080600080601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9157600080fd5b505afa158015612ca5573d6000803e3d6000fd5b505050506040513d6020811015612cbb57600080fd5b50519050612cc98682612efb565b91508180612cd657508086145b15612cfc576000612cea8888886000613b9d565b90508060400151945080606001519350505b5093509350939050565b6015546001600160a01b031690565b60048054601454604080516339f20c3560e21b81529051929360009384936001600160a01b03169263e7c830d49281810192602092909190829003018186803b158015612d6157600080fd5b505afa158015612d75573d6000803e3d6000fd5b505050506040513d6020811015612d8b57600080fd5b505160408051808201909152601f81527f6e6f2065706f6368207769746820636c61696d61626c65207265776172647300602082015290915081612e105760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50600181039150509091565b600080612e3434601354612e7690919063ffffffff16565b905047915080821115612e6857612e6081612e5a84601054612e7690919063ffffffff16565b90613f46565b601055612e72565b80821015612e7257fe5b9091565b600082820183811015612ed0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6000612ef6600c54612e5a601054600f54612e7690919063ffffffff16565b905090565b6000600454831080612f0d5750818310155b15612f1a575060006126a4565b50600192915050565b612f2b614254565b60145460408051637976d5ad60e11b81526004810186905290516000926001600160a01b03169163f2edab5a916024808301926020929190829003018186803b158015612f7757600080fd5b505afa158015612f8b573d6000803e3d6000fd5b505050506040513d6020811015612fa157600080fd5b505190506000612fb2858780613fa3565b90506000612fbe61427c565b8215612fdf5785612fda57612fd487898a613fd6565b60208201525b612ffd565b612fea888886614009565b9150612ff787898461417f565b60208201525b828061300d575060008160200151115b151581526016546040805163ed475a7960e01b81526001600160a01b038b811660048301526024820188905291516060938493169163ed475a79916044808301926000929190829003018186803b15801561306757600080fd5b505afa15801561307b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260808110156130a457600080fd5b8101908080516040519392919084600160201b8211156130c357600080fd5b9083019060208201858111156130d857600080fd5b82518660208202830111600160201b821117156130f457600080fd5b82525081516020918201928201910280838360005b83811015613121578181015183820152602001613109565b5050505090500160405260200180516040519392919084600160201b82111561314957600080fd5b90830190602082018581111561315e57600080fd5b82518660208202830111600160201b8211171561317a57600080fd5b82525081516020918201928201910280838360005b838110156131a757818101518382015260200161318f565b5050505091909101604052505083518751949650929450919291506131cf90505760006131d2565b60015b60ff16016001600160401b03811180156131eb57600080fd5b50604051908082528060200260200182016040528015613215578160200160208202803683370190505b50808852516001600160401b038111801561322f57600080fd5b50604051908082528060200260200182016040528015613259578160200160208202803683370190505b5060208801528651516001600160401b038111801561327757600080fd5b506040519080825280602002602001820160405280156132a1578160200160208202803683370190505b5060408801528651516001600160401b03811180156132bf57600080fd5b506040519080825280602002602001820160405280156132e9578160200160208202803683370190505b50606088015282511561338d5789876000015160008151811061330857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084876060015160008151811061333a57fe5b60200260200101901515908115158152505083876020015160008151811061335e57fe5b6020026020010181815250508260200151876040015160008151811061338057fe5b6020026020010181815250505b8151156135e9576016546040805163277166bf60e11b81526001600160a01b038d81166004830152602482018a905291516000939290921691634ee2cd7e91604480820192602092909190829003018186803b1580156133ec57600080fd5b505afa158015613400573d6000803e3d6000fd5b505050506040513d602081101561341657600080fd5b5051905060005b83518110156135e657600081866000015161343957600061343c565b60015b60ff1601905084828151811061344e57fe5b60200260200101518a60000151828151811061346657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506134a48c86848151811061349657fe5b60200260200101518f613fa3565b8a6060015182815181106134b457fe5b602002602001019015159081151581525050896060015181815181106134d657fe5b602002602001015115613528578a613523576135068c8684815181106134f857fe5b60200260200101518f613fd6565b8a60400151828151811061351657fe5b6020026020010181815250505b6135dd565b61356f85838151811061353757fe5b602002602001015161356986858151811061354e57fe5b6020026020010151612710876137339092919063ffffffff16565b8e614211565b8a60200151828151811061357f57fe5b6020026020010181815250506135c08c86848151811061359b57fe5b60200260200101518c6020015184815181106135b357fe5b602002602001015161417f565b8a6040015182815181106135d057fe5b6020026020010181815250505b5060010161341d565b50505b5050505050509392505050565b6000806136116201517f601154612e7690919063ffffffff16565b905080841115604051806040016040528060118152602001706166746572206461696c79206379636c6560781b8152509061368d5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5061369c83611d218387613f46565b600101949350505050565b6000612ef66136c3600d54600b54613f4690919063ffffffff16565b600e5490613f46565b6000808211613722576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161372b57fe5b049392505050565b600080821161377c576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b8361378957506000612ed0565b8383028385828161379657fe5b0414156137af578281816137a657fe5b04915050612ed0565b60008386816137ba57fe5b04905060008487816137c857fe5b06905060008587816137d657fe5b04905060008688816137e457fe5b06905061382c6137f888611d218685613839565b6138266138058686613839565b6138266138128987613839565b6138268d6138208c8b613839565b90613839565b90612e76565b9998505050505050505050565b600082613848575060006126a4565b8282028284828161385557fe5b0414612ed05760405162461bcd60e51b81526004018080602001828103825260218152602001806142946021913960400191505060405180910390fd5b600080805b835151811015613a1557836060015181815181106138b157fe5b6020026020010151156138c357613a0d565b6000846000015182815181106138d557fe5b602002602001015190506000856020015183815181106138f157fe5b60200260200101519050600081111561392e5760008781526005602090815260408083206001600160a01b03861684529091529020805482900390555b60008660400151848151811061394057fe5b6020026020010151905060008111156139895760008881526006602090815260408083206001600160a01b0387168452909152902080548290039055600c805482019055938401935b60008881526007602090815260408083206001600160a01b038088168086529184528285203380875290855294839020805460ff191660019081178255810187905583518e815294850187905283519095918f169491937f6ec685171a9028d19dc155a48e7824e3c68b03bc8995410e006abe3cbbeb3e2d928290030190a4505050505b600101613897565b50949350505050565b8015613aed576040516000906001600160a01b0384169083908381818185875af1925050503d8060008114613a6f576040519150601f19603f3d011682016040523d82523d6000602084013e613a74565b606091505b50509050806040518060400160405280600c81526020016b18db185a5b4819985a5b195960a21b81525090613aea5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50505b5050565b6001600160a01b0382166000908152600a6020526040812080545b8015613b7357815460001990910190829082908110613b2757fe5b6000918252602090912001546201000090046001600160f01b03168410613b6e57818181548110613b5457fe5b60009182526020909120015461ffff1692506126a4915050565b613b0c565b507f0000000000000000000000000000000000000000000000000000000000000000949350505050565b613ba5614254565b60145460408051637976d5ad60e11b81526004810187905290516000926001600160a01b03169163f2edab5a916024808301926020929190829003018186803b158015613bf157600080fd5b505afa158015613c05573d6000803e3d6000fd5b505050506040513d6020811015613c1b57600080fd5b50518451858452909150806001600160401b0381118015613c3b57600080fd5b50604051908082528060200260200182016040528015613c65578160200160208202803683370190505b506020840152806001600160401b0381118015613c8157600080fd5b50604051908082528060200260200182016040528015613cab578160200160208202803683370190505b506040840152806001600160401b0381118015613cc757600080fd5b50604051908082528060200260200182016040528015613cf1578160200160208202803683370190505b50606084015260005b81811015613f3b57613d2087878381518110613d1257fe5b60200260200101518a613fa3565b84606001518281518110613d3057fe5b60200260200101901515908115158152505083606001518181518110613d5257fe5b602002602001015115613da45784613d9f57613d8287878381518110613d7457fe5b60200260200101518a613fd6565b84604001518281518110613d9257fe5b6020026020010181815250505b613f33565b876001600160a01b0316868281518110613dba57fe5b60200260200101516001600160a01b03161415613dfd57613ddc888885614009565b84602001518281518110613dec57fe5b602002602001018181525050613eee565b60165486516000916001600160a01b03169063e64767aa908b908a9086908110613e2357fe5b6020026020010151876040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b03168152602001828152602001935050505060206040518083038186803b158015613e8057600080fd5b505afa158015613e94573d6000803e3d6000fd5b505050506040513d6020811015613eaa57600080fd5b50518751909150613ed090889084908110613ec157fe5b6020026020010151828a614211565b85602001518381518110613ee057fe5b602002602001018181525050505b613f1687878381518110613efe57fe5b6020026020010151866020015184815181106135b357fe5b84604001518281518110613f2657fe5b6020026020010181815250505b600101613cfa565b505050949350505050565b600082821115613f9d576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60009283526007602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b60009283526007602090815260408085206001600160a01b03948516865282528085209290931684525290206001015490565b60165460408051634181ad4160e11b81526001600160a01b038681166004830152602482018590529151600093849316916383035a82916044808301926020929190829003018186803b15801561405f57600080fd5b505afa158015614073573d6000803e3d6000fd5b505050506040513d602081101561408957600080fd5b505160165460408051631257fcdb60e31b81526001600160a01b03898116600483015260248201889052915193945060009391909216916392bfe6d8916044808301926020929190829003018186803b1580156140e557600080fd5b505afa1580156140f9573d6000803e3d6000fd5b505050506040513d602081101561410f57600080fd5b50519050818114156141305761412781612710613839565b92505050612ed0565b81811161413957fe5b6000821561414f5761414d83612710613839565b015b600061415b8888613af1565b905080156141745761416f84840382613839565b820191505b509695505050505050565b60008161418e57506000612ed0565b60008481526006602090815260408083206001600160a01b0387168452909152902054806141c0576000915050612ed0565b60008581526005602090815260408083206001600160a01b0388168452909152902054838114156141f357509050612ed0565b8084106141fc57fe5b614207828583613733565b9695505050505050565b60008261422057506000612ed0565b60008061422d8685613af1565b9050612710811015613a155761424885612710839003613839565b90910195945050505050565b6040518060800160405280606081526020016060815260200160608152602001606081525090565b60408051808201909152600080825260208201529056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776665652070657263656e746167652063616e206e6f742062652075706461746564a2646970667358221220c77332c03f54170b5ac7d2477cae90b33dc94b91c253bacad5a3af23c10b8cb764736f6c63430007060033000000000000000000000000493044fbbaa7f9f78379864fa88accaff6a7586e000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000007d0

Deployed ByteCode

0x6080604052600436106102465760003560e01c806385b4c53811610139578063b4a2043d116100b6578063d38bfff41161007a578063d38bfff414610c33578063d418634a14610c66578063d6c1dbee14610c90578063e273956314610cba578063e416b7e114610ce4578063ed39d3f814610e4957610246565b8063b4a2043d146109f5578063c373a08e14610a78578063cfbcd25f14610aab578063d0c1c39314610ade578063d20bb54214610af357610246565b8063a4472c10116100fd578063a4472c1014610690578063a5555aea146107b0578063a9b79e17146107c5578063b2af870a14610922578063b4824034146109e057610246565b806385b4c538146105925780638ab63380146105ee5780639d6a890f146106335780639edbf007146106665780639f71043e1461067b57610246565b80633e7ff857116101c75780635de8b2f31161018b5780635de8b2f31461050557806360f7ac971461051a578063657d96951461052f578063708e34ce14610568578063729936151461057d57610246565b80633e7ff8571461049c578063473252c4146104b157806351b42b00146104c65780635aa6e675146104db5780635d36b190146104f057610246565b806311a7aaaa1161020e57806311a7aaaa146102e857806316e693281461031957806316fe49c7146103435780632dafdbbf1461035857806333b7971e1461038b57610246565b806302fb0c5e1461024b5780630441218e1461027457806306201f1d146102a25780630cc2a8fe146102ac5780630f15f4c0146102d3575b600080fd5b34801561025757600080fd5b50610260610e5e565b604080519115158252519081900360200190f35b34801561028057600080fd5b50610289610e67565b6040805192835260208301919091528051918290030190f35b6102aa610e7b565b005b3480156102b857600080fd5b506102c161100f565b60408051918252519081900360200190f35b3480156102df57600080fd5b506102aa611015565b3480156102f457600080fd5b506102fd611100565b604080516001600160a01b039092168252519081900360200190f35b34801561032557600080fd5b506102c16004803603602081101561033c57600080fd5b503561110f565b34801561034f57600080fd5b506102c16113f8565b34801561036457600080fd5b5061036d61141c565b60408051938452602084019290925282820152519081900360600190f35b34801561039757600080fd5b506103be600480360360208110156103ae57600080fd5b50356001600160a01b0316611427565b60405180806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156104065781810151838201526020016103ee565b50505050905001848103835286818151815260200191508051906020019060200280838360005b8381101561044557818101518382015260200161042d565b50505050905001848103825285818151815260200191508051906020019060200280838360005b8381101561048457818101518382015260200161046c565b50505050905001965050505050505060405180910390f35b3480156104a857600080fd5b506102c16116e0565b3480156104bd57600080fd5b506102c16116e6565b3480156104d257600080fd5b506102aa6116ec565b3480156104e757600080fd5b506102fd611749565b3480156104fc57600080fd5b506102aa611758565b34801561051157600080fd5b506102c161181a565b34801561052657600080fd5b506102fd611820565b34801561053b57600080fd5b506102896004803603604081101561055257600080fd5b50803590602001356001600160a01b031661182f565b34801561057457600080fd5b506102c161186b565b34801561058957600080fd5b506102c1611871565b34801561059e57600080fd5b506105d3600480360360608110156105b557600080fd5b508035906001600160a01b0360208201358116916040013516611877565b60408051921515835260208301919091528051918290030190f35b3480156105fa57600080fd5b506102aa6004803603606081101561061157600080fd5b506001600160a01b0381358116916020810135821691604090910135166118b3565b34801561063f57600080fd5b506102aa6004803603602081101561065657600080fd5b50356001600160a01b0316611aae565b34801561067257600080fd5b506102fd611b88565b34801561068757600080fd5b506102c1611b97565b34801561069c57600080fd5b506106c9600480360360408110156106b357600080fd5b506001600160a01b038135169060200135611b9d565b604051808060200180602001806020018515158152602001848103845288818151815260200191508051906020019060200280838360005b83811015610719578181015183820152602001610701565b50505050905001848103835287818151815260200191508051906020019060200280838360005b83811015610758578181015183820152602001610740565b50505050905001848103825286818151815260200191508051906020019060200280838360005b8381101561079757818101518382015260200161077f565b5050505090500197505050505050505060405180910390f35b3480156107bc57600080fd5b506102c1611c6c565b3480156107d157600080fd5b506102aa60048036036101208110156107e957600080fd5b810190602081018135600160201b81111561080357600080fd5b82018360208201111561081557600080fd5b803590602001918460208302840111600160201b8311171561083657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561088557600080fd5b82018360208201111561089757600080fd5b803590602001918460208302840111600160201b831117156108b857600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550508235935050506020810135906001600160a01b036040820135169060608101359060808101359060a08101359060c00135611c72565b34801561092e57600080fd5b506102c16004803603604081101561094557600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561096f57600080fd5b82018360208201111561098157600080fd5b803590602001918460208302840111600160201b831117156109a257600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506120fc945050505050565b3480156109ec57600080fd5b506102c16123b1565b348015610a0157600080fd5b50610a2860048036036020811015610a1857600080fd5b50356001600160a01b03166123d5565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610a64578181015183820152602001610a4c565b505050509050019250505060405180910390f35b348015610a8457600080fd5b506102aa60048036036020811015610a9b57600080fd5b50356001600160a01b031661257a565b348015610ab757600080fd5b506102c160048036036020811015610ace57600080fd5b50356001600160a01b031661261f565b348015610aea57600080fd5b506102c16126aa565b348015610aff57600080fd5b506102c160048036036060811015610b1657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b811115610b4057600080fd5b820183602082011115610b5257600080fd5b803590602001918460208302840111600160201b83111715610b7357600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b811115610bc257600080fd5b820183602082011115610bd457600080fd5b803590602001918460208302840111600160201b83111715610bf557600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506126b0945050505050565b348015610c3f57600080fd5b506102aa60048036036020811015610c5657600080fd5b50356001600160a01b0316612951565b348015610c7257600080fd5b5061028960048036036020811015610c8957600080fd5b5035612a13565b348015610c9c57600080fd5b506102aa60048036036020811015610cb357600080fd5b5035612a32565b348015610cc657600080fd5b506102aa60048036036020811015610cdd57600080fd5b5035612b6e565b348015610cf057600080fd5b50610da760048036036060811015610d0757600080fd5b6001600160a01b0382351691602081013591810190606081016040820135600160201b811115610d3657600080fd5b820183602082011115610d4857600080fd5b803590602001918460208302840111600160201b83111715610d6957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550612c3d945050505050565b6040518080602001806020018415158152602001838103835286818151815260200191508051906020019060200280838360005b83811015610df3578181015183820152602001610ddb565b50505050905001838103825285818151815260200191508051906020019060200280838360005b83811015610e32578181015183820152602001610e1a565b505050509050019550505050505060405180910390f35b348015610e5557600080fd5b506102fd612d06565b60035460ff1681565b600080610e72612d15565b90939092509050565b60155460408051808201909152600e81526d696e666c6174696f6e206f6e6c7960901b6020820152906001600160a01b03163314610f375760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610efc578181015183820152602001610ee4565b50505050905090810190601f168015610f295780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000610f42612e1c565b50600f54909150610f539034612e76565b600f5560138190556040805134815290517f95c4e29cc99bc027cfc3cd719d6fd973d5f0317061885fbb322b9b17d8d35d379181900360200190a150610f97612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b8152509061100c5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50565b600d5481565b6000546001600160a01b03163314611066576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6015546001600160a01b03161580159061108a57506014546001600160a01b031615155b80156110a057506016546001600160a01b031615155b6110f1576040805162461bcd60e51b815260206004820152601a60248201527f636f6e747261637420616464726573736573206e6f7420736574000000000000604482015290519081900360640190fd5b6003805460ff19166001179055565b6014546001600160a01b031681565b60006127108211156040518060400160405280601c81526020017f696e76616c6964206665652070657263656e746167652076616c756500000000815250906111995760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5060007f0000000000000000000000000000000000000000000000000000000000000003601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b15801561120b57600080fd5b505afa15801561121f573d6000803e3d6000fd5b505050506040513d602081101561123557600080fd5b5051336000908152600a60205260409020805492909101925090801561132d5781600182038154811061126457fe5b9060005260206000200160000160029054906101000a90046001600160f01b03166001600160f01b03168310156040518060600160405280602181526020016142b560219139906112f65760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5081600182038154811061130657fe5b6000918252602090912001546201000090046001600160f01b031683141561132d57600019015b8154811415611343578154600101825560008290525b8482828154811061135057fe5b6000918252602090912001805461ffff191661ffff92909216919091179055600160f01b831061137c57fe5b8282828154811061138957fe5b60009182526020918290200180546001600160f01b0393909316620100000261ffff9093169290921790915560408051878152918201859052805133927fd89f05622c2dcb0b4fcaa19e62fc2a2b0923955685fb7b0c641467f764244abc92908290030190a250909392505050565b7f000000000000000000000000000000000000000000000000000000000000000381565b600e54600c54600092565b6001600160a01b0381166000908152600a6020526040902080546060918291829190156116d857601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b15801561149357600080fd5b505afa1580156114a7573d6000803e3d6000fd5b505050506040513d60208110156114bd57600080fd5b505182549091505b6000811180156114fd5750818360018303815481106114e057fe5b6000918252602090912001546201000090046001600160f01b0316115b1561150b57600019016114c5565b825481900380156116d457806001600160401b038111801561152c57600080fd5b50604051908082528060200260200182016040528015611556578160200160208202803683370190505b509650806001600160401b038111801561156f57600080fd5b50604051908082528060200260200182016040528015611599578160200160208202803683370190505b509550806001600160401b03811180156115b257600080fd5b506040519080825280602002602001820160405280156115dc578160200160208202803683370190505b50945060005b818110156116d25784838201815481106115f857fe5b600091825260209091200154885161ffff9091169089908390811061161957fe5b602002602001018181525050848382018154811061163357fe5b9060005260206000200160000160029054906101000a90046001600160f01b03166001600160f01b031687828151811061166957fe5b6020026020010181815250507f0000000000000000000000000000000000000000000000000000000000000003848883815181106116a357fe5b60200260200101510314158682815181106116ba57fe5b911515602092830291909101909101526001016115e2565b505b5050505b509193909250565b60045490565b60115481565b6000546001600160a01b0316331461173d576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6003805460ff19169055565b6000546001600160a01b031681565b6001546001600160a01b031633146117a7576040805162461bcd60e51b815260206004820152600d60248201526c1b9bdd0818db185a5b585a5b9d609a1b604482015290519081900360640190fd5b600054600154604080516001600160a01b03938416815292909116602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600b5481565b6001546001600160a01b031681565b60008281526006602090815260408083206001600160a01b03909416808452938252808320549483526005825280832093835292905220549091565b60125481565b60105481565b60009283526007602090815260408085206001600160a01b0394851686528252808520929093168452529020805460019091015460ff90911691565b6000546001600160a01b03163314611904576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b60408051808201909152600e81526d696e666c6174696f6e207a65726f60901b60208201526001600160a01b03841661197e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5060408051808201909152600f81526e373790333a39b79036b0b730b3b2b960891b60208201526001600160a01b0383166119fa5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506040805180820190915260078152661b9bc81dd3985d60ca1b60208201526001600160a01b038216611a6e5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50601580546001600160a01b039485166001600160a01b031991821617909155601480549385169382169390931790925560168054919093169116179055565b600154600160a01b900460ff1615611b04576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b6001805460ff60a01b1916600160a01b179055600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6016546001600160a01b031681565b600c5481565b6060806060600080601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b158015611bf357600080fd5b505afa158015611c07573d6000803e3d6000fd5b505050506040513d6020811015611c1d57600080fd5b50519050611c2b8682612efb565b91508180611c3857508086145b15611c62576000611c4b88886000612f23565b805160408201516060909201519097509095509350505b5092959194509250565b600f5481565b6014546040805180820190915260118152706674736f206d616e61676572206f6e6c7960781b6020820152906001600160a01b03163314611cf45760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b508615801590611d045750885115155b611d0a57fe5b6000611d27611d1984876135f6565b611d216136a7565b906136cc565b905060008a516001600160401b0381118015611d4257600080fd5b50604051908082528060200260200182016040528015611d6c578160200160208202803683370190505b5090508181600081518110611d7d57fe5b602002602001018181525050888a600081518110611d9757fe5b60209081029190910101528a51600019015b611df98b8281518110611db857fe5b60200260200101518c600081518110611dcd57fe5b602002602001015184600081518110611de257fe5b60200260200101516137339092919063ffffffff16565b828281518110611e0557fe5b602002602001018181525050818181518110611e1d57fe5b60200260200101516006600088815260200190815260200160002060008e8481518110611e4657fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008282540192505081905550611f30612710601660009054906101000a90046001600160a01b03166001600160a01b03166392bfe6d88f8581518110611eb157fe5b6020026020010151886040518363ffffffff1660e01b815260040180836001600160a01b031681526020018281526020019250505060206040518083038186803b158015611efe57600080fd5b505afa158015611f12573d6000803e3d6000fd5b505050506040513d6020811015611f2857600080fd5b505190613839565b6005600088815260200190815260200160002060008e8481518110611f5157fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055508060001415611f8b57611ffa565b818181518110611f9757fe5b602002602001015182600081518110611fac57fe5b6020026020010181815103915081815250508a8181518110611fca57fe5b60200260200101518b600081518110611fdf57fe5b60209081029190910101805191909103905260001901611da9565b6000868152600860205260409020805484019055600b5461201b9084612e76565b600b81905550876001600160a01b03167f8b2bc56c62594afde5b520e83e1ca19ebd071798db21382e328014f47b31ce578a8e85604051808481526020018060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015612099578181015183820152602001612081565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156120d85781810151838201526020016120c0565b505050509050019550505050505060405180910390a2505050505050505050505050565b60035460408051808201909152601a81527f726577617264206d616e61676572206465616374697661746564000000000000602082015260009160ff166121845760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506002805414156121dc576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600280556121e8612e1c565b5050601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b15801561222f57600080fd5b505afa158015612243573d6000803e3d6000fd5b505050506040513d602081101561225957600080fd5b5051905060005b83518110156123185761228684828151811061227857fe5b602002602001015183612efb565b61228f57612310565b60006122b0338684815181106122a157fe5b60200260200101516001612f23565b905060006122d2878785815181106122c457fe5b602002602001015184613892565b905080600960008886815181106122e557fe5b6020026020010151815260200190815260200160002060008282540192505081905550808501945050505b600101612260565b506123238483613a1e565b50476013556001600255612335612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b815250906123aa5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5092915050565b7f00000000000000000000000000000000000000000000000000000000000007d081565b60606000806123e2612d15565b90925090506001828203016000816001600160401b038111801561240557600080fd5b5060405190808252806020026020018201604052801561242f578160200160208202803683370190505b5090506000805b838110156124dc57600061244e898389016001612f23565b905060005b8160600151518110156124d2578160600151818151811061247057fe5b602002602001015115801561249c575060008260400151828151811061249257fe5b6020026020010151115b156124ca5760018584815181106124af57fe5b911515602092830291909101909101526001909301926124d2565b600101612453565b5050600101612436565b50806001600160401b03811180156124f357600080fd5b5060405190808252806020026020018201604052801561251d578160200160208202803683370190505b5095506000805b8481101561256e5783818151811061253857fe5b6020026020010151156125665780870188838151811061255457fe5b60209081029190910101526001909101905b600101612524565b50505050505050919050565b6000546001600160a01b031633146125cb576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600180546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f1f95fb40be3a947982072902a887b521248d1d8931a39eb38f84f4d6fd758b699181900360200190a150565b60006126a482601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b15801561267357600080fd5b505afa158015612687573d6000803e3d6000fd5b505050506040513d602081101561269d57600080fd5b5051613af1565b92915050565b600e5481565b60035460408051808201909152601a81527f726577617264206d616e61676572206465616374697661746564000000000000602082015260009160ff166127385760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50600280541415612790576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002805561279c612e1c565b5050601454604080516339f20c3560e21b815290516000926001600160a01b03169163e7c830d4916004808301926020929190829003018186803b1580156127e357600080fd5b505afa1580156127f7573d6000803e3d6000fd5b505050506040513d602081101561280d57600080fd5b5051905060005b84518110156128b75761282c85828151811061227857fe5b612835576128af565b61283d614254565b61285d3387848151811061284d57fe5b6020026020010151876001613b9d565b90506000612871888885815181106122c457fe5b9050806009600089868151811061288457fe5b6020026020010151815260200190815260200160002060008282540192505081905550808501945050505b600101612814565b506128c28583613a1e565b504760135560016002556128d4612ed7565b47146040518060400160405280600e81526020016d6f7574206f662062616c616e636560901b815250906129495760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b509392505050565b6000546001600160a01b031633146129a2576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b600054604080516001600160a01b039283168152918316602083015280517f434a2db650703b36c824e745330d6397cdaa9ee2cc891a4938ae853e1c50b68d9281900390910190a1600080546001600160a01b039092166001600160a01b0319928316179055600180549091169055565b6000908152600860209081526040808320546009909252909120549091565b6014546040805180820190915260118152706674736f206d616e61676572206f6e6c7960781b6020820152906001600160a01b03163314612ab45760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b508060045414612b03576040805162461bcd60e51b81526020600482015260156024820152741ddc9bdb99c81c995dd85c9908195c1bd8da081a59605a1b604482015290519081900360640190fd5b600081815260096020908152604080832054600890925290912054600d5491900390612b2f9082612e76565b600d556040805183815290517f5d05c64f281304391697cf987812e1a736413a062a9bdf39af4102209eb6fa589181900360200190a150600101600455565b60155460408051808201909152600e81526d696e666c6174696f6e206f6e6c7960901b6020820152906001600160a01b03163314612bed5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b506012819055600e54612c009082612e76565b600e55426011556040805182815290517f187f32a0f765499f15b3bb52ed0aebf6015059f230f2ace7e701e60a476695959181900360200190a150565b606080600080601460009054906101000a90046001600160a01b03166001600160a01b031663e7c830d46040518163ffffffff1660e01b815260040160206040518083038186803b158015612c9157600080fd5b505afa158015612ca5573d6000803e3d6000fd5b505050506040513d6020811015612cbb57600080fd5b50519050612cc98682612efb565b91508180612cd657508086145b15612cfc576000612cea8888886000613b9d565b90508060400151945080606001519350505b5093509350939050565b6015546001600160a01b031690565b60048054601454604080516339f20c3560e21b81529051929360009384936001600160a01b03169263e7c830d49281810192602092909190829003018186803b158015612d6157600080fd5b505afa158015612d75573d6000803e3d6000fd5b505050506040513d6020811015612d8b57600080fd5b505160408051808201909152601f81527f6e6f2065706f6368207769746820636c61696d61626c65207265776172647300602082015290915081612e105760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50600181039150509091565b600080612e3434601354612e7690919063ffffffff16565b905047915080821115612e6857612e6081612e5a84601054612e7690919063ffffffff16565b90613f46565b601055612e72565b80821015612e7257fe5b9091565b600082820183811015612ed0576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6000612ef6600c54612e5a601054600f54612e7690919063ffffffff16565b905090565b6000600454831080612f0d5750818310155b15612f1a575060006126a4565b50600192915050565b612f2b614254565b60145460408051637976d5ad60e11b81526004810186905290516000926001600160a01b03169163f2edab5a916024808301926020929190829003018186803b158015612f7757600080fd5b505afa158015612f8b573d6000803e3d6000fd5b505050506040513d6020811015612fa157600080fd5b505190506000612fb2858780613fa3565b90506000612fbe61427c565b8215612fdf5785612fda57612fd487898a613fd6565b60208201525b612ffd565b612fea888886614009565b9150612ff787898461417f565b60208201525b828061300d575060008160200151115b151581526016546040805163ed475a7960e01b81526001600160a01b038b811660048301526024820188905291516060938493169163ed475a79916044808301926000929190829003018186803b15801561306757600080fd5b505afa15801561307b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260808110156130a457600080fd5b8101908080516040519392919084600160201b8211156130c357600080fd5b9083019060208201858111156130d857600080fd5b82518660208202830111600160201b821117156130f457600080fd5b82525081516020918201928201910280838360005b83811015613121578181015183820152602001613109565b5050505090500160405260200180516040519392919084600160201b82111561314957600080fd5b90830190602082018581111561315e57600080fd5b82518660208202830111600160201b8211171561317a57600080fd5b82525081516020918201928201910280838360005b838110156131a757818101518382015260200161318f565b5050505091909101604052505083518751949650929450919291506131cf90505760006131d2565b60015b60ff16016001600160401b03811180156131eb57600080fd5b50604051908082528060200260200182016040528015613215578160200160208202803683370190505b50808852516001600160401b038111801561322f57600080fd5b50604051908082528060200260200182016040528015613259578160200160208202803683370190505b5060208801528651516001600160401b038111801561327757600080fd5b506040519080825280602002602001820160405280156132a1578160200160208202803683370190505b5060408801528651516001600160401b03811180156132bf57600080fd5b506040519080825280602002602001820160405280156132e9578160200160208202803683370190505b50606088015282511561338d5789876000015160008151811061330857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505084876060015160008151811061333a57fe5b60200260200101901515908115158152505083876020015160008151811061335e57fe5b6020026020010181815250508260200151876040015160008151811061338057fe5b6020026020010181815250505b8151156135e9576016546040805163277166bf60e11b81526001600160a01b038d81166004830152602482018a905291516000939290921691634ee2cd7e91604480820192602092909190829003018186803b1580156133ec57600080fd5b505afa158015613400573d6000803e3d6000fd5b505050506040513d602081101561341657600080fd5b5051905060005b83518110156135e657600081866000015161343957600061343c565b60015b60ff1601905084828151811061344e57fe5b60200260200101518a60000151828151811061346657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250506134a48c86848151811061349657fe5b60200260200101518f613fa3565b8a6060015182815181106134b457fe5b602002602001019015159081151581525050896060015181815181106134d657fe5b602002602001015115613528578a613523576135068c8684815181106134f857fe5b60200260200101518f613fd6565b8a60400151828151811061351657fe5b6020026020010181815250505b6135dd565b61356f85838151811061353757fe5b602002602001015161356986858151811061354e57fe5b6020026020010151612710876137339092919063ffffffff16565b8e614211565b8a60200151828151811061357f57fe5b6020026020010181815250506135c08c86848151811061359b57fe5b60200260200101518c6020015184815181106135b357fe5b602002602001015161417f565b8a6040015182815181106135d057fe5b6020026020010181815250505b5060010161341d565b50505b5050505050509392505050565b6000806136116201517f601154612e7690919063ffffffff16565b905080841115604051806040016040528060118152602001706166746572206461696c79206379636c6560781b8152509061368d5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b5061369c83611d218387613f46565b600101949350505050565b6000612ef66136c3600d54600b54613f4690919063ffffffff16565b600e5490613f46565b6000808211613722576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161372b57fe5b049392505050565b600080821161377c576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b8361378957506000612ed0565b8383028385828161379657fe5b0414156137af578281816137a657fe5b04915050612ed0565b60008386816137ba57fe5b04905060008487816137c857fe5b06905060008587816137d657fe5b04905060008688816137e457fe5b06905061382c6137f888611d218685613839565b6138266138058686613839565b6138266138128987613839565b6138268d6138208c8b613839565b90613839565b90612e76565b9998505050505050505050565b600082613848575060006126a4565b8282028284828161385557fe5b0414612ed05760405162461bcd60e51b81526004018080602001828103825260218152602001806142946021913960400191505060405180910390fd5b600080805b835151811015613a1557836060015181815181106138b157fe5b6020026020010151156138c357613a0d565b6000846000015182815181106138d557fe5b602002602001015190506000856020015183815181106138f157fe5b60200260200101519050600081111561392e5760008781526005602090815260408083206001600160a01b03861684529091529020805482900390555b60008660400151848151811061394057fe5b6020026020010151905060008111156139895760008881526006602090815260408083206001600160a01b0387168452909152902080548290039055600c805482019055938401935b60008881526007602090815260408083206001600160a01b038088168086529184528285203380875290855294839020805460ff191660019081178255810187905583518e815294850187905283519095918f169491937f6ec685171a9028d19dc155a48e7824e3c68b03bc8995410e006abe3cbbeb3e2d928290030190a4505050505b600101613897565b50949350505050565b8015613aed576040516000906001600160a01b0384169083908381818185875af1925050503d8060008114613a6f576040519150601f19603f3d011682016040523d82523d6000602084013e613a74565b606091505b50509050806040518060400160405280600c81526020016b18db185a5b4819985a5b195960a21b81525090613aea5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315610efc578181015183820152602001610ee4565b50505b5050565b6001600160a01b0382166000908152600a6020526040812080545b8015613b7357815460001990910190829082908110613b2757fe5b6000918252602090912001546201000090046001600160f01b03168410613b6e57818181548110613b5457fe5b60009182526020909120015461ffff1692506126a4915050565b613b0c565b507f00000000000000000000000000000000000000000000000000000000000007d0949350505050565b613ba5614254565b60145460408051637976d5ad60e11b81526004810187905290516000926001600160a01b03169163f2edab5a916024808301926020929190829003018186803b158015613bf157600080fd5b505afa158015613c05573d6000803e3d6000fd5b505050506040513d6020811015613c1b57600080fd5b50518451858452909150806001600160401b0381118015613c3b57600080fd5b50604051908082528060200260200182016040528015613c65578160200160208202803683370190505b506020840152806001600160401b0381118015613c8157600080fd5b50604051908082528060200260200182016040528015613cab578160200160208202803683370190505b506040840152806001600160401b0381118015613cc757600080fd5b50604051908082528060200260200182016040528015613cf1578160200160208202803683370190505b50606084015260005b81811015613f3b57613d2087878381518110613d1257fe5b60200260200101518a613fa3565b84606001518281518110613d3057fe5b60200260200101901515908115158152505083606001518181518110613d5257fe5b602002602001015115613da45784613d9f57613d8287878381518110613d7457fe5b60200260200101518a613fd6565b84604001518281518110613d9257fe5b6020026020010181815250505b613f33565b876001600160a01b0316868281518110613dba57fe5b60200260200101516001600160a01b03161415613dfd57613ddc888885614009565b84602001518281518110613dec57fe5b602002602001018181525050613eee565b60165486516000916001600160a01b03169063e64767aa908b908a9086908110613e2357fe5b6020026020010151876040518463ffffffff1660e01b815260040180846001600160a01b03168152602001836001600160a01b03168152602001828152602001935050505060206040518083038186803b158015613e8057600080fd5b505afa158015613e94573d6000803e3d6000fd5b505050506040513d6020811015613eaa57600080fd5b50518751909150613ed090889084908110613ec157fe5b6020026020010151828a614211565b85602001518381518110613ee057fe5b602002602001018181525050505b613f1687878381518110613efe57fe5b6020026020010151866020015184815181106135b357fe5b84604001518281518110613f2657fe5b6020026020010181815250505b600101613cfa565b505050949350505050565b600082821115613f9d576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60009283526007602090815260408085206001600160a01b03948516865282528085209290931684525290205460ff1690565b60009283526007602090815260408085206001600160a01b03948516865282528085209290931684525290206001015490565b60165460408051634181ad4160e11b81526001600160a01b038681166004830152602482018590529151600093849316916383035a82916044808301926020929190829003018186803b15801561405f57600080fd5b505afa158015614073573d6000803e3d6000fd5b505050506040513d602081101561408957600080fd5b505160165460408051631257fcdb60e31b81526001600160a01b03898116600483015260248201889052915193945060009391909216916392bfe6d8916044808301926020929190829003018186803b1580156140e557600080fd5b505afa1580156140f9573d6000803e3d6000fd5b505050506040513d602081101561410f57600080fd5b50519050818114156141305761412781612710613839565b92505050612ed0565b81811161413957fe5b6000821561414f5761414d83612710613839565b015b600061415b8888613af1565b905080156141745761416f84840382613839565b820191505b509695505050505050565b60008161418e57506000612ed0565b60008481526006602090815260408083206001600160a01b0387168452909152902054806141c0576000915050612ed0565b60008581526005602090815260408083206001600160a01b0388168452909152902054838114156141f357509050612ed0565b8084106141fc57fe5b614207828583613733565b9695505050505050565b60008261422057506000612ed0565b60008061422d8685613af1565b9050612710811015613a155761424885612710839003613839565b90910195945050505050565b6040518060800160405280606081526020016060815260200160608152602001606081525090565b60408051808201909152600080825260208201529056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f776665652070657263656e746167652063616e206e6f742062652075706461746564a2646970667358221220c77332c03f54170b5ac7d2477cae90b33dc94b91c253bacad5a3af23c10b8cb764736f6c63430007060033