false
false
0

Contract Address Details

0xAB7C7da1Ff2b25D0867908ddFa52827570e02894

Contract Name
FtsoManager
Creator
0x4598a6–d10d29 at 0xde01de–4b99a1
Balance
0 FLR
Tokens
Fetching tokens...
Transactions
31 Transactions
Transfers
0 Transfers
Gas Used
7,387,681
Last Balance Update
23064860
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
FtsoManager




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




Optimization runs
200
Verified at
2022-07-13T21:18:39.119580Z

Constructor Arguments

0000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d290000000000000000000000001000000000000000000000000000000000000002000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b000000000000000000000000100000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062cf1e4600000000000000000000000000000000000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000062d9a2300000000000000000000000000000000000000000000000000000000000049d400000000000000000000000000000000000000000000000000000000000000002

Arg [0] (address) : 0x4598a6c05910ab914f0cbaaca1911cd337d10d29
Arg [1] (address) : 0x1000000000000000000000000000000000000002
Arg [2] (address) : 0xbaf89d873d198ff78e72d2745b01cba3c6e5be6b
Arg [3] (address) : 0x1000000000000000000000000000000000000003
Arg [4] (address) : 0x0000000000000000000000000000000000000000
Arg [5] (uint256) : 1657740870
Arg [6] (uint256) : 180
Arg [7] (uint256) : 90
Arg [8] (uint256) : 1658430000
Arg [9] (uint256) : 302400
Arg [10] (uint256) : 2

              

contracts/ftso/implementation/FtsoManager.sol

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

import "../interface/IIFtsoManager.sol";
import "../interface/IIFtsoManagerV1.sol";
import "../interface/IIFtso.sol";
import "../lib/FtsoManagerSettings.sol";
import "../lib/FtsoManagement.sol";
import "../../genesis/implementation/FlareDaemon.sol";
import "../../genesis/interface/IIPriceSubmitter.sol";
import "../../governance/implementation/Governed.sol";
import "../../inflation/interface/IISupply.sol";
import "../../tokenPools/interface/IIFtsoRewardManager.sol";
import "../../token/implementation/CleanupBlockNumberManager.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../../utils/implementation/GovernedAndFlareDaemonized.sol";
import "../../utils/implementation/RevertErrorTracking.sol";
import "../../utils/interface/IIFtsoRegistry.sol";
import "../../utils/interface/IIVoterWhitelister.sol";
import "../../utils/interface/IUpdateValidators.sol";


/**
 * FtsoManager is in charge of:
 * - defining reward epochs (few days)
 * - per reward epoch choose a single block that represents vote power of this epoch.
 * - keep track of all FTSO contracts
 * - per price epoch (few minutes)
 *    - randomly choose one FTSO for rewarding.
 *    - trigger finalize price reveal epoch
 *    - determines addresses and reward weights and triggers rewardDistribution
 */    
//solhint-disable-next-line max-states-count
contract FtsoManager is IIFtsoManager, GovernedAndFlareDaemonized, AddressUpdatable, RevertErrorTracking {
    using FtsoManagerSettings for FtsoManagerSettings.State;
    using FtsoManagement for FtsoManagement.State;

    uint256 public constant MAX_TRUSTED_ADDRESSES_LENGTH = 5;

    string internal constant ERR_FIRST_EPOCH_START_TS_IN_FUTURE = "First epoch start ts in future";
    string internal constant ERR_REWARD_EPOCH_DURATION_ZERO = "Reward epoch 0";
    string internal constant ERR_REWARD_EPOCH_START_TOO_SOON = "Reward epoch start too soon";
    string internal constant ERR_REWARD_EPOCH_NOT_INITIALIZED = "Reward epoch not initialized yet";
    string internal constant ERR_REWARD_EPOCH_START_CONDITION_INVALID = "Reward epoch start condition invalid";
    string internal constant ERR_REWARD_EPOCH_DURATION_CONDITION_INVALID = "Reward epoch duration condition invalid";
    string internal constant ERR_PRICE_EPOCH_DURATION_ZERO = "Price epoch 0";
    string internal constant ERR_VOTE_POWER_INTERVAL_FRACTION_ZERO = "Vote power interval fraction 0";
    string internal constant ERR_REVEAL_PRICE_EPOCH_DURATION_ZERO = "Reveal price epoch 0";
    string internal constant ERR_REVEAL_PRICE_EPOCH_TOO_LONG = "Reveal price epoch too long";
    string internal constant ERR_GOV_PARAMS_NOT_INIT_FOR_FTSOS = "Gov. params not initialized";
    string internal constant ERR_GOV_PARAMS_INVALID = "Gov. params invalid";
    string internal constant ERR_NOT_FOUND = "Not found";
    string internal constant ERR_ALREADY_ADDED = "Already added";
    string internal constant ERR_ALREADY_ACTIVATED = "Already activated";
    string internal constant ERR_CLOSING_EXPIRED_REWARD_EPOCH_FAIL = "err close expired";
    string internal constant ERR_SET_CLEANUP_BLOCK_FAIL = "err set cleanup block";
    string internal constant ERR_PRICE_EPOCH_FINALIZE_FAIL = "err finalize price epoch";
    string internal constant ERR_DISTRIBUTE_REWARD_FAIL = "err distribute rewards";
    string internal constant ERR_ACCRUE_UNEARNED_REWARD_FAIL = "err accrue unearned rewards";
    string internal constant ERR_FALLBACK_FINALIZE_FAIL = "err fallback finalize price epoch";
    string internal constant ERR_INIT_EPOCH_REVEAL_FAIL = "err init epoch for reveal";
    string internal constant ERR_FALLBACK_INIT_EPOCH_REVEAL_FAIL = "err fallback init epoch for reveal";
    string internal constant ERR_UPDATE_REWARD_EPOCH_SWITCHOVER_CALL = "err calling updateActiveValidators";


    bool public override active;
    mapping(uint256 => RewardEpochData) internal rewardEpochsMapping;
    address public lastRewardedFtsoAddress;
    uint256 internal rewardEpochsLength;
    uint256 public override currentRewardEpochEnds;

    FtsoManagerSettings.State public settings;
    FtsoManagement.State public ftsoManagement;

    // price epoch data
    uint256 internal immutable firstPriceEpochStartTs;
    uint256 internal immutable priceEpochDurationSeconds;
    uint256 internal immutable revealEpochDurationSeconds;
    uint256 internal lastUnprocessedPriceEpoch;
    uint256 internal lastUnprocessedPriceEpochRevealEnds;
    // indicates if lastUnprocessedPriceEpoch is initialized for reveal
    // it has to be finalized before new reward epoch can start
    bool internal lastUnprocessedPriceEpochInitialized;

    // reward Epoch data
    uint256 public immutable override rewardEpochsStartTs;
    uint256 public override rewardEpochDurationSeconds;
    uint256 internal votePowerIntervalFraction;
    uint256 internal nextRewardEpochToExpire;

    IIPriceSubmitter public immutable priceSubmitter;
    IIFtsoRewardManager public rewardManager;
    IISupply public supply;
    CleanupBlockNumberManager public cleanupBlockNumberManager;
    IUpdateValidators public updateOnRewardEpochSwitchover;

    // fallback mode
    bool internal fallbackMode; // all ftsos in fallback mode

    // for redeploy
    IIFtsoManagerV1 public immutable oldFtsoManager;

    constructor(
        address _governance,
        FlareDaemon _flareDaemon,
        address _addressUpdater,
        IIPriceSubmitter _priceSubmitter,
        IIFtsoManagerV1 _oldFtsoManager,
        uint256 _firstPriceEpochStartTs,
        uint256 _priceEpochDurationSeconds,
        uint256 _revealEpochDurationSeconds,
        uint256 _firstRewardEpochStartTs,
        uint256 _rewardEpochDurationSeconds,
        uint256 _votePowerIntervalFraction
    ) 
        GovernedAndFlareDaemonized(_governance, _flareDaemon) AddressUpdatable(_addressUpdater)
    {
        require(block.timestamp >= _firstPriceEpochStartTs, ERR_FIRST_EPOCH_START_TS_IN_FUTURE);
        require(_rewardEpochDurationSeconds > 0, ERR_REWARD_EPOCH_DURATION_ZERO);
        require(_priceEpochDurationSeconds > 0, ERR_PRICE_EPOCH_DURATION_ZERO);
        require(_revealEpochDurationSeconds > 0, ERR_REVEAL_PRICE_EPOCH_DURATION_ZERO);
        require(_votePowerIntervalFraction > 0, ERR_VOTE_POWER_INTERVAL_FRACTION_ZERO);

        require(_revealEpochDurationSeconds < _priceEpochDurationSeconds, ERR_REVEAL_PRICE_EPOCH_TOO_LONG);
        require(_firstPriceEpochStartTs + _revealEpochDurationSeconds <= _firstRewardEpochStartTs, 
            ERR_REWARD_EPOCH_START_TOO_SOON);
        require((_firstRewardEpochStartTs - _revealEpochDurationSeconds - _firstPriceEpochStartTs) %
            _priceEpochDurationSeconds == 0, ERR_REWARD_EPOCH_START_CONDITION_INVALID);
        require(_rewardEpochDurationSeconds % _priceEpochDurationSeconds == 0,
            ERR_REWARD_EPOCH_DURATION_CONDITION_INVALID);

        // reward epoch
        rewardEpochsStartTs = _firstRewardEpochStartTs;
        rewardEpochDurationSeconds = _rewardEpochDurationSeconds;
        votePowerIntervalFraction = _votePowerIntervalFraction;

        // price epoch
        firstPriceEpochStartTs = _firstPriceEpochStartTs;
        priceEpochDurationSeconds = _priceEpochDurationSeconds;
        revealEpochDurationSeconds = _revealEpochDurationSeconds;
        lastUnprocessedPriceEpochRevealEnds = _firstRewardEpochStartTs;
        lastUnprocessedPriceEpoch = (_firstRewardEpochStartTs - _firstPriceEpochStartTs) / _priceEpochDurationSeconds;

        priceSubmitter = _priceSubmitter;
        oldFtsoManager = _oldFtsoManager;
    }

    /**
     * @notice Set reward data to values from old ftso manager
     * @dev Can be called only before activation
     */
    function setInitialRewardData(
        uint256 _nextRewardEpochToExpire,
        uint256 _rewardEpochsLength,
        uint256 _currentRewardEpochEnds
    ) 
        external override
        onlyGovernance
    {
        require(!active, ERR_ALREADY_ACTIVATED);
        nextRewardEpochToExpire = _nextRewardEpochToExpire;
        rewardEpochsLength = _rewardEpochsLength;
        currentRewardEpochEnds = _currentRewardEpochEnds;
    }
    
    /**
     * @notice Activates FTSO manager (daemonize() runs jobs)
     */
    function activate() external override onlyGovernance {
        active = true;
    }

    /**
     * @notice Runs task triggered by Daemon.
     * The tasks include the following by priority
     * - finalizePriceEpoch     
     * - Set governance parameters and initialize epochs
     * - finalizeRewardEpoch 
     */
    function daemonize() external override onlyFlareDaemon returns (bool) {
        // flare daemon trigger. once every block
        if (!active) return false;

        if (rewardEpochsLength == 0) {
            _initializeFirstRewardEpoch();
        } else {
            // all three conditions can be executed in the same block,
            // but are split into three `if else if` groups to reduce gas usage per one block
            if (lastUnprocessedPriceEpochInitialized && lastUnprocessedPriceEpochRevealEnds <= block.timestamp) {
                // finalizes initialized price epoch if reveal period is over
                // sets lastUnprocessedPriceEpochInitialized = false
                _finalizePriceEpoch();
            } else if (!lastUnprocessedPriceEpochInitialized && currentRewardEpochEnds <= block.timestamp) {
                // initialized price epoch must be finalized before new reward epoch can start
                // advance currentRewardEpochEnds
                _finalizeRewardEpoch();
                _closeExpiredRewardEpochs();
                _cleanupOnRewardEpochFinalization();
                _rewardEpochSwitchoverTrigger();
            } else if (lastUnprocessedPriceEpochRevealEnds <= block.timestamp) {
                // new price epoch can be initialized after previous was finalized 
                // and after new reward epoch was started (if needed)
                // initializes price epoch and sets governance parameters on ftsos and price submitter
                // advance lastUnprocessedPriceEpochRevealEnds, sets lastUnprocessedPriceEpochInitialized = true
                _initializeCurrentEpochFTSOStatesForReveal(); 
            }
        }
        return true;
    }

    /**
     * @notice Called if out of gas or any other unknown error occures in flare daemonize call
     */
    function switchToFallbackMode() external override onlyFlareDaemon returns (bool) {
        if (!fallbackMode) {
            fallbackMode = true;
            emit FallbackMode(true);
            return true;
        }
        return false;
    }

     /**
     * @notice Adds FTSO to the list of rewarded FTSOs
     * All ftsos in multi asset ftso must be managed by this ftso manager
     */
    function addFtso(IIFtso _ftso) external override onlyGovernance {
        _addFtso(_ftso, true);
    }

    /**
     * @notice Adds FTSO list to the list of rewarded FTSOs
     * All ftsos in multi asset ftso must be managed by this ftso manager
     */
    function addFtsosBulk(IIFtso[] memory _ftsos) external override onlyGovernance {
        for (uint256 i = 0; i < _ftsos.length; i++) {
            _addFtso(_ftsos[i], true);
        }
    }

    /**
     * @notice Removes FTSO from the list of the rewarded FTSOs - revert if ftso is used in multi asset ftso
     * @dev Deactivates _ftso
     */
    function removeFtso(IIFtso _ftso) external override onlyGovernance {
        ftsoManagement.removeFtso(_ftso);
    }
    
    /**
     * @notice Replaces one ftso with another
     * All ftsos in multi asset ftso must be managed by this ftso manager
     * @dev Deactivates old ftso
     */
    function replaceFtso(
        IIFtso _ftsoToAdd,
        bool _copyCurrentPrice,
        bool _copyAssetOrAssetFtsos
    )
        external override
        onlyGovernance
    {
        _replaceFtso(_ftsoToAdd, _copyCurrentPrice, _copyAssetOrAssetFtsos);
    }

    /**
     * @notice Bulk replaces one ftso with another
     * All ftsos in multi asset ftso must be managed by this ftso manager
     * @dev Deactivates old ftsos
     */
    function replaceFtsosBulk(
        IIFtso[] memory _ftsosToAdd,
        bool _copyCurrentPrice,
        bool _copyAssetOrAssetFtsos
    )
        external override
        onlyGovernance
    {
        for (uint256 i = 0; i < _ftsosToAdd.length; i++) {
            _replaceFtso(_ftsosToAdd[i], _copyCurrentPrice, _copyAssetOrAssetFtsos);
        }
    }

    /**
     * @notice Deactivates ftsos that are no longer used on ftso registry
     */
    function deactivateFtsos(IIFtso[] memory _ftsos) external onlyGovernance {
        ftsoManagement.deactivateFtsos(_ftsos);
    }
    
    /**
     * @notice Set asset for FTSO
     */
    function setFtsoAsset(IIFtso _ftso, IIVPToken _asset) external override onlyGovernance {
        _ftso.setAsset(_asset);
    }

    /**
     * @notice Set asset FTSOs for FTSO - all ftsos should already be managed by this ftso manager
     */
    function setFtsoAssetFtsos(IIFtso _ftso, IIFtso[] memory _assetFtsos) external override onlyGovernance {
        ftsoManagement.setFtsoAssetFtsos(_ftso, _assetFtsos);
    }

    /**
     * @notice Set fallback mode
     */
    function setFallbackMode(bool _fallbackMode) external override onlyImmediateGovernance {
        fallbackMode = _fallbackMode;
        emit FallbackMode(_fallbackMode);
    }

    /**
     * @notice Set fallback mode for ftso
     */
    function setFtsoFallbackMode(IIFtso _ftso, bool _fallbackMode) external override onlyImmediateGovernance {
        require(ftsoManagement.managedFtsos[_ftso], ERR_NOT_FOUND);
        ftsoManagement.ftsoInFallbackMode[_ftso] = _fallbackMode;
        emit FtsoFallbackMode(_ftso, _fallbackMode);
    }

    /**
     * @notice Sets governance parameters for FTSOs
     */
    function setGovernanceParameters(
        uint256 _maxVotePowerNatThresholdFraction,
        uint256 _maxVotePowerAssetThresholdFraction,
        uint256 _lowAssetUSDThreshold,
        uint256 _highAssetUSDThreshold,
        uint256 _highAssetTurnoutThresholdBIPS,
        uint256 _lowNatTurnoutThresholdBIPS,
        uint256 _rewardExpiryOffsetSeconds,
        address[] memory _trustedAddresses
    )
        external override onlyGovernance 
    {
        require(
            _maxVotePowerNatThresholdFraction > 0 &&
            _maxVotePowerAssetThresholdFraction > 0 &&
            _highAssetUSDThreshold >= _lowAssetUSDThreshold &&
            _highAssetTurnoutThresholdBIPS <= 1e4 &&
            _lowNatTurnoutThresholdBIPS <= 1e4 &&
            _rewardExpiryOffsetSeconds > 0 &&
            _trustedAddresses.length <= MAX_TRUSTED_ADDRESSES_LENGTH,
            ERR_GOV_PARAMS_INVALID
        );
        
        settings._setState(
            _maxVotePowerNatThresholdFraction,
            _maxVotePowerAssetThresholdFraction,
            _lowAssetUSDThreshold,
            _highAssetUSDThreshold,
            _highAssetTurnoutThresholdBIPS,
            _lowNatTurnoutThresholdBIPS,
            _rewardExpiryOffsetSeconds,
            _trustedAddresses
        );
    }

    function setRewardEpochDurationSeconds(uint256 _rewardEpochDurationSeconds) external onlyGovernance {
        require(_rewardEpochDurationSeconds > 0, ERR_REWARD_EPOCH_DURATION_ZERO);
        require(_rewardEpochDurationSeconds % priceEpochDurationSeconds == 0,
            ERR_REWARD_EPOCH_DURATION_CONDITION_INVALID);
        rewardEpochDurationSeconds = _rewardEpochDurationSeconds;
    }

    function setUpdateOnRewardEpochSwitchover(IUpdateValidators _updateValidators) external onlyGovernance {
        updateOnRewardEpochSwitchover = _updateValidators;
    }

    function setVotePowerIntervalFraction(uint256 _votePowerIntervalFraction) external onlyGovernance {
        require(_votePowerIntervalFraction > 0, ERR_VOTE_POWER_INTERVAL_FRACTION_ZERO);
        votePowerIntervalFraction = _votePowerIntervalFraction;
    }

    function getVotePowerIntervalFraction() external view returns (uint256) {
        return votePowerIntervalFraction;
    }

    function getPriceSubmitter() external view returns (IIPriceSubmitter) {
        return priceSubmitter;
    }

    function getCurrentPriceEpochId() external view override returns (uint256 _priceEpochId) {
        return _getCurrentPriceEpochId();
    }

    /**
     * @dev half-closed intervals - end time not included
     */
    function getCurrentPriceEpochData() external view override 
        returns (
            uint256 _priceEpochId,
            uint256 _priceEpochStartTimestamp,
            uint256 _priceEpochEndTimestamp,
            uint256 _priceEpochRevealEndTimestamp,
            uint256 _currentTimestamp
        )
    {
        uint256 epochId = _getCurrentPriceEpochId();
        return (
            epochId,
            firstPriceEpochStartTs + epochId * priceEpochDurationSeconds,
            firstPriceEpochStartTs + (epochId + 1) * priceEpochDurationSeconds,
            firstPriceEpochStartTs + (epochId + 1) * priceEpochDurationSeconds + revealEpochDurationSeconds,
            block.timestamp
        );
    }
    
    /**
     * @notice Gets vote power block of the specified reward epoch
     * @param _rewardEpoch          Reward epoch sequence number
     */
    function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view override 
        returns (
            uint256 _votepowerBlock
        )
    {
        return getRewardEpochData(_rewardEpoch).votepowerBlock;
    }

    /**
     * @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 Returns the list of FTSOs
     */
    function getFtsos() external view override returns (IIFtso[] memory _ftsos) {
        return _getFtsos();
    }

    function getPriceEpochConfiguration() external view override 
        returns (
            uint256 _firstPriceEpochStartTs,
            uint256 _priceEpochDurationSeconds,
            uint256 _revealEpochDurationSeconds
        )
    {
        return (firstPriceEpochStartTs, priceEpochDurationSeconds, revealEpochDurationSeconds);
    }

    function getRewardEpochConfiguration() external view override
        returns (
            uint256 _firstRewardEpochStartTs,
            uint256 _rewardEpochDurationSeconds
        )
    {
        return (rewardEpochsStartTs, rewardEpochDurationSeconds);
    }

    function getFallbackMode() external view override
        returns (
            bool _fallbackMode,
            IIFtso[] memory _ftsos,
            bool[] memory _ftsoInFallbackMode
        )
    {
        _fallbackMode = fallbackMode;
        _ftsos = _getFtsos();
        uint256 len = _ftsos.length;
        _ftsoInFallbackMode = new bool[](len);
        
        for (uint256 i = 0; i < len; i++) {
            _ftsoInFallbackMode[i] = ftsoManagement.ftsoInFallbackMode[_ftsos[i]];
        }
    }

    /**
     * @notice Gets governance parameters for FTSOs
     */
    function getGovernanceParameters() external view 
        returns (
            uint256 _maxVotePowerNatThresholdFraction,
            uint256 _maxVotePowerAssetThresholdFraction,
            uint256 _lowAssetUSDThreshold,
            uint256 _highAssetUSDThreshold,
            uint256 _highAssetTurnoutThresholdBIPS,
            uint256 _lowNatTurnoutThresholdBIPS,
            uint256 _rewardExpiryOffsetSeconds,
            address[] memory _trustedAddresses,
            bool _initialized,
            bool _changed
        )
    {
        return (
            settings.maxVotePowerNatThresholdFraction,
            settings.maxVotePowerAssetThresholdFraction,
            settings.lowAssetUSDThreshold,
            settings.highAssetUSDThreshold,
            settings.highAssetTurnoutThresholdBIPS,
            settings.lowNatTurnoutThresholdBIPS,
            settings.rewardExpiryOffsetSeconds,
            settings.trustedAddresses,
            settings.initialized,
            settings.changed
        );
    }

    function getLastUnprocessedPriceEpochData() external view override
        returns (
            uint256 _lastUnprocessedPriceEpoch,
            uint256 _lastUnprocessedPriceEpochRevealEnds,
            bool _lastUnprocessedPriceEpochInitialized
        )
    {
        return (lastUnprocessedPriceEpoch, lastUnprocessedPriceEpochRevealEnds, lastUnprocessedPriceEpochInitialized);
    }
            
    /**
     * @notice Returns current reward epoch index (one currently running)
     */
    function getCurrentRewardEpoch() external view override returns (uint256) {
        require(rewardEpochsLength != 0, ERR_REWARD_EPOCH_NOT_INITIALIZED);
        return _getCurrentRewardEpochId();
    }

    function rewardEpochs(uint256 _rewardEpochId) external view override
        returns (
            uint256 _votepowerBlock,
            uint256 _startBlock,
            uint256 _startTimestamp
        )
    {
        RewardEpochData memory rewardEpochData = getRewardEpochData(_rewardEpochId);
        _votepowerBlock = rewardEpochData.votepowerBlock;
        _startBlock = rewardEpochData.startBlock;
        _startTimestamp = rewardEpochData.startTimestamp;
    }
    
    function notInitializedFtsos(IIFtso _ftso) external view override returns (bool) {
        return ftsoManagement.notInitializedFtsos[_ftso];
    }

    function ftsoRegistry() external view returns (IIFtsoRegistry) {
        return ftsoManagement.ftsoRegistry;
    }

    function voterWhitelister() external view returns (IIVoterWhitelister) {
        return ftsoManagement.voterWhitelister;
    }
    
    /**
     * @notice Implement this function for updating daemonized contracts through AddressUpdater.
     */
    function getContractName() external pure override returns (string memory) {
        return "FtsoManager";
    }
    
    /**
     * @notice Returns reward epoch data
     * @param _rewardEpochId        Reward epoch id
     */
    function getRewardEpochData(uint256 _rewardEpochId) public view override returns (RewardEpochData memory) {
        require(_rewardEpochId < rewardEpochsLength, ERR_REWARD_EPOCH_NOT_INITIALIZED);
        return _getRewardEpoch(_rewardEpochId);
    }

    function _rewardEpochSwitchoverTrigger() internal {
        if (address(updateOnRewardEpochSwitchover) != address(0)) {
            uint256 currentRewardEpoch = _getCurrentRewardEpochId();
            try updateOnRewardEpochSwitchover.updateActiveValidators() {
            } catch Error(string memory message) {
                emit UpdatingActiveValidatorsTriggerFailed(currentRewardEpoch);
                addRevertError(address(updateOnRewardEpochSwitchover), message);
            } catch {
                emit UpdatingActiveValidatorsTriggerFailed(currentRewardEpoch);
                addRevertError(address(updateOnRewardEpochSwitchover), ERR_UPDATE_REWARD_EPOCH_SWITCHOVER_CALL);
            }
        }
    }

    function _addFtso(IIFtso _ftso, bool _addNewFtso) internal {
        ftsoManagement.addFtso(settings, _ftso, _addNewFtso, lastUnprocessedPriceEpochInitialized);
        _initialActivateFtso(_ftso);
    }
    
    function _replaceFtso(IIFtso _ftsoToAdd, bool _copyCurrentPrice, bool _copyAssetOrAssetFtsos) internal {
        ftsoManagement.replaceFtso(settings, _ftsoToAdd, _copyCurrentPrice, _copyAssetOrAssetFtsos, 
            lastUnprocessedPriceEpochInitialized);
        _initialActivateFtso(_ftsoToAdd);
    }
    
    function _initialActivateFtso(IIFtso _ftso) internal {
        _ftso.activateFtso(firstPriceEpochStartTs, priceEpochDurationSeconds, revealEpochDurationSeconds);

        // Set the vote power block
        if (rewardEpochsLength != 0) {
            _ftso.setVotePowerBlock(_getRewardEpoch(_getCurrentRewardEpochId()).votepowerBlock);
        }
    }

    /**
     * @notice Initializes first reward epoch. Also sets vote power block to FTSOs
     */
    function _initializeFirstRewardEpoch() internal {

        if (block.timestamp >= rewardEpochsStartTs) {
            IIFtso[] memory ftsos = _getFtsos();
            uint256 numFtsos = ftsos.length;
            // Prime the reward epoch array with a new reward epoch
            RewardEpochData memory epochData = RewardEpochData({
                votepowerBlock: block.number - 1,
                startBlock: block.number,
                startTimestamp: block.timestamp
            });

            rewardEpochsMapping[rewardEpochsLength] = epochData;
            rewardEpochsLength++;

            for (uint256 i = 0; i < numFtsos; ++i) {
                ftsos[i].setVotePowerBlock(epochData.votepowerBlock);
            }
            
            currentRewardEpochEnds = rewardEpochsStartTs + rewardEpochDurationSeconds;
        }
    }

    /**
     * @notice Finalizes reward epoch
     */
    function _finalizeRewardEpoch() internal {
        IIFtso[] memory ftsos = _getFtsos();
        uint256 numFtsos = ftsos.length;

        uint256 lastRandom = block.timestamp;
        lastRandom += priceSubmitter.getCurrentRandom();

        lastRandom = uint256(keccak256(abi.encode(lastRandom)));
        // @dev when considering block boundary for vote power block:
        // - if far from now, it doesn't reflect last vote power changes
        // - if too small, possible loan attacks.     
        // IMPORTANT: currentRewardEpoch is actually the one just getting finalized!
        uint256 votepowerBlockBoundary = 
            (block.number - _getRewardEpoch(_getCurrentRewardEpochId()).startBlock) / votePowerIntervalFraction;
        // note: votePowerIntervalFraction > 0
 
        if (votepowerBlockBoundary == 0) {
            votepowerBlockBoundary = 1;
        }
 
        //slither-disable-next-line weak-prng           // lastRandom calculated from ftso inputs
        uint256 votepowerBlocksAgo = lastRandom % votepowerBlockBoundary;
        // prevent block.number becoming votePowerBlock
        // if  lastRandom % votepowerBlockBoundary == 0  
        if (votepowerBlocksAgo == 0) {
            votepowerBlocksAgo = 1;
        }
        
        RewardEpochData memory epochData = RewardEpochData({
            votepowerBlock: block.number - votepowerBlocksAgo, 
            startBlock: block.number,
            startTimestamp: block.timestamp
        });
        rewardEpochsMapping[rewardEpochsLength] = epochData;
        rewardEpochsLength++;
        for (uint256 i = 0; i < numFtsos; i++) {
            ftsos[i].setVotePowerBlock(epochData.votepowerBlock);
        }

        emit RewardEpochFinalized(epochData.votepowerBlock, epochData.startBlock);

        // Advance reward epoch end-time
        currentRewardEpochEnds += rewardEpochDurationSeconds;
    }

    /**
     * @notice Closes expired reward epochs
     */
    function _closeExpiredRewardEpochs() internal {
        uint256 currentRewardEpoch = _getCurrentRewardEpochId();
        uint256 expiryThreshold = block.timestamp - settings.rewardExpiryOffsetSeconds;
        // NOTE: start time of (i+1)th reward epoch is the end time of i-th  
        // This loop is clearly bounded by the value currentRewardEpoch, which is
        // always kept to the value of rewardEpochs.length - 1 in code and this value
        // does not change in the loop.
        while (
            nextRewardEpochToExpire < currentRewardEpoch && 
            _getRewardEpoch(nextRewardEpochToExpire + 1).startTimestamp <= expiryThreshold) 
        {   // Note: Since nextRewardEpochToExpire + 1 starts at that time
            // nextRewardEpochToExpire ends strictly before expiryThreshold, 
            try rewardManager.closeExpiredRewardEpoch(nextRewardEpochToExpire) {
                nextRewardEpochToExpire++;
            } catch Error(string memory message) {
                // closing of expired failed, which is not critical
                // just emit event for diagnostics
                emit ClosingExpiredRewardEpochFailed(nextRewardEpochToExpire);
                addRevertError(address(rewardManager), message);
                // Do not proceed with the loop.
                break;
            } catch {
                emit ClosingExpiredRewardEpochFailed(nextRewardEpochToExpire);
                addRevertError(address(rewardManager), ERR_CLOSING_EXPIRED_REWARD_EPOCH_FAIL);
                // Do not proceed with the loop.
                break;
            }
        }
    }

    /**
     * @notice Performs any cleanup needed immediately after a reward epoch is finalized
     */
    function _cleanupOnRewardEpochFinalization() internal {

        uint256 cleanupBlock = _getRewardEpoch(nextRewardEpochToExpire).votepowerBlock;
        
        try cleanupBlockNumberManager.setCleanUpBlockNumber(cleanupBlock) {
        } catch Error(string memory message) {
            // cleanup block number manager call failed, which is not critical
            // just emit event for diagnostics
            emit CleanupBlockNumberManagerFailedForBlock(cleanupBlock);
            addRevertError(address(cleanupBlockNumberManager), message);
        } catch {
            emit CleanupBlockNumberManagerFailedForBlock(cleanupBlock);
            addRevertError(address(cleanupBlockNumberManager), ERR_SET_CLEANUP_BLOCK_FAIL);
        }
    }

    function _finalizePriceEpochFailed(IIFtso ftso, string memory message) internal {
        emit FinalizingPriceEpochFailed(
            ftso, 
            lastUnprocessedPriceEpoch, 
            IFtso.PriceFinalizationType.WEIGHTED_MEDIAN
        );

        addRevertError(address(ftso), message);

        _fallbackFinalizePriceEpoch(ftso);
    }

    /**
     * @notice Finalizes price epoch
     */
    function _finalizePriceEpoch() internal {
        IIFtso[] memory ftsos = _getFtsos();
        uint256 numFtsos = ftsos.length;

        // Are there any FTSOs to process?
        if (numFtsos > 0 && !fallbackMode) {
            // choose winning ftso
            uint256 chosenFtsoId;
            if (lastRewardedFtsoAddress == address(0)) {
                // pump not yet primed
                //slither-disable-next-line weak-prng           // only used for first epoch
                chosenFtsoId = uint256(keccak256(abi.encode(
                        block.difficulty, block.timestamp
                    ))) % numFtsos;
            } else {
                // at least one finalize with real FTSO
                uint256 currentRandom = priceSubmitter.getCurrentRandom();
                //slither-disable-next-line weak-prng           // random calculated safely from inputs
                chosenFtsoId = uint256(keccak256(abi.encode(
                        currentRandom, block.timestamp
                    ))) % numFtsos;
            }
            address[] memory addresses;
            uint256[] memory weights;
            uint256 totalWeight;

            // On the off chance that the winning FTSO does not have any
            // recipient within the truncated price distribution to
            // receive rewards, find the next FTSO that does have reward
            // recipients and declare it the winner. Start with the next ftso.
            bool wasDistributed = false;
            address rewardedFtsoAddress = address(0);
            for (uint256 i = 0; i < numFtsos; i++) {
                //slither-disable-next-line weak-prng           // not a random, just choosing next
                uint256 id = (chosenFtsoId + i) % numFtsos;
                IIFtso ftso = ftsos[id];

                // skip finalizing ftso, as it is not initialized for reveal and tx would revert
                if (ftsoManagement.notInitializedFtsos[ftso]) {
                    delete ftsoManagement.notInitializedFtsos[ftso];
                    continue;
                }

                try ftso.finalizePriceEpoch(lastUnprocessedPriceEpoch, !wasDistributed) returns (
                    address[] memory _addresses,
                    uint256[] memory _weights,
                    uint256 _totalWeight
                ) {
                    if (!wasDistributed && _addresses.length > 0) { // change also in FTSO if condition changes
                        (addresses, weights, totalWeight) = (_addresses, _weights, _totalWeight);
                        wasDistributed = true;
                        rewardedFtsoAddress = address(ftso);
                    }
                } catch Error(string memory message) {
                    _finalizePriceEpochFailed(ftso, message);
                } catch {
                    _finalizePriceEpochFailed(ftso, ERR_PRICE_EPOCH_FINALIZE_FAIL);
                }
            }

            uint256 currentRewardEpoch = _getCurrentRewardEpochId();

            if (wasDistributed) {
                try rewardManager.distributeRewards(
                    addresses,
                    weights,
                    totalWeight,
                    lastUnprocessedPriceEpoch,
                    rewardedFtsoAddress,
                    priceEpochDurationSeconds,
                    currentRewardEpoch,
                    _getPriceEpochEndTime(lastUnprocessedPriceEpoch) - 1, // actual end time (included)
                    _getRewardEpoch(currentRewardEpoch).votepowerBlock) {
                } catch Error(string memory message) {
                    emit DistributingRewardsFailed(rewardedFtsoAddress, lastUnprocessedPriceEpoch);
                    addRevertError(address(rewardManager), message);
                } catch {
                    emit DistributingRewardsFailed(rewardedFtsoAddress, lastUnprocessedPriceEpoch);
                    addRevertError(address(rewardManager), ERR_DISTRIBUTE_REWARD_FAIL);
                }
            } else {
                // If here, it means that no FTSO was initialized, or no FTSO had a recipient
                // eligible to receive rewards. And if so, burn rewards for this price epoch.
                _accrueUnearnedRewards();
            }

            lastRewardedFtsoAddress = rewardedFtsoAddress;
            emit PriceEpochFinalized(rewardedFtsoAddress, currentRewardEpoch);
        } else {
            // only for fallback mode
            for (uint256 i = 0; i < numFtsos; i++) {
                IIFtso ftso = ftsos[i];
                // skip finalizing ftso, as it is not initialized for reveal and tx would revert
                if (ftsoManagement.notInitializedFtsos[ftso]) {
                    delete ftsoManagement.notInitializedFtsos[ftso];
                    continue;
                }
                _fallbackFinalizePriceEpoch(ftso);
            }

            // Because FTSO manager in fallback, burn rewards for this price epoch.
            _accrueUnearnedRewards();

            lastRewardedFtsoAddress = address(0);
            emit PriceEpochFinalized(address(0), _getCurrentRewardEpochId());
        }
        
        lastUnprocessedPriceEpochInitialized = false;
    }

    function _accrueUnearnedRewards() internal {
        try rewardManager.accrueUnearnedRewards(
            lastUnprocessedPriceEpoch,
            priceEpochDurationSeconds,
            _getPriceEpochEndTime(lastUnprocessedPriceEpoch) - 1) { // actual end time (included)
        } catch Error(string memory message) {
            emit AccruingUnearnedRewardsFailed(lastUnprocessedPriceEpoch);
            addRevertError(address(rewardManager), message);
        } catch {
            emit AccruingUnearnedRewardsFailed(lastUnprocessedPriceEpoch);
            addRevertError(address(rewardManager), ERR_ACCRUE_UNEARNED_REWARD_FAIL);
        }
    }

    function _fallbackFinalizePriceEpochFailed(IIFtso _ftso, string memory message) internal {
        emit FinalizingPriceEpochFailed(
            _ftso, 
            lastUnprocessedPriceEpoch, 
            IFtso.PriceFinalizationType.TRUSTED_ADDRESSES
        );
        addRevertError(address(_ftso), message);

        // if reverts we want to propagate up to daemon
        _ftso.forceFinalizePriceEpoch(lastUnprocessedPriceEpoch);
    }

    function _fallbackFinalizePriceEpoch(IIFtso _ftso) internal {
        try _ftso.fallbackFinalizePriceEpoch(lastUnprocessedPriceEpoch) {
        } catch Error(string memory message) {
            _fallbackFinalizePriceEpochFailed(_ftso, message);
        } catch {
            _fallbackFinalizePriceEpochFailed(_ftso, ERR_FALLBACK_FINALIZE_FAIL);
        }
    }

    /**
     * @notice Initializes epoch states in FTSOs for reveal. 
     * Prior to initialization it sets governance parameters, if 
     * governance has changed them. It also sets price submitter trusted addresses.
     */
    function _initializeCurrentEpochFTSOStatesForReveal() internal {
        if (settings.changed) {
            priceSubmitter.setTrustedAddresses(settings.trustedAddresses);
        }

        IIFtso[] memory ftsos = _getFtsos();
        uint256 numFtsos = ftsos.length;

        // circulating supply is used only when ftso is not in fallback mode
        uint256 circulatingSupplyNat;
        if (numFtsos > 0 && !fallbackMode) {
            uint256 votePowerBlock = _getRewardEpoch(_getCurrentRewardEpochId()).votepowerBlock;
            circulatingSupplyNat = supply.getCirculatingSupplyAtCached(votePowerBlock);
        }

        for (uint256 i = 0; i < numFtsos; i++) {
            IIFtso ftso = ftsos[i];
            if (settings.changed) {
                ftso.configureEpochs(
                    settings.maxVotePowerNatThresholdFraction,
                    settings.maxVotePowerAssetThresholdFraction,
                    settings.lowAssetUSDThreshold,
                    settings.highAssetUSDThreshold,
                    settings.highAssetTurnoutThresholdBIPS,
                    settings.lowNatTurnoutThresholdBIPS,
                    settings.trustedAddresses
                );
            }

            try ftso.initializeCurrentEpochStateForReveal(
                circulatingSupplyNat,
                fallbackMode || ftsoManagement.ftsoInFallbackMode[ftso]) {
            } catch Error(string memory message) {
                _initializeCurrentEpochStateForRevealFailed(ftso, message);
            } catch {
                _initializeCurrentEpochStateForRevealFailed(ftso, ERR_INIT_EPOCH_REVEAL_FAIL);
            }

        }
        settings.changed = false;

        // Advance price epoch id and end-time
        uint256 currentPriceEpochId = _getCurrentPriceEpochId();
        lastUnprocessedPriceEpoch = currentPriceEpochId;
        lastUnprocessedPriceEpochRevealEnds = _getPriceEpochRevealEndTime(currentPriceEpochId);

        lastUnprocessedPriceEpochInitialized = true;
    }

    function _initializeCurrentEpochStateForRevealFailed(IIFtso ftso, string memory message) internal {
        emit InitializingCurrentEpochStateForRevealFailed(ftso, _getCurrentPriceEpochId());
        addRevertError(address(ftso), message);

        // if it was already called with fallback = true, just mark as not initialized, else retry
        if (fallbackMode || ftsoManagement.ftsoInFallbackMode[ftso]) {
            ftsoManagement.notInitializedFtsos[ftso] = true;
        } else {
            try ftso.initializeCurrentEpochStateForReveal(0, true) {
            } catch Error(string memory message1) {
                ftsoManagement.notInitializedFtsos[ftso] = true;
                emit InitializingCurrentEpochStateForRevealFailed(ftso, _getCurrentPriceEpochId());
                addRevertError(address(ftso), message1);
            } catch {
                ftsoManagement.notInitializedFtsos[ftso] = true;
                emit InitializingCurrentEpochStateForRevealFailed(ftso, _getCurrentPriceEpochId());
                addRevertError(address(ftso), ERR_FALLBACK_INIT_EPOCH_REVEAL_FAIL);
            }
        }
    }

    /**
     * @notice Implementation of the AddressUpdatable abstract method.
     */
    function _updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    )
        internal override
    {
        rewardManager = IIFtsoRewardManager(
            _getContractAddress(_contractNameHashes, _contractAddresses, "FtsoRewardManager"));
        supply = IISupply(
            _getContractAddress(_contractNameHashes, _contractAddresses, "Supply"));
        cleanupBlockNumberManager = CleanupBlockNumberManager(
            _getContractAddress(_contractNameHashes, _contractAddresses, "CleanupBlockNumberManager"));
        ftsoManagement.ftsoRegistry = IIFtsoRegistry(
            _getContractAddress(_contractNameHashes, _contractAddresses, "FtsoRegistry"));
        ftsoManagement.voterWhitelister = IIVoterWhitelister(
            _getContractAddress(_contractNameHashes, _contractAddresses, "VoterWhitelister"));
    }

    /**
     * @notice Returns current reward epoch id without additional checks
     */
    function _getCurrentRewardEpochId() internal view returns (uint256) {
        return rewardEpochsLength - 1;
    }

    /**
     * Get reward epoch from current ftso manager or from old one if no data in current
     */
    function _getRewardEpoch(uint256 _rewardEpochId) internal view returns (RewardEpochData memory _rewardEpoch) {
        _rewardEpoch = rewardEpochsMapping[_rewardEpochId];
        if (_rewardEpoch.startTimestamp == 0) {
            (uint256 vpBlock, uint256 sBlock, uint256 sTimestamp) = oldFtsoManager.rewardEpochs(_rewardEpochId);
            _rewardEpoch = RewardEpochData({
                votepowerBlock: vpBlock, 
                startBlock: sBlock,
                startTimestamp: sTimestamp
            });
        }
    }

    /**
     * @notice Returns price epoch reveal end time.
     * @param _priceEpochId The price epoch id.
     * @dev half-closed interval - end time not included
     */
    function _getPriceEpochRevealEndTime(uint256 _priceEpochId) internal view returns (uint256) {
        return firstPriceEpochStartTs + (_priceEpochId + 1) * priceEpochDurationSeconds + revealEpochDurationSeconds;
    }

    /**
     * @notice Returns price epoch end time.
     * @param _forPriceEpochId The price epoch id of the end time to fetch.
     * @dev half-closed interval - end time not included
     */
    function _getPriceEpochEndTime(uint256 _forPriceEpochId) internal view returns (uint256) {
        return firstPriceEpochStartTs + ((_forPriceEpochId + 1) * priceEpochDurationSeconds);
    }

    /**
     * @notice Returns current price epoch id. The calculation in this function
     * should fully match to definition of current epoch id in FTSO contracts.
     */
    function _getCurrentPriceEpochId() internal view returns (uint256) {
        return (block.timestamp - firstPriceEpochStartTs) / priceEpochDurationSeconds;
    }

    function _getFtsos() internal view returns (IIFtso[] memory) {
        return ftsoManagement.ftsoRegistry.getSupportedFtsos();
    }
}
        

contracts/tokenPools/interface/IITokenPool.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

interface IITokenPool {

    /**
     * @notice Return token pool supply data
     * @return _lockedFundsWei                  Funds that are intentionally locked in the token pool 
     * and not part of circulating supply
     * @return _totalInflationAuthorizedWei     Total inflation authorized amount (wei)
     * @return _totalClaimedWei                 Total claimed amount (wei)
     */
    function getTokenPoolSupplyData() external returns (
        uint256 _lockedFundsWei,
        uint256 _totalInflationAuthorizedWei,
        uint256 _totalClaimedWei
    );
}
          

contracts/utils/interface/IIVoterWhitelister.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IVoterWhitelister.sol";


interface IIVoterWhitelister is IVoterWhitelister {
    /**
     * Set the maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
     * Possibly removes several voters with the least votepower from the whitelist.
     * Only governance can call this method.
     */
    function setMaxVotersForFtso(uint256 _ftsoIndex, uint256 _newMaxVoters) external;

    /**
     * Set the maximum number of voters in the whitelist for a new FTSO.
     * Only governance can call this method.
     */
    function setDefaultMaxVotersForFtso(uint256 _defaultMaxVotersForFtso) external;

    /**
     * Create whitelist with default size for ftso.
     * Only ftso manager can call this method.
     */
    function addFtso(uint256 _ftsoIndex) external;
    
    /**
     * Clear whitelist for ftso at `_ftsoIndex`.
     * Only ftso manager can call this method.
     */
    function removeFtso(uint256 _ftsoIndex) external;

    /**
     * Remove `_trustedAddress` from whitelist for ftso at `_ftsoIndex`.
     */
    function removeTrustedAddressFromWhitelist(address _trustedAddress, uint256 _ftsoIndex) external;
}
          

contracts/ftso/interface/IIFtsoManager.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
pragma abicoder v2;

import "../../ftso/interface/IIFtso.sol";
import "../../userInterfaces/IFtsoManager.sol";
import "../../genesis/interface/IFlareDaemonize.sol";
import "../../token/interface/IIVPToken.sol";


interface IIFtsoManager is IFtsoManager, IFlareDaemonize {

    struct RewardEpochData {
        uint256 votepowerBlock;
        uint256 startBlock;
        uint256 startTimestamp;
    }

    event ClosingExpiredRewardEpochFailed(uint256 rewardEpoch);
    event CleanupBlockNumberManagerFailedForBlock(uint256 blockNumber);
    event UpdatingActiveValidatorsTriggerFailed(uint256 rewardEpoch);
    event FtsoDeactivationFailed(IIFtso ftso);

    function activate() external;

    function setInitialRewardData(
        uint256 _nextRewardEpochToExpire,
        uint256 _rewardEpochsLength,
        uint256 _currentRewardEpochEnds
    ) 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 addFtsosBulk(IIFtso[] memory _ftsos) external;

    function removeFtso(IIFtso _ftso) external;

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

    function replaceFtsosBulk(
        IIFtso[] memory _ftsosToAdd,
        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;

    function notInitializedFtsos(IIFtso) external view returns (bool);

    function getRewardEpochData(uint256 _rewardEpochId) external view returns (RewardEpochData memory);

    function currentRewardEpochEnds() external view returns (uint256);

    function getLastUnprocessedPriceEpochData() external view
        returns(
            uint256 _lastUnprocessedPriceEpoch,
            uint256 _lastUnprocessedPriceEpochRevealEnds,
            bool _lastUnprocessedPriceEpochInitialized
        );

    function rewardEpochsStartTs() external view returns(uint256);

    function rewardEpochDurationSeconds() external view returns(uint256);

    function rewardEpochs(uint256 _rewardEpochId) external view 
        returns (
            uint256 _votepowerBlock,
            uint256 _startBlock,
            uint256 _startTimestamp
        );
}
          

contracts/governance/implementation/Governed.sol

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

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


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

contracts/genesis/interface/IIPriceSubmitter.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IPriceSubmitter.sol";
interface IIPriceSubmitter is IPriceSubmitter {

    /**
     * Set trusted addresses that are always allowed to submit and reveal.
     * Only ftso manager can call this method.
     */
    function setTrustedAddresses(address[] memory _trustedAddresses) external;

    /**
     * Called from whitelister when new voter has been whitelisted.
     */
    function voterWhitelisted(address _voter, uint256 _ftsoIndex) external;
    
    /**
     * Called from whitelister when one or more voters have been removed.
     */
    function votersRemovedFromWhitelist(address[] memory _voters, uint256 _ftsoIndex) external;

    /**
     * Returns a list of trusted addresses that are always allowed to submit and reveal.
     */
    function getTrustedAddresses() external view returns (address[] memory);
}
          

contracts/token/interface/IIGovernanceVotePower.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IVPToken.sol";
import "../../userInterfaces/IGovernanceVotePower.sol";

interface IIGovernanceVotePower is IGovernanceVotePower {
    /**
     * Event triggered when an delegator's balance changes.
     *
     * Note: the event is always emitted from `GovernanceVotePower`.
     */
    event DelegateVotesChanged(
    address indexed delegate, 
    uint256 previousBalance, 
    uint256 newBalance
    );

    /**
     * Event triggered when an account delegates to another account.
     *
     * Note: the event is always emitted from `GovernanceVotePower`.
     */
    event DelegateChanged(
    address indexed delegator, 
    address indexed fromDelegate, 
    address indexed toDelegate
    );

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

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

contracts/inflation/interface/IISupply.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


interface IISupply {

    /**
     * @notice Updates circulating supply
     * @dev Also updates the burn address amount
    */
    function updateCirculatingSupply() external;

    /**
     * @notice Updates authorized inflation and circulating supply - emits event if error
     * @param _inflationAuthorizedWei               Authorized inflation
     * @dev Also updates the burn address amount
    */
    function updateAuthorizedInflationAndCirculatingSupply(uint256 _inflationAuthorizedWei) external;

    /**
     * @notice Get approximate circulating supply for given block number from cache - only past block
     * @param _blockNumber                          Block number
     * @return _circulatingSupplyWei Return approximate circulating supply for last known block <= _blockNumber
    */
    function getCirculatingSupplyAtCached(uint256 _blockNumber) external returns(uint256 _circulatingSupplyWei);

    /**
     * @notice Get approximate circulating supply for given block number
     * @param _blockNumber                          Block number
     * @return _circulatingSupplyWei Return approximate circulating supply for last known block <= _blockNumber
    */
    function getCirculatingSupplyAt(uint256 _blockNumber) external view returns(uint256 _circulatingSupplyWei);

    /**
     * @notice Get total inflatable balance (initial genesis amount + total authorized inflation)
     * @return _inflatableBalanceWei Return inflatable balance
    */
    function getInflatableBalance() external view returns(uint256 _inflatableBalanceWei);
}
          

contracts/userInterfaces/IVPContractEvents.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

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

contracts/utils/implementation/SafePct.sol

// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";

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

contracts/inflation/interface/IIInflationReceiver.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

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);
    
    /**
     * Implement this function for updating inflation receiver contracts through AddressUpdater.
     */
    function getContractName() external view returns (string memory);
}
          

contracts/genesis/implementation/FlareDaemon.sol

// SPDX-License-Identifier: MIT
// WARNING, WARNING, WARNING
// If you modify this contract, you need to re-install the binary into the validator 
// genesis file for the chain you wish to run. See ./docs/CompilingContracts.md for more information.
// You have been warned. That is all.
pragma solidity 0.7.6;
pragma abicoder v2;

import "../../governance/implementation/GovernedAtGenesis.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../interface/IInflationGenesis.sol";
import "../interface/IFlareDaemonize.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../utils/implementation/SafePct.sol";


/**
 * @title Flare Daemon contract
 * @notice This contract exists to coordinate regular daemon-like polling of contracts
 *   that are registered to receive said polling. The trigger method is called by the 
 *   validator right at the end of block state transition.
 */
contract FlareDaemon is GovernedAtGenesis, AddressUpdatable {
    using SafeMath for uint256;
    using SafePct for uint256;

    //====================================================================
    // Data Structures
    //====================================================================
    struct DaemonizedError {
        uint192 lastErrorBlock;
        uint64 numErrors;
        address fromContract;
        uint64 errorTypeIndex;
        string errorMessage;
    }

    struct LastErrorData {
        uint192 totalDaemonizedErrors;
        uint64 lastErrorTypeIndex;
    }

    struct Registration {
        IFlareDaemonize daemonizedContract;
        uint256 gasLimit;
    }

    string internal constant ERR_ALREADY_SET = "already set";
    string internal constant ERR_OUT_OF_BALANCE = "out of balance";
    string internal constant ERR_NOT_INFLATION = "not inflation";
    string internal constant ERR_TOO_MANY = "too many";
    string internal constant ERR_TOO_BIG = "too big";
    string internal constant ERR_TOO_OFTEN = "too often";
    string internal constant ERR_INFLATION_ZERO = "inflation zero";
    string internal constant ERR_BLOCK_NUMBER_SMALL = "block.number small";
    string internal constant INDEX_TOO_HIGH = "start index high";
    string internal constant UPDATE_GAP_TOO_SHORT = "time gap too short";
    string internal constant MAX_MINT_TOO_HIGH = "max mint too high";
    string internal constant MAX_MINT_IS_ZERO = "max mint is zero";
    string internal constant ERR_DUPLICATE_ADDRESS = "dup address";
    string internal constant ERR_ADDRESS_ZERO = "address zero";
    string internal constant ERR_OUT_OF_GAS = "out of gas";
    string internal constant ERR_INFLATION_MINT_RECEIVE_FAIL = "unknown error. receiveMinting";

    uint256 internal constant MAX_DAEMONIZE_CONTRACTS = 10;
    // Initial max mint request - 60 million native token
    uint256 internal constant MAX_MINTING_REQUEST_DEFAULT = 60000000 ether;
    // How often can inflation request minting from the validator - 23 hours constant
    uint256 internal constant MAX_MINTING_FREQUENCY_SEC = 23 hours;
    // How often can the maximal mint request amount be updated
    uint256 internal constant MAX_MINTING_REQUEST_FREQUENCY_SEC = 24 hours;
    // By how much can the maximum be increased (as a percentage of the previous maximum)
    uint256 internal constant MAX_MINTING_REQUEST_INCREASE_PERCENT = 110;
    // upper estimate of gas needed after error occurs in call to daemonizedContract.daemonize()
    uint256 internal constant MIN_GAS_LEFT_AFTER_DAEMONIZE = 300000;
    // lower estimate for gas needed for daemonize() call in trigger
    uint256 internal constant MIN_GAS_FOR_DAEMONIZE_CALL = 5000;

    IInflationGenesis public inflation;
    uint256 public systemLastTriggeredAt;
    uint256 public totalMintingRequestedWei;
    uint256 public totalMintingReceivedWei;
    uint256 public totalMintingWithdrawnWei;
    uint256 public totalSelfDestructReceivedWei;
    uint256 public maxMintingRequestWei;
    uint256 public lastMintRequestTs;
    uint256 public lastUpdateMaxMintRequestTs;
    LastErrorData public errorData;
    uint256 public blockHoldoff;

    uint256 private lastBalance;
    uint256 private expectedMintRequest;
    bool private initialized;

    // track deamonized contracts
    IFlareDaemonize[] internal daemonizeContracts;
    mapping (IFlareDaemonize => uint256) internal gasLimits;
    mapping (IFlareDaemonize => uint256) internal blockHoldoffsRemaining;

    // track daemonize errors
    mapping(bytes32 => DaemonizedError) internal daemonizedErrors;
    bytes32 [] internal daemonizeErrorHashes;

    event ContractDaemonized(address theContract, uint256 gasConsumed);
    event ContractDaemonizeErrored(address theContract, uint256 atBlock, string theMessage, uint256 gasConsumed);
    event ContractHeldOff(address theContract, uint256 blockHoldoffsRemaining);
    event ContractsSkippedOutOfGas(uint256 numberOfSkippedConstracts);
    event MintingRequestReceived(uint256 amountWei);
    event MintingRequestTriggered(uint256 amountWei);
    event MintingReceived(uint256 amountWei);
    event MintingWithdrawn(uint256 amountWei);
    event RegistrationUpdated(IFlareDaemonize theContract, bool add);
    event SelfDestructReceived(uint256 amountWei);
    event InflationSet(IInflationGenesis theNewContract, IInflationGenesis theOldContract);

    /**
     * @dev As there is not a constructor, this modifier exists to make sure the inflation
     *   contract is set for methods that require it.
     */
    modifier inflationSet {
        // Don't revert...just report.
        if (address(inflation) == address(0)) {
            addDaemonizeError(address(this), ERR_INFLATION_ZERO, 0);
        }
        _;
    }

    /**
     * @dev Access control to protect methods to allow only minters to call select methods
     *   (like transferring balance out).
     */
    modifier onlyInflation (address _inflation) {
        require (address(inflation) == _inflation, ERR_NOT_INFLATION);
        _;
    }
    
    /**
     * @dev Access control to protect trigger() method. 
     * Please note that the sender address is the same as deployed FlareDaemon address in this case.
     */
    modifier onlySystemTrigger {
        require (msg.sender == 0x1000000000000000000000000000000000000002);
        _;
    }

    //====================================================================
    // Constructor for pre-compiled code
    //====================================================================

    /**
     * @dev This constructor should contain no code as this contract is pre-loaded into the genesis block.
     *   The super constructor is called for testing convenience.
     */
    constructor() GovernedAtGenesis(address(0)) AddressUpdatable(address(0)) {
        /* empty block */
    }

    //====================================================================
    // Functions
    //====================================================================  

    /**
     * @notice Register contracts to be polled by the daemon process.
     * @param _registrations    An array of Registration structures of IFlareDaemonize contracts to daemonize
     *                          and gas limits for each contract.
     * @dev A gas limit of zero will set no limit for the contract but the validator has an overall
     *   limit for the trigger() method.
     * @dev If any registrations already exist, they will be unregistered.
     * @dev Contracts will be daemonized in the order in which presented via the _registrations array.
     */
    function registerToDaemonize(Registration[] memory _registrations) external onlyGovernance {
        _registerToDaemonize(_registrations);
    }

    /**
     * @notice Queue up a minting request to send to the validator at next trigger.
     * @param _amountWei    The amount to mint.
     */
    function requestMinting(uint256 _amountWei) external onlyInflation(msg.sender) {
        require(_amountWei <= maxMintingRequestWei, ERR_TOO_BIG);
        require(_getNextMintRequestAllowedTs() < block.timestamp, ERR_TOO_OFTEN);
        if (_amountWei > 0) {
            lastMintRequestTs = block.timestamp;
            totalMintingRequestedWei = totalMintingRequestedWei.add(_amountWei);
            emit MintingRequestReceived(_amountWei);
        }
    }

    /**
     * @notice Set number of blocks that must elapse before a daemonized contract exceeding gas limit can have
     *   its daemonize() method called again.
     * @param _blockHoldoff    The number of blocks to holdoff.
     */
    function setBlockHoldoff(uint256 _blockHoldoff) external onlyGovernance {
        blockHoldoff = _blockHoldoff;
    }

    /**
     * @notice Set limit on how much can be minted per request.
     * @param _maxMintingRequestWei    The request maximum in wei.
     * @notice this number can't be udated too often
     */
    function setMaxMintingRequest(uint256 _maxMintingRequestWei) external onlyGovernance {
        // make sure increase amount is reasonable
        require(
            _maxMintingRequestWei <= (maxMintingRequestWei.mulDiv(MAX_MINTING_REQUEST_INCREASE_PERCENT,100)),
            MAX_MINT_TOO_HIGH
        );
        require(_maxMintingRequestWei > 0, MAX_MINT_IS_ZERO);
        // make sure enough time since last update
        require(
            block.timestamp > lastUpdateMaxMintRequestTs + MAX_MINTING_REQUEST_FREQUENCY_SEC,
            UPDATE_GAP_TOO_SHORT
        );

        maxMintingRequestWei = _maxMintingRequestWei;
        lastUpdateMaxMintRequestTs = block.timestamp;
    }

    /**
     * @notice Sets the address udpater contract.
     * @param _addressUpdater   The address updater contract.
     */
    function setAddressUpdater(address _addressUpdater) external onlyGovernance {
        require(getAddressUpdater() == address(0), ERR_ALREADY_SET);
        setAddressUpdaterValue(_addressUpdater);
    }

    /**
     * @notice The meat of this contract. Poll all registered contracts, calling the daemonize() method of each,
     *   in the order in which registered.
     * @return  _toMintWei     Return the amount to mint back to the validator. The asked for balance will show
     *                          up in the next block (it is actually added right before this block's state transition,
     *                          but well after this method call will see it.)
     * @dev This method watches for balances being added to this contract and handles appropriately - legit
     *   mint requests as made via requestMinting, and also self-destruct sending to this contract, should
     *   it happen for some reason.
     */
    //slither-disable-next-line reentrancy-eth      // method protected by reentrancy guard (see comment below)
    function trigger() external virtual inflationSet onlySystemTrigger returns (uint256 _toMintWei) {
        return triggerInternal();
    }

    function getDaemonizedContractsData() external view 
        returns(
            IFlareDaemonize[] memory _daemonizeContracts,
            uint256[] memory _gasLimits,
            uint256[] memory _blockHoldoffsRemaining
        )
    {
        uint256 len = daemonizeContracts.length;
        _daemonizeContracts = new IFlareDaemonize[](len);
        _gasLimits = new uint256[](len);
        _blockHoldoffsRemaining = new uint256[](len);

        for (uint256 i; i < len; i++) {
            IFlareDaemonize daemonizeContract = daemonizeContracts[i];
            _daemonizeContracts[i] = daemonizeContract;
            _gasLimits[i] = gasLimits[daemonizeContract];
            _blockHoldoffsRemaining[i] = blockHoldoffsRemaining[daemonizeContract];
        }
    }

    function getNextMintRequestAllowedTs() external view returns(uint256) {
        return _getNextMintRequestAllowedTs();
    }

    function showLastDaemonizedError () external view 
        returns(
            uint256[] memory _lastErrorBlock,
            uint256[] memory _numErrors,
            string[] memory _errorString,
            address[] memory _erroringContract,
            uint256 _totalDaemonizedErrors
        )
    {
        return showDaemonizedErrors(errorData.lastErrorTypeIndex, 1);
    }

    /**
     * @notice Set the governance address to a hard-coded known address.
     * @dev This should be done at contract deployment time.
     * @return The governance address.
     */
    function initialiseFixedAddress() public override returns(address) {
        if (!initialized) {
            initialized = true;
            address governanceAddress = super.initialiseFixedAddress();
            return governanceAddress;
        } else {
            return governance();
        }
    }

    function showDaemonizedErrors (uint startIndex, uint numErrorTypesToShow) public view 
        returns(
            uint256[] memory _lastErrorBlock,
            uint256[] memory _numErrors,
            string[] memory _errorString,
            address[] memory _erroringContract,
            uint256 _totalDaemonizedErrors
        )
    {
        require(startIndex < daemonizeErrorHashes.length, INDEX_TOO_HIGH);
        uint256 numReportElements = 
            daemonizeErrorHashes.length >= startIndex + numErrorTypesToShow ?
            numErrorTypesToShow :
            daemonizeErrorHashes.length - startIndex;

        _lastErrorBlock = new uint256[] (numReportElements);
        _numErrors = new uint256[] (numReportElements);
        _errorString = new string[] (numReportElements);
        _erroringContract = new address[] (numReportElements);

        // we have error data error type.
        // error type is hash(error_string, source contract)
        // per error type we report how many times it happened.
        // what was last block it happened.
        // what is the error string.
        // what is the erroring contract
        for (uint i = 0; i < numReportElements; i++) {
            bytes32 hash = daemonizeErrorHashes[startIndex + i];

            _lastErrorBlock[i] = daemonizedErrors[hash].lastErrorBlock;
            _numErrors[i] = daemonizedErrors[hash].numErrors;
            _errorString[i] = daemonizedErrors[hash].errorMessage;
            _erroringContract[i] = daemonizedErrors[hash].fromContract;
        }
        _totalDaemonizedErrors = errorData.totalDaemonizedErrors;
    }

    /**
     * @notice Implementation of the AddressUpdatable abstract method - updates Inflation and daemonized contracts.
     * @dev It also sets `maxMintingRequestWei` if it was not set before.
     */
    function _updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    )
        internal override
    {
        IInflationGenesis _inflation = IInflationGenesis(
            _getContractAddress(_contractNameHashes, _contractAddresses, "Inflation"));
        emit InflationSet(_inflation, inflation);
        inflation = _inflation;
        if (maxMintingRequestWei == 0) {
            maxMintingRequestWei = MAX_MINTING_REQUEST_DEFAULT;
        }

        uint256 len = daemonizeContracts.length;
        if (len == 0) {
            return;
        }

        Registration[] memory registrations = new Registration[](len);
        for (uint256 i = 0; i < len; i++) {
            IFlareDaemonize daemonizeContract = daemonizeContracts[i];
            registrations[i].daemonizedContract = IFlareDaemonize(
                _getContractAddress(_contractNameHashes, _contractAddresses, daemonizeContract.getContractName()));
            registrations[i].gasLimit = gasLimits[daemonizeContract];
        }

        _registerToDaemonize(registrations);
    }

    /**
     * @notice Implementation of the trigger() method. The external wrapper has extra guard for msg.sender.
     */
    //slither-disable-next-line reentrancy-eth      // method protected by reentrancy guard (see comment below)
    function triggerInternal() internal returns (uint256 _toMintWei) {
        // only one trigger() call per block allowed
        // this also serves as reentrancy guard, since any re-entry will happen in the same block
        if(block.number == systemLastTriggeredAt) return 0;
        systemLastTriggeredAt = block.number;

        uint256 currentBalance = address(this).balance;

        // Did the validator or a self-destructor conjure some native token?
        if (currentBalance > lastBalance) {
            uint256 balanceExpected = lastBalance.add(expectedMintRequest);
            // Did we get what was last asked for?
            if (currentBalance == balanceExpected) {
                // Yes, so assume it all came from the validator.
                uint256 minted = expectedMintRequest;
                totalMintingReceivedWei = totalMintingReceivedWei.add(minted);
                emit MintingReceived(minted);
                //slither-disable-next-line arbitrary-send          // only sent to inflation, set by governance
                try inflation.receiveMinting{ value: minted }() {
                    totalMintingWithdrawnWei = totalMintingWithdrawnWei.add(minted);
                    emit MintingWithdrawn(minted);
                } catch Error(string memory message) {
                    addDaemonizeError(address(this), message, 0);
                } catch {
                    addDaemonizeError(address(this), ERR_INFLATION_MINT_RECEIVE_FAIL, 0);
                }
            } else if (currentBalance < balanceExpected) {
                // No, and if less, there are two possibilities: 1) the validator did not
                // send us what we asked (not possible unless a bug), or 2) an attacker
                // sent us something in between a request and a mint. Assume 2.
                uint256 selfDestructReceived = currentBalance.sub(lastBalance);
                totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(selfDestructReceived);
                emit SelfDestructReceived(selfDestructReceived);
            } else {
                // No, so assume we got a minting request (perhaps zero...does not matter)
                // and some self-destruct proceeds (unlikely but can happen).
                totalMintingReceivedWei = totalMintingReceivedWei.add(expectedMintRequest);
                uint256 selfDestructReceived = currentBalance.sub(lastBalance).sub(expectedMintRequest);
                totalSelfDestructReceivedWei = totalSelfDestructReceivedWei.add(selfDestructReceived);
                emit MintingReceived(expectedMintRequest);
                emit SelfDestructReceived(selfDestructReceived);
                //slither-disable-next-line arbitrary-send          // only sent to inflation, set by governance
                try inflation.receiveMinting{ value: expectedMintRequest }() {
                    totalMintingWithdrawnWei = totalMintingWithdrawnWei.add(expectedMintRequest);
                    emit MintingWithdrawn(expectedMintRequest);
                } catch Error(string memory message) {
                    addDaemonizeError(address(this), message, 0);
                } catch {
                    addDaemonizeError(address(this), ERR_INFLATION_MINT_RECEIVE_FAIL, 0);
                }
            }
        }

        uint256 len = daemonizeContracts.length;

        // Perform trigger operations here
        for (uint256 i = 0; i < len; i++) {
            IFlareDaemonize daemonizedContract = daemonizeContracts[i];
            uint256 blockHoldoffRemainingForContract = blockHoldoffsRemaining[daemonizedContract];
            if (blockHoldoffRemainingForContract > 0) {
                blockHoldoffsRemaining[daemonizedContract] = blockHoldoffRemainingForContract - 1;
                emit ContractHeldOff(address(daemonizedContract), blockHoldoffRemainingForContract);
            } else {
                // Figure out what gas to limit call by
                uint256 gasLimit = gasLimits[daemonizedContract];
                uint256 startGas = gasleft();
                // End loop if there isn't enough gas left for any daemonize call
                if (startGas < MIN_GAS_LEFT_AFTER_DAEMONIZE + MIN_GAS_FOR_DAEMONIZE_CALL) {
                    emit ContractsSkippedOutOfGas(len - i);
                    break;
                }
                // Calculate the gas limit for the next call
                uint256 useGas = startGas - MIN_GAS_LEFT_AFTER_DAEMONIZE;
                if (gasLimit > 0 && gasLimit < useGas) {
                    useGas = gasLimit;
                }
                // Run daemonize for the contract, consume errors, and record
                try daemonizedContract.daemonize{gas: useGas}() {
                    emit ContractDaemonized(address(daemonizedContract), (startGas - gasleft()));
                // Catch all requires with messages
                } catch Error(string memory message) {
                    addDaemonizeError(address(daemonizedContract), message, (startGas - gasleft()));
                    daemonizedContract.switchToFallbackMode();
                // Catch everything else...out of gas, div by zero, asserts, etc.
                } catch {
                    uint256 endGas = gasleft();
                    // Interpret out of gas errors
                    if (gasLimit > 0 && startGas.sub(endGas) >= gasLimit) {
                        addDaemonizeError(address(daemonizedContract), ERR_OUT_OF_GAS, (startGas - endGas));
                        // When daemonize() fails with out-of-gas, try to fix it in two steps:
                        // 1) try to switch contract to fallback mode
                        //    (to allow the contract's daemonize() to recover in fallback mode in next block)
                        // 2) if constract is already in fallback mode or fallback mode is not supported
                        //    (switchToFallbackMode() returns false), start the holdoff for this contract
                        bool switchedToFallback = daemonizedContract.switchToFallbackMode();
                        if (!switchedToFallback) {
                            blockHoldoffsRemaining[daemonizedContract] = blockHoldoff;
                        }
                    } else {
                        // Don't know error cause...just log it as unknown
                        addDaemonizeError(address(daemonizedContract), "unknown", (startGas - endGas));
                        daemonizedContract.switchToFallbackMode();
                    }
                }
            }
        }

        // Get any requested minting and return to validator
        _toMintWei = getPendingMintRequest();
        if (_toMintWei > 0) {
            expectedMintRequest = _toMintWei;
            emit MintingRequestTriggered(_toMintWei);
        } else {
            expectedMintRequest = 0;            
        }

        // Update balance
        lastBalance = address(this).balance;
        
        // We should be in balance - don't revert, just report...
        uint256 contractBalanceExpected = getExpectedBalance();
        if (contractBalanceExpected != address(this).balance) {
            addDaemonizeError(address(this), ERR_OUT_OF_BALANCE, 0);
        }
    }

    function addDaemonizeError(address daemonizedContract, string memory message, uint256 gasConsumed) internal {
        bytes32 errorStringHash = keccak256(abi.encode(daemonizedContract, message));

        DaemonizedError storage daemonizedError = daemonizedErrors[errorStringHash];
        if (daemonizedError.numErrors == 0) {
            // first time we recieve this error string.
            daemonizeErrorHashes.push(errorStringHash);
            daemonizedError.fromContract = daemonizedContract;
            // limit message length to fit in fixed number of storage words (to make gas usage predictable)
            daemonizedError.errorMessage = truncateString(message, 64);
            daemonizedError.errorTypeIndex = uint64(daemonizeErrorHashes.length - 1);
        }
        daemonizedError.numErrors += 1;
        daemonizedError.lastErrorBlock = uint192(block.number);
        emit ContractDaemonizeErrored(daemonizedContract, block.number, message, gasConsumed);

        errorData.totalDaemonizedErrors += 1;
        errorData.lastErrorTypeIndex = daemonizedError.errorTypeIndex;        
    }

    /**
     * @notice Register contracts to be polled by the daemon process.
     * @param _registrations    An array of Registration structures of IFlareDaemonize contracts to daemonize
     *                          and gas limits for each contract.
     * @dev A gas limit of zero will set no limit for the contract but the validator has an overall
     *   limit for the trigger() method.
     * @dev If any registrations already exist, they will be unregistered.
     * @dev Contracts will be daemonized in the order in which presented via the _registrations array.
     */
    function _registerToDaemonize(Registration[] memory _registrations) internal {
        // Make sure there are not too many contracts to register.
        uint256 registrationsLength = _registrations.length;
        require(registrationsLength <= MAX_DAEMONIZE_CONTRACTS, ERR_TOO_MANY);

        // Unregister everything first
        _unregisterAll();

        // Loop over all contracts to register
        for (uint256 registrationIndex = 0; registrationIndex < registrationsLength; registrationIndex++) {
            // Address cannot be zero
            require(address(_registrations[registrationIndex].daemonizedContract) != address(0), ERR_ADDRESS_ZERO);

            uint256 daemonizeContractsLength = daemonizeContracts.length;
            // Make sure no dups...yes, inefficient. Registration should not be done often.
            for (uint256 i = 0; i < daemonizeContractsLength; i++) {
                require(_registrations[registrationIndex].daemonizedContract != daemonizeContracts[i], 
                    ERR_DUPLICATE_ADDRESS); // already registered
            }
            // Store off the registered contract to daemonize, in the order presented.
            daemonizeContracts.push(_registrations[registrationIndex].daemonizedContract);
            // Record the gas limit for the contract.
            gasLimits[_registrations[registrationIndex].daemonizedContract] = 
                _registrations[registrationIndex].gasLimit;
            // Clear any blocks being held off for the given contract, if any. Contracts may be re-presented
            // if only order is being modified, for example.
            blockHoldoffsRemaining[_registrations[registrationIndex].daemonizedContract] = 0;
            emit RegistrationUpdated (_registrations[registrationIndex].daemonizedContract, true);
        }
    }

    /**
     * @notice Unregister all contracts from being polled by the daemon process.
     */
    function _unregisterAll() private {

        uint256 len = daemonizeContracts.length;

        for (uint256 i = 0; i < len; i++) {
            IFlareDaemonize daemonizedContract = daemonizeContracts[daemonizeContracts.length - 1];
            daemonizeContracts.pop();
            emit RegistrationUpdated (daemonizedContract, false);
        }
    }

    /**
     * @notice Net totals to obtain the expected balance of the contract.
     */
    function getExpectedBalance() private view returns(uint256 _balanceExpectedWei) {
        _balanceExpectedWei = totalMintingReceivedWei.
            sub(totalMintingWithdrawnWei).
            add(totalSelfDestructReceivedWei);
    }

    /**
     * @notice Net total received from total requested.
     */
    function getPendingMintRequest() private view returns(uint256 _mintRequestPendingWei) {
        _mintRequestPendingWei = totalMintingRequestedWei.sub(totalMintingReceivedWei);
    }


    function _getNextMintRequestAllowedTs() internal view returns (uint256) {
        return (lastMintRequestTs + MAX_MINTING_FREQUENCY_SEC);
    }

    function truncateString(string memory _str, uint256 _maxlength) private pure returns (string memory) {
        bytes memory strbytes = bytes(_str);
        if (strbytes.length <= _maxlength) {
            return _str;
        }
        bytes memory result = new bytes(_maxlength);
        for (uint256 i = 0; i < _maxlength; i++) {
            result[i] = strbytes[i];
        }
        return string(result);
    }
}
          

contracts/ftso/lib/FtsoManagement.sol

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

import "../interface/IIFtsoManager.sol";
import "../interface/IIFtsoManagerV1.sol";
import "../interface/IIFtso.sol";
import "../lib/FtsoManagerSettings.sol";
import "../../genesis/implementation/FlareDaemon.sol";
import "../../genesis/interface/IIPriceSubmitter.sol";
import "../../governance/implementation/Governed.sol";
import "../../inflation/interface/IISupply.sol";
import "../../tokenPools/interface/IIFtsoRewardManager.sol";
import "../../token/implementation/CleanupBlockNumberManager.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../../utils/implementation/GovernedAndFlareDaemonized.sol";
import "../../utils/implementation/RevertErrorTracking.sol";
import "../../utils/interface/IIFtsoRegistry.sol";
import "../../utils/interface/IIVoterWhitelister.sol";
import "../../utils/interface/IUpdateValidators.sol";

library FtsoManagement {
    using FtsoManagerSettings for FtsoManagerSettings.State;
    
    struct State {
        mapping(IIFtso => bool) managedFtsos;
        mapping(IIFtso => bool) notInitializedFtsos;
        mapping(IIFtso => bool) ftsoInFallbackMode;
        
        IIFtsoRegistry ftsoRegistry;
        IIVoterWhitelister voterWhitelister;
    }
    
    string internal constant ERR_GOV_PARAMS_NOT_INIT_FOR_FTSOS = "Gov. params not initialized";
    string internal constant ERR_ASSET_FTSO_NOT_MANAGED = "Asset FTSO not managed";
    string internal constant ERR_ALREADY_ADDED = "Already added";
    string internal constant ERR_FTSO_ASSET_FTSO_ZERO = "Asset ftsos list empty";
    string internal constant ERR_FTSO_EQUALS_ASSET_FTSO = "ftso equals asset ftso";
    
    // libraries cannot emit event from interfaces, so we have to copy events here
    event FtsoAdded(IIFtso ftso, bool add);     // copied from IFtsoManager.sol
    event FtsoDeactivationFailed(IIFtso ftso);  // copied from IIFtsoManager.sol
    
    function addFtso(
        State storage _state, 
        FtsoManagerSettings.State storage _settings, 
        IIFtso _ftso, 
        bool _addNewFtso,
        bool _lastUnprocessedPriceEpochInitialized
    ) 
        public
    {
        require(_settings.initialized, ERR_GOV_PARAMS_NOT_INIT_FOR_FTSOS);

        _checkAssetFtsosAreManaged(_state, _ftso.getAssetFtsos());

        if (_addNewFtso) {
            // Check if symbol already exists in registry
            bytes32 symbol = keccak256(abi.encode(_ftso.symbol()));
            string[] memory supportedSymbols = _state.ftsoRegistry.getSupportedSymbols();
            uint256 len = supportedSymbols.length;
            while (len > 0) {
                --len;
                if (keccak256(abi.encode(supportedSymbols[len])) == symbol) {
                    revert(ERR_ALREADY_ADDED);
                }
            }
        }

        // Configure 
        _ftso.configureEpochs(
            _settings.maxVotePowerNatThresholdFraction,
            _settings.maxVotePowerAssetThresholdFraction,
            _settings.lowAssetUSDThreshold,
            _settings.highAssetUSDThreshold,
            _settings.highAssetTurnoutThresholdBIPS,
            _settings.lowNatTurnoutThresholdBIPS,
            _settings.trustedAddresses
        );
        
        // skip first round of price finalization if price epoch was already initialized for reveal
        _state.notInitializedFtsos[_ftso] = _lastUnprocessedPriceEpochInitialized;
        _state.managedFtsos[_ftso] = true;
        uint256 ftsoIndex = _state.ftsoRegistry.addFtso(_ftso);

        // When a new ftso is added we also add it to the voter whitelister contract
        if (_addNewFtso) {
            _state.voterWhitelister.addFtso(ftsoIndex);
        }
        
        emit FtsoAdded(_ftso, true);
    }
    
    /**
     * @notice Replaces one ftso with another - symbols must match
     * All ftsos in multi asset ftso must be managed by this ftso manager
     * @dev Deactivates old ftso
     */
    function replaceFtso(
        State storage _state, 
        FtsoManagerSettings.State storage _settings, 
        IIFtso _ftsoToAdd,
        bool _copyCurrentPrice,
        bool _copyAssetOrAssetFtsos,
        bool _lastUnprocessedPriceEpochInitialized
    )
        public
    {
        IIFtso ftsoToRemove = _state.ftsoRegistry.getFtsoBySymbol(_ftsoToAdd.symbol());

        if (_copyCurrentPrice) {
            (uint256 currentPrice, uint256 timestamp) = ftsoToRemove.getCurrentPrice();
            _ftsoToAdd.updateInitialPrice(currentPrice, timestamp);
        }

        if (_copyAssetOrAssetFtsos) {
            IIVPToken asset = ftsoToRemove.getAsset();
            if (address(asset) != address(0)) { // copy asset if exists
                _ftsoToAdd.setAsset(asset);
            } else { // copy assetFtsos list if not empty
                IIFtso[] memory assetFtsos = ftsoToRemove.getAssetFtsos();
                if (assetFtsos.length > 0) {
                    _ftsoToAdd.setAssetFtsos(assetFtsos);
                }
            }
        }

        // Add without duplicate check
        addFtso(_state, _settings, _ftsoToAdd, false, _lastUnprocessedPriceEpochInitialized);
        
        // replace old contract with the new one in multi asset ftsos
        IIFtso[] memory contracts = _state.ftsoRegistry.getSupportedFtsos();

        uint256 ftsosLen = contracts.length;
        for (uint256 i = 0; i < ftsosLen; i++) {
            IIFtso ftso = contracts[i];
            if (ftso.ftsoManager() != address(this)) { // it cannot be updated and will be replaced
                continue;
            }
            IIFtso[] memory assetFtsos = ftso.getAssetFtsos();
            uint256 assetFtsosLen = assetFtsos.length;
            if (assetFtsosLen > 0) {
                bool changed = false;
                for (uint256 j = 0; j < assetFtsosLen; j++) {
                    if (assetFtsos[j] == ftsoToRemove) {
                        assetFtsos[j] = _ftsoToAdd;
                        changed = true;
                    }
                }
                if (changed) {
                    ftso.setAssetFtsos(assetFtsos);
                }
            }
        }

        // cleanup old contract
        cleanFtso(_state, ftsoToRemove);
    }
    
    function deactivateFtsos(
        State storage _state, 
        IIFtso[] memory _ftsos
    ) 
        public
    {
        uint256 len = _ftsos.length;
        while(len > 0) {
            len--;
            IIFtso ftso = _ftsos[len];
            try _state.ftsoRegistry.getFtsoBySymbol(ftso.symbol()) returns (IIFtso _ftso) {
                if (_ftso != ftso) {
                    // deactivate ftso if it was already replaced on ftso registry
                    ftso.deactivateFtso();
                    delete _state.ftsoInFallbackMode[ftso];
                    delete _state.notInitializedFtsos[ftso];
                    delete _state.managedFtsos[ftso];
                } else {
                    // ftso still in use on ftso registy - it could be removed using removeFtso call
                    emit FtsoDeactivationFailed(ftso);
                }
            } catch {
                // deactivate ftso if ftso symbol is not used anymore on ftso registry
                ftso.deactivateFtso();
                delete _state.ftsoInFallbackMode[ftso];
                delete _state.notInitializedFtsos[ftso];
                delete _state.managedFtsos[ftso];
            }
        }
    }
    
    /**
     * @notice Removes FTSO from the list of the rewarded FTSOs - revert if ftso is used in multi asset ftso
     * @dev Deactivates _ftso
     */
    function removeFtso(
        State storage _state, 
        IIFtso _ftso
    ) 
        public
    {
        uint256 ftsoIndex = _state.ftsoRegistry.getFtsoIndex(_ftso.symbol());
        _state.voterWhitelister.removeFtso(ftsoIndex);
        _state.ftsoRegistry.removeFtso(_ftso);
        cleanFtso(_state, _ftso);
    }

    function cleanFtso(
        State storage _state, 
        IIFtso _ftso
    ) 
        public
    {
        // Since this is as mapping, we can also just delete it, as false is default value for non-existing keys
        delete _state.ftsoInFallbackMode[_ftso];
        delete _state.notInitializedFtsos[_ftso];
        delete _state.managedFtsos[_ftso];

        // may fail if not managed by current ftso manager (can happen in redeploy)
        if (_ftso.ftsoManager() == address(this)) {
            _ftso.deactivateFtso();
            _checkMultiAssetFtsosAreManaged(_state, _state.ftsoRegistry.getSupportedFtsos());
        } else {
            // do nothing, old ftso not deactivated, but actually it is not a problem, just emit an event
            emit FtsoDeactivationFailed(_ftso);
        }
        emit FtsoAdded(_ftso, false);
    }


    /**
     * @notice Set asset FTSOs for FTSO - all ftsos should already be managed by this ftso manager
     */
    function setFtsoAssetFtsos(
        State storage _state, 
        IIFtso _ftso, 
        IIFtso[] memory _assetFtsos
    ) 
        public
    {
        uint256 len = _assetFtsos.length;
        require(len > 0, ERR_FTSO_ASSET_FTSO_ZERO);
        for (uint256 i = 0; i < len; i++) {
            if (_ftso == _assetFtsos[i]) {
                revert(ERR_FTSO_EQUALS_ASSET_FTSO);
            }
        }

        if (_state.managedFtsos[_ftso]) {
            _checkAssetFtsosAreManaged(_state, _assetFtsos);
        }
        _ftso.setAssetFtsos(_assetFtsos);
    }
    
    /**
     * @notice Check if asset ftsos are managed by this ftso manager, revert otherwise
     */
    function _checkAssetFtsosAreManaged(
        State storage _state, 
        IIFtso[] memory _assetFtsos
    ) internal view {
        uint256 len = _assetFtsos.length;
        for (uint256 i = 0; i < len; i++) {
            if (!_state.managedFtsos[_assetFtsos[i]]) {
                revert(ERR_ASSET_FTSO_NOT_MANAGED);
            }
        }
    }

    /**
     * @notice Check if all multi asset ftsos are managed by this ftso manager, revert otherwise
     */
    function _checkMultiAssetFtsosAreManaged(
        State storage _state, 
        IIFtso[] memory _ftsos
    ) internal view {
        uint256 len = _ftsos.length;
        for (uint256 i = 0; i < len; i++) {
            _checkAssetFtsosAreManaged(_state, _ftsos[i].getAssetFtsos());
        }
    }
}
          

contracts/token/implementation/CleanupBlockNumberManager.sol

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

import "../../governance/implementation/Governed.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../../token/interface/IICleanable.sol";


/**
 * @title Token history cleanup manager
 * @notice Maintains the list of cleanable tokens for which history cleanup can be collectively cleaned u 
 */
contract CleanupBlockNumberManager is Governed, AddressUpdatable {

    string internal constant ERR_CONTRACT_NOT_FOUND = "contract not found";
    string internal constant ERR_TRIGGER_CONTRACT_ONLY = "trigger contract only";

    IICleanable[] public registeredTokens;
    address public triggerContract;
    string public triggerContractName; // needed for updating trigger contract through AddressUpdater call

    event RegistrationUpdated (IICleanable theContract, bool add);
    event CleanupBlockNumberSet (IICleanable theContract, uint256 blockNumber, bool success);
        
    modifier onlyTrigger {
        require(msg.sender == triggerContract, ERR_TRIGGER_CONTRACT_ONLY);
        _;
    }

    constructor(
        address _governance,
        address _addressUpdater,
        string memory _triggerContractName
    )
        Governed(_governance) AddressUpdatable(_addressUpdater)
    {
        triggerContractName = _triggerContractName;
    }

    /**
     * @notice Register a contract of which history cleanup index is to be managed
     * @param _cleanableToken     The address of the contract to be managed.
     * @dev when using this function take care that call of setCleanupBlockNumber
     * is permitted by this object
     */
    function registerToken(IICleanable _cleanableToken) external onlyGovernance {
        uint256 len = registeredTokens.length;

        for (uint256 i = 0; i < len; i++) {
            if (_cleanableToken == registeredTokens[i]) {
                return; // already registered
            }
        }

        registeredTokens.push(_cleanableToken);
        emit RegistrationUpdated (_cleanableToken, true);
    }

    /**
     * @notice Unregiseter a contract from history cleanup index management
     * @param _cleanableToken     The address of the contract to unregister.
     */
    function unregisterToken(IICleanable _cleanableToken) external onlyGovernance {        
        uint256 len = registeredTokens.length;

        for (uint256 i = 0; i < len; i++) {
            if (_cleanableToken == registeredTokens[i]) {
                registeredTokens[i] = registeredTokens[len -1];
                registeredTokens.pop();
                emit RegistrationUpdated (_cleanableToken, false);
                return;
            }
        }

        revert(ERR_CONTRACT_NOT_FOUND);
    }

    /**
     * @notice Sets clean up block number on managed cleanable tokens
     * @param _blockNumber cleanup block number
     */
    function setCleanUpBlockNumber(uint256 _blockNumber) external onlyTrigger {
        uint256 len = registeredTokens.length;
        for (uint256 i = 0; i < len; i++) {
            try registeredTokens[i].setCleanupBlockNumber(_blockNumber) {
                emit CleanupBlockNumberSet(registeredTokens[i], _blockNumber, true);
            } catch {
                emit CleanupBlockNumberSet(registeredTokens[i], _blockNumber, false);
            }
        }
    }

    /**
     * @notice Implementation of the AddressUpdatable abstract method.
     */
    function _updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    )
        internal override
    {
        triggerContract = _getContractAddress(_contractNameHashes, _contractAddresses, triggerContractName);
    }
}
          

contracts/tokenPools/interface/IIFtsoRewardManager.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IFtsoRewardManager.sol";
import "../interface/IITokenPool.sol";
import "../../inflation/interface/IIInflationReceiver.sol";

interface IIFtsoRewardManager is IFtsoRewardManager, IIInflationReceiver, IITokenPool {

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

    function activate() external;
    function enableClaims() 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;

    function accrueUnearnedRewards(
        uint256 epochId,
        uint256 priceEpochDurationSeconds,
        uint256 priceEpochEndTime
    ) external;

    function firstClaimableRewardEpoch() external view returns (uint256);

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

contracts/userInterfaces/IPriceSubmitter.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../genesis/interface/IFtsoGenesis.sol";
import "../genesis/interface/IFtsoRegistryGenesis.sol";
import "../genesis/interface/IFtsoManagerGenesis.sol";

interface IPriceSubmitter {
    /**
     * Event emitted when hash was submitted through PriceSubmitter.
     * @param submitter the address of the sender
     * @param epochId current price epoch id
     * @param hash the submitted hash
     * @param timestamp current block timestamp
     */
    event HashSubmitted(
        address indexed submitter,
        uint256 indexed epochId,
        bytes32 hash,
        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 random,
        uint256 timestamp
    );
    
    /**
     * @notice Submits hash for current epoch
     * @param _epochId              Target epoch id to which hash is submitted
     * @param _hash                 Hash of ftso indices, prices, random number and voter address
     * @notice Emits HashSubmitted event
     */
    function submitHash(
        uint256 _epochId,
        bytes32 _hash
    ) 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 increasing ftso indices
     * @param _prices               List of submitted prices in USD
     * @param _random               Submitted random number
     * @notice The hash of ftso indices, prices, random number and voter address must be equal to the submitted hash
     * @notice Emits PricesRevealed event
     */
    function revealPrices(
        uint256 _epochId,
        uint256[] memory _ftsoIndices,
        uint256[] memory _prices,
        uint256 _random
    ) 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 (IFtsoManagerGenesis);

    /**
     * @notice Returns current random number
     */
    function getCurrentRandom() 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);
}
          

contracts/userInterfaces/IFtso.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

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

    event PriceRevealed(
        address indexed voter, uint256 indexed epochId, uint256 price, 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 _firstEpochStartTs           First epoch start timestamp
     * @return _submitPeriodSeconds         Submit period in seconds
     * @return _revealPeriodSeconds         Reveal period in seconds
     */
    function getPriceEpochConfiguration() external view returns (
        uint256 _firstEpochStartTs,
        uint256 _submitPeriodSeconds,
        uint256 _revealPeriodSeconds
    );
    
    /**
     * @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 asset price details
     * @return _price                                   Price in USD multiplied by ASSET_PRICE_USD_DECIMALS
     * @return _priceTimestamp                          Time when price was updated for the last time
     * @return _priceFinalizationType                   Finalization type when price was updated for the last time
     * @return _lastPriceEpochFinalizationTimestamp     Time when last price epoch was finalized
     * @return _lastPriceEpochFinalizationType          Finalization type of last finalized price epoch
     */
    function getCurrentPriceDetails() external view returns (
        uint256 _price,
        uint256 _priceTimestamp,
        PriceFinalizationType _priceFinalizationType,
        uint256 _lastPriceEpochFinalizationTimestamp,
        PriceFinalizationType _lastPriceEpochFinalizationType
    );

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

contracts/userInterfaces/IFtsoManager.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../ftso/interface/IIFtso.sol";
import "../genesis/interface/IFtsoManagerGenesis.sol";

interface IFtsoManager is IFtsoManagerGenesis {

    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);
    event AccruingUnearnedRewardsFailed(uint256 epochId);

    function active() external view returns (bool);

    function getCurrentRewardEpoch() external view returns (uint256);

    function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view returns (uint256);

    function getRewardEpochToExpireNext() 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 getRewardEpochConfiguration() external view 
        returns (
            uint256 _firstRewardEpochStartTs,
            uint256 _rewardEpochDurationSeconds
        );

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

contracts/ftso/lib/FtsoManagerSettings.sol

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


/**
 * @title A library used for Ftso Manager settings management
 */
library FtsoManagerSettings {
    struct State {
        // struct holding settings related to FTSOs
        // configurable settings
        uint256 maxVotePowerNatThresholdFraction; // high threshold for native token vote power per voter
        uint256 maxVotePowerAssetThresholdFraction; // high threshold for asset vote power per voter
        uint256 lowAssetUSDThreshold; // threshold for low asset vote power (in scaled USD)
        uint256 highAssetUSDThreshold; // threshold for high asset vote power (in scaled USD)
        uint256 highAssetTurnoutThresholdBIPS; // threshold for high asset turnout (in BIPS)
        // actual vote power in (W)NATs / total native token circulating supply (in BIPS)
        uint256 lowNatTurnoutThresholdBIPS;
        uint256 rewardExpiryOffsetSeconds; // Reward epoch closed earlier than 
                                           //block.timestamp - rewardExpiryOffsetSeconds expire
        address[] trustedAddresses; //trusted addresses will be used as a fallback mechanism for setting the price
        bool changed;
        bool initialized;
    }

    function _setState (
        State storage _state,
        uint256 _maxVotePowerNatThresholdFraction,
        uint256 _maxVotePowerAssetThresholdFraction,
        uint256 _lowAssetUSDThreshold,
        uint256 _highAssetUSDThreshold,
        uint256 _highAssetTurnoutThresholdBIPS,
        uint256 _lowNatTurnoutThresholdBIPS,
        uint256 _rewardExpiryOffsetSeconds,
        address[] memory _trustedAddresses
    ) 
        internal
    {
        if (_state.maxVotePowerNatThresholdFraction != _maxVotePowerNatThresholdFraction) {
            _state.changed = true;
            _state.maxVotePowerNatThresholdFraction = _maxVotePowerNatThresholdFraction;
        }
        if (_state.maxVotePowerAssetThresholdFraction != _maxVotePowerAssetThresholdFraction) {
            _state.changed = true;
            _state.maxVotePowerAssetThresholdFraction = _maxVotePowerAssetThresholdFraction;
        }
        if (_state.lowAssetUSDThreshold != _lowAssetUSDThreshold) {
            _state.changed = true;
            _state.lowAssetUSDThreshold = _lowAssetUSDThreshold;
        }
        if (_state.highAssetUSDThreshold != _highAssetUSDThreshold) {
            _state.changed = true;
            _state.highAssetUSDThreshold = _highAssetUSDThreshold;
        }
        if (_state.highAssetTurnoutThresholdBIPS != _highAssetTurnoutThresholdBIPS) {
            _state.changed = true;
            _state.highAssetTurnoutThresholdBIPS = _highAssetTurnoutThresholdBIPS;
        }
        if (_state.lowNatTurnoutThresholdBIPS != _lowNatTurnoutThresholdBIPS) {
            _state.changed = true;
            _state.lowNatTurnoutThresholdBIPS = _lowNatTurnoutThresholdBIPS;
        }
        if (_state.rewardExpiryOffsetSeconds != _rewardExpiryOffsetSeconds) {
            _state.changed = true;
            _state.rewardExpiryOffsetSeconds = _rewardExpiryOffsetSeconds;
        }
        if (_state.trustedAddresses.length != _trustedAddresses.length) {
            _state.trustedAddresses = _trustedAddresses;
            _state.changed = true;
        } else {
            for (uint i = 0; i < _trustedAddresses.length; i++) {
                if (_state.trustedAddresses[i] != _trustedAddresses[i]) {
                    _state.changed = true;
                    _state.trustedAddresses[i] = _trustedAddresses[i];
                }
            }
        }
        _state.initialized = true;
    }
}
          

contracts/addressUpdater/interface/IIAddressUpdatable.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


interface IIAddressUpdatable {
    /**
     * @notice Updates contract addresses - should be called only from AddressUpdater contract
     * @param _contractNameHashes       list of keccak256(abi.encode(...)) contract names
     * @param _contractAddresses        list of contract addresses corresponding to the contract names
     */
    function updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
        ) external;
}
          

contracts/utils/implementation/RevertErrorTracking.sol

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


/**
 * @title Revert Error Tracking
 * @notice A contract to track and store revert errors
 **/
contract RevertErrorTracking {

    struct RevertedError {
        uint192 lastErrorBlock;
        uint64 numErrors;
        address fromContract;
        uint64 errorTypeIndex;
        string errorMessage;
    }

    struct LastErrorData {
        uint192 totalRevertedErrors;
        uint64 lastErrorTypeIndex;
    }

    string internal constant INDEX_TOO_HIGH = "start index high";

    mapping(bytes32 => RevertedError) internal revertedErrors;
    bytes32 [] internal revertErrorHashes;
    LastErrorData public errorData;

    event ContractRevertError(address theContract, uint256 atBlock, string theMessage);

    /**
     * @notice Returns latest reverted error
     * @return _lastErrorBlock         Block number of last reverted error
     * @return _numErrors              Number of times same error with same contract address has been reverted
     * @return _errorString            Revert error message
     * @return _erroringContract       Array of addresses of the reverting contracts
     * @return _totalRevertedErrors    Total number of revert errors across all contracts
     */
    function showLastRevertedError () external view 
        returns(
            uint256[] memory _lastErrorBlock,
            uint256[] memory _numErrors,
            string[] memory _errorString,
            address[] memory _erroringContract,
            uint256 _totalRevertedErrors
        )
    {
        return showRevertedErrors(errorData.lastErrorTypeIndex, 1);
    }
    
    /**
     * @notice Adds caught error to reverted errors mapping
     * @param revertedContract         Address of the reverting contract
     * @param message                  Reverte message
     */
    function addRevertError(address revertedContract, string memory message) internal {
        bytes32 errorStringHash = keccak256(abi.encode(revertedContract, message));

        revertedErrors[errorStringHash].numErrors += 1;
        revertedErrors[errorStringHash].lastErrorBlock = uint192(block.number);
        emit ContractRevertError(revertedContract, block.number, message);
        errorData.totalRevertedErrors += 1;
        
        if (revertedErrors[errorStringHash].numErrors > 1) {
            // not first time this errors
            return;
        }

        // first time we recieve this error string.
        revertErrorHashes.push(errorStringHash);
        revertedErrors[errorStringHash].fromContract = revertedContract;
        revertedErrors[errorStringHash].errorMessage = message;
        revertedErrors[errorStringHash].errorTypeIndex = uint64(revertErrorHashes.length - 1);

        errorData.lastErrorTypeIndex = revertedErrors[errorStringHash].errorTypeIndex;        
    }

    /**
     * @notice Returns latest reverted error
     * @param startIndex               Starting index in the error list array
     * @param numErrorTypesToShow      Number of error types to show
     * @return _lastErrorBlock         Array of last block number this error reverted
     * @return _numErrors              Number of times the same error with same contract address has been tracked
     * @return _errorString            Array of revert error messages
     * @return _erroringContract       Array of addresses of the reverting contracts
     * @return _totalRevertedErrors    Total number of errors reverted across all contracts
     */
    function showRevertedErrors (uint startIndex, uint numErrorTypesToShow) public view 
        returns(
            uint256[] memory _lastErrorBlock,
            uint256[] memory _numErrors,
            string[] memory _errorString,
            address[] memory _erroringContract,
            uint256 _totalRevertedErrors
        )
    {
        require(startIndex < revertErrorHashes.length, INDEX_TOO_HIGH);
        uint256 numReportElements = 
            revertErrorHashes.length >= startIndex + numErrorTypesToShow ?
            numErrorTypesToShow :
            revertErrorHashes.length - startIndex;

        _lastErrorBlock = new uint256[] (numReportElements);
        _numErrors = new uint256[] (numReportElements);
        _errorString = new string[] (numReportElements);
        _erroringContract = new address[] (numReportElements);

        // we have error data error type.
        // error type is hash(error_string, source contract)
        // per error type we report how many times it happened.
        // what was last block it happened.
        // what is the error string.
        // what is the erroring contract
        for (uint i = 0; i < numReportElements; i++) {
            bytes32 hash = revertErrorHashes[startIndex + i];

            _lastErrorBlock[i] = revertedErrors[hash].lastErrorBlock;
            _numErrors[i] = revertedErrors[hash].numErrors;
            _errorString[i] = revertedErrors[hash].errorMessage;
            _erroringContract[i] = revertedErrors[hash].fromContract;
        }
        _totalRevertedErrors = errorData.totalRevertedErrors;
    }
}
          

contracts/token/interface/IICleanable.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

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;
    
    /**
     * Get the current cleanup block number.
     */
    function cleanupBlockNumber() external view returns (uint256);
}
          

contracts/genesis/interface/IFtsoRegistryGenesis.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "./IFtsoGenesis.sol";


interface IFtsoRegistryGenesis {

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

contracts/genesis/interface/IInflationGenesis.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


interface IInflationGenesis {
    /**
     * @notice Receive newly minted native tokens from the FlareDaemon.
     * @dev Assume that the amount received will be >= last topup requested across all services.
     *   If there is not enough balance sent to cover the topup request, expect library method will revert.
     *   Also assume that any balance received greater than the topup request calculated
     *   came from self-destructor sending a balance to this contract.
     */
    function receiveMinting() external payable;
}
          

contracts/utils/interface/IUpdateValidators.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

interface IUpdateValidators {
    /**
    update the validators after reward epoch
     **/
    function updateActiveValidators() external;
}
          

contracts/userInterfaces/IFtsoRegistry.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
pragma abicoder v2;

import "../ftso/interface/IIFtso.sol";
import "../genesis/interface/IFtsoRegistryGenesis.sol";

interface IFtsoRegistry is IFtsoRegistryGenesis {

    function getFtso(uint256 _ftsoIndex) external view returns(IIFtso _activeFtsoAddress);
    function getFtsoBySymbol(string memory _symbol) external view returns(IIFtso _activeFtsoAddress);
    function getSupportedIndices() external view returns(uint256[] memory _supportedIndices);
    function getSupportedSymbols() external view returns(string[] memory _supportedSymbols);
    function getSupportedFtsos() external view returns(IIFtso[] memory _ftsos);
    function getFtsoIndex(string memory _symbol) external view returns (uint256 _assetIndex);
    function getFtsoSymbol(uint256 _ftsoIndex) external view returns (string memory _symbol);
    function getCurrentPrice(uint256 _ftsoIndex) external view returns(uint256 _price, uint256 _timestamp);
    function getCurrentPrice(string memory _symbol) external view returns(uint256 _price, uint256 _timestamp);

    function getSupportedIndicesAndFtsos() external view 
        returns(uint256[] memory _supportedIndices, IIFtso[] memory _ftsos);

    function getSupportedSymbolsAndFtsos() external view 
        returns(string[] memory _supportedSymbols, IIFtso[] memory _ftsos);

    function getSupportedIndicesAndSymbols() external view 
        returns(uint256[] memory _supportedIndices, string[] memory _supportedSymbols);

    function getSupportedIndicesSymbolsAndFtsos() external view 
        returns(uint256[] memory _supportedIndices, string[] memory _supportedSymbols, IIFtso[] memory _ftsos);
}
          

contracts/governance/implementation/GovernedAtGenesis.sol

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

import "./GovernedBase.sol";


/**
 * @title Governed At Genesis
 * @dev This contract enforces a fixed governance address when the constructor
 *  is not executed on a contract (for instance when directly loaded to the genesis block).
 *  This is required to fix governance on a contract when the network starts, at such point
 *  where theoretically no accounts yet exist, and leaving it ungoverned could result in a race
 *  to claim governance by an unauthorized address.
 **/
contract GovernedAtGenesis is GovernedBase {
    constructor(address _governance) GovernedBase(_governance) { }

    /**
     * @notice Set governance to a fixed address when constructor is not called.
     **/
    function initialiseFixedAddress() public virtual returns (address) {
        address governanceAddress = address(0xfffEc6C83c8BF5c3F4AE0cCF8c45CE20E4560BD7);
        
        super.initialise(governanceAddress);
        return governanceAddress;
    }

    /**
     * @notice Disallow initialise to be called
     * @param _governance The governance address for initial claiming
     **/
    // solhint-disable-next-line no-unused-vars
    function initialise(address _governance) public override pure {
        assert(false);
    }
}
          

contracts/governance/implementation/GovernedBase.sol

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

import "../../userInterfaces/IGovernanceSettings.sol";


/**
 * @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 {
    struct TimelockedCall {
        uint256 allowedAfterTimestamp;
        bytes encodedCall;
    }
    
    // solhint-disable-next-line const-name-snakecase
    IGovernanceSettings public constant governanceSettings = 
        IGovernanceSettings(0x1000000000000000000000000000000000000007);

    address private initialGovernance;

    bool private initialised;
    
    bool public productionMode;
    
    bool private executing;
    
    mapping(bytes4 => TimelockedCall) public timelockedCalls;
    
    event GovernanceCallTimelocked(bytes4 selector, uint256 allowedAfterTimestamp, bytes encodedCall);
    event TimelockedGovernanceCallExecuted(bytes4 selector, uint256 timestamp);
    event TimelockedGovernanceCallCanceled(bytes4 selector, uint256 timestamp);
    
    event GovernanceInitialised(address initialGovernance);
    event GovernedProductionModeEntered(address governanceSettings);
    
    modifier onlyGovernance {
        if (executing || !productionMode) {
            _beforeExecute();
            _;
        } else {
            _recordTimelockedCall(msg.data);
        }
    }
    
    modifier onlyImmediateGovernance () {
        _checkOnlyGovernance();
        _;
    }

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

    /**
     * @notice Execute the timelocked governance calls once the timelock period expires.
     * @dev Only executor can call this method.
     * @param _selector The method selector (only one timelocked call per method is stored).
     */
    function executeGovernanceCall(bytes4 _selector) external {
        require(governanceSettings.isExecutor(msg.sender), "only executor");
        TimelockedCall storage call = timelockedCalls[_selector];
        require(call.allowedAfterTimestamp != 0, "timelock: invalid selector");
        require(block.timestamp >= call.allowedAfterTimestamp, "timelock: not allowed yet");
        bytes memory encodedCall = call.encodedCall;
        delete timelockedCalls[_selector];
        executing = true;
        //solhint-disable-next-line avoid-low-level-calls
        (bool success,) = address(this).call(encodedCall);
        executing = false;
        emit TimelockedGovernanceCallExecuted(_selector, block.timestamp);
        _passReturnOrRevert(success);
    }
    
    /**
     * Cancel a timelocked governance call before it has been executed.
     * @dev Only governance can call this method.
     * @param _selector The method selector.
     */
    function cancelGovernanceCall(bytes4 _selector) external onlyImmediateGovernance {
        require(timelockedCalls[_selector].allowedAfterTimestamp != 0, "timelock: invalid selector");
        emit TimelockedGovernanceCallCanceled(_selector, block.timestamp);
        delete timelockedCalls[_selector];
    }
    
    /**
     * Enter the production mode after all the initial governance settings have been set.
     * This enables timelocks and the governance is afterwards obtained by calling 
     * governanceSettings.getGovernanceAddress(). 
     */
    function switchToProductionMode() external {
        _checkOnlyGovernance();
        require(!productionMode, "already in production mode");
        initialGovernance = address(0);
        productionMode = true;
        emit GovernedProductionModeEntered(address(governanceSettings));
    }

    /**
     * @notice Initialize the governance address if not first initialized.
     */
    function initialise(address _initialGovernance) public virtual {
        require(initialised == false, "initialised != false");
        initialised = true;
        initialGovernance = _initialGovernance;
        emit GovernanceInitialised(_initialGovernance);
    }
    
    /**
     * Returns the current effective governance address.
     */
    function governance() public view returns (address) {
        return productionMode ? governanceSettings.getGovernanceAddress() : initialGovernance;
    }

    function _beforeExecute() private {
        if (executing) {
            // can only be run from executeGovernanceCall(), where we check that only executor can call
            // make sure nothing else gets executed, even in case of reentrancy
            assert(msg.sender == address(this));
            executing = false;
        } else {
            // must be called with: productionMode=false
            // must check governance in this case
            _checkOnlyGovernance();
        }
    }

    function _recordTimelockedCall(bytes calldata _data) private {
        _checkOnlyGovernance();
        bytes4 selector;
        //solhint-disable-next-line no-inline-assembly
        assembly {
            selector := calldataload(_data.offset)
        }
        uint256 timelock = governanceSettings.getTimelock();
        uint256 allowedAt = block.timestamp + timelock;
        timelockedCalls[selector] = TimelockedCall({
            allowedAfterTimestamp: allowedAt,
            encodedCall: _data
        });
        emit GovernanceCallTimelocked(selector, allowedAt, _data);
    }
    
    function _checkOnlyGovernance() private view {
        require(msg.sender == governance(), "only governance");
    }
    
    function _passReturnOrRevert(bool _success) private pure {
        // pass exact return or revert data - needs to be done in assembly
        //solhint-disable-next-line no-inline-assembly
        assembly {
            let size := returndatasize()
            let ptr := mload(0x40)
            mstore(0x40, add(ptr, size))
            returndatacopy(ptr, 0, size)
            if _success {
                return(ptr, size)
            }
            revert(ptr, size)
        }
    }
}
          

contracts/userInterfaces/IVoterWhitelister.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

interface IVoterWhitelister {
    /**
     * Raised when an account is removed from the voter whitelist.
     */
    event VoterWhitelisted(address voter, uint256 ftsoIndex);
    
    /**
     * Raised when an account is removed from the voter whitelist.
     */
    event VoterRemovedFromWhitelist(address voter, uint256 ftsoIndex);

    /**
     * Request to whitelist `_voter` account to ftso at `_ftsoIndex`. Will revert if vote power too low.
     * May be called by any address.
     */
    function requestWhitelistingVoter(address _voter, uint256 _ftsoIndex) external;

    /**
     * Request to whitelist `_voter` account to all active ftsos.
     * May be called by any address.
     * It returns an array of supported ftso indices and success flag per index.
     */
    function requestFullVoterWhitelisting(
        address _voter
    ) 
        external 
        returns (
            uint256[] memory _supportedIndices,
            bool[] memory _success
        );

    /**
     * Maximum number of voters in the whitelist for a new FTSO.
     */
    function defaultMaxVotersForFtso() external view returns (uint256);
    
    /**
     * Maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
     */
    function maxVotersForFtso(uint256 _ftsoIndex) external view returns (uint256);

    /**
     * Get whitelisted price providers for ftso with `_symbol`
     */
    function getFtsoWhitelistedPriceProvidersBySymbol(string memory _symbol) external view returns (address[] memory);

    /**
     * Get whitelisted price providers for ftso at `_ftsoIndex`
     */
    function getFtsoWhitelistedPriceProviders(uint256 _ftsoIndex) external view returns (address[] memory);
}
          

contracts/token/interface/IIVPContract.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IVPToken.sol";
import "../../userInterfaces/IVPContractEvents.sol";
import "./IICleanable.sol";

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

    /**
    * @notice Get the vote power of `_who` at block `_blockNumber`, ignoring revocation information (and cache).
    * @param _who The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_who` at `_blockNumber`. Result doesn't change if vote power is revoked.
    */
    function votePowerOfAtIgnoringRevocation(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);
}
          

contracts/utils/implementation/GovernedAndFlareDaemonized.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
import { FlareDaemon } from "../../genesis/implementation/FlareDaemon.sol";
import { Governed } from "../../governance/implementation/Governed.sol";


contract GovernedAndFlareDaemonized is Governed {

    FlareDaemon public immutable flareDaemon;

    modifier onlyFlareDaemon () {
        require (msg.sender == address(flareDaemon), "only flare daemon");
        _;
    }

    constructor(address _governance, FlareDaemon _flareDaemon) Governed(_governance) {
        require(address(_flareDaemon) != address(0), "flare daemon zero");
        flareDaemon = _flareDaemon;
    }
}
          

@openzeppelin/contracts/math/SafeMath.sol

// SPDX-License-Identifier: MIT

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

contracts/ftso/interface/IIFtso.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../genesis/interface/IFtsoGenesis.sol";
import "../../userInterfaces/IFtso.sol";
import "../../token/interface/IIVPToken.sol";


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 fallbackFinalizePriceEpoch(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 _submitPeriodSeconds,
        uint256 _revealPeriodSeconds
    ) 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 ftso manager address
     */
    function ftsoManager() external view returns (address);

    /**
     * @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);
    
    /**
     * @notice Returns current asset price calculated from trusted providers
     * @return _price               Price in USD multiplied by ASSET_PRICE_USD_DECIMALS
     * @return _timestamp           Time when price was updated for the last time
     */
    function getCurrentPriceFromTrustedProviders() external view returns (uint256 _price, uint256 _timestamp);
}
          

contracts/ftso/interface/IIFtsoManagerV1.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


// interface for the first version of ftso manger (V1 = oldest version) - last version is always without any Vx
interface IIFtsoManagerV1 {
    function rewardEpochsStartTs() external view returns(uint256);
    function rewardEpochDurationSeconds() external view returns(uint256);

    function getCurrentRewardEpoch() external view returns(uint256);
    
    function rewardEpochs(uint256 _rewardEpochId) external view returns (
        uint256 _votepowerBlock,
        uint256 _startBlock,
        uint256 _startTimestamp
    );

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

contracts/genesis/interface/IFlareDaemonize.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


/// Any contracts that want to recieve a trigger from Flare daemon should 
///     implement IFlareDaemonize
interface IFlareDaemonize {

    /// Implement this function for recieving a trigger from FlareDaemon.
    function daemonize() external returns (bool);
    
    /// This function will be called after an error is caught in daemonize().
    /// It will switch the contract to a simpler fallback mode, which hopefully works when full mode doesn't.
    /// Not every contract needs to support fallback mode (FtsoManager does), so this method may be empty.
    /// Switching back to normal mode is left to the contract (typically a governed method call).
    /// This function may be called due to low-gas error, so it shouldn't use more than ~30.000 gas.
    /// @return true if switched to fallback mode, false if already in fallback mode or if falback not supported
    function switchToFallbackMode() external returns (bool);

    
    /// Implement this function for updating daemonized contracts through AddressUpdater.
    function getContractName() external view returns (string memory);
}
          

contracts/utils/interface/IIFtsoRegistry.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;
pragma abicoder v2;

import "../../ftso/interface/IIFtso.sol";
import "../../userInterfaces/IFtsoRegistry.sol";


interface IIFtsoRegistry is IFtsoRegistry {

    // returns ftso index
    function addFtso(IIFtso _ftsoContract) external returns(uint256);

    function removeFtso(IIFtso _ftso) external;
}
          

contracts/userInterfaces/IFtsoRewardManager.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

interface IFtsoRewardManager {

    event RewardClaimed(
        address indexed dataProvider,
        address indexed whoClaimed,
        address indexed sentTo,
        uint256 rewardEpoch, 
        uint256 amount
    );

    event UnearnedRewardsAccrued(
        uint256 epochId,
        uint256 reward
    );

    event RewardsDistributed(
        address indexed ftso,
        uint256 epochId,
        address[] addresses,
        uint256[] rewards
    );

    event RewardClaimsEnabled(
        uint256 rewardEpochId
    ); 

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

    event RewardClaimsExpired(
        uint256 rewardEpochId
    );    
    
    event ClaimExecutorsChanged(
        address rewardOwner,
        address[] executors
    );

    event AllowedClaimRecipientsChanged(
        address rewardOwner,
        address[] recipients
    );

    /**
     * @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 a percentage delegator to claim and wrap rewards.
     * @notice This function is intended to be used to claim and wrap 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 claimAndWrapReward(address payable _recipient, uint256[] memory _rewardEpochs)
        external returns (uint256 _rewardAmount);

    /**
     * @notice Allows a percentage delegator to claim and wrap rewards.
     * @notice This function is intended to be used to claim and wrap rewards in case of delegation by percentage.
     * @notice The caller does not have to be the owner, but must be approved by the owner to claim on his behalf.
     *   this approval is done by calling `addClaimExecutor`.
     * @notice It is actually safe for this to be called by anybody (nothing can be stolen), but by limiting who can
     *   call, we allow the owner to control the timing of the calls.
     * @param _rewardOwner          address of the reward owner
     * @param _recipient            address of the recipient; must be either _rewardOwner or one of the addresses 
     *  allowed by the _rewardOwner
     * @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 claimAndWrapRewardByExecutor(
        address _rewardOwner,
        address payable _recipient,
        uint256[] memory _rewardEpochs
    ) external returns (uint256 _rewardAmount);

    /**
     * @notice Allows the sender to claim 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 the sender to claim and wrap rewards from specified data providers.
     * @notice This function is intended to be used to claim and wrap 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 claimAndWrapRewardFromDataProviders(
        address payable _recipient,
        uint256[] memory _rewardEpochs,
        address[] memory _dataProviders
    )
        external
        returns (uint256 _rewardAmount);

    /**
     * @notice Allows the sender to claim and wrap rewards from specified data providers.
     * @notice This function is intended to be used to claim and wrap rewards in case of delegation by amount.
     * @notice The caller does not have to be the owner, but must be approved by the owner to claim on his behalf.
     *   this approval is done by calling `addClaimExecutor`.
     * @notice It is actually safe for this to be called by anybody (nothing can be stolen), but by limiting who can
     *   call, we allow the owner to control the timing of the calls.
     * @param _rewardOwner          address of the reward owner
     * @param _recipient            address of the recipient; must be either _rewardOwner or one of the addresses 
     *  allowed by the _rewardOwner
     * @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 claimAndWrapRewardFromDataProvidersByExecutor(
        address _rewardOwner,
        address payable _recipient,
        uint256[] memory _rewardEpochs,
        address[] memory _dataProviders
    ) external returns (uint256 _rewardAmount);
        
    /**
     * Set the addresses of executors, who are allowed to call claimAndWrapRewardByExecutor
     * and claimAndWrapRewardFromDataProvidersByExecutor.
     * @param _executors The new executors. All old executors will be deleted and replaced by these.
     */    
    function setClaimExecutors(address[] memory _executors) external;

    /**
     * Set the addresses of allowed recipients in the methods claimAndWrapRewardByExecutor
     * and claimAndWrapRewardFromDataProvidersByExecutor.
     * Apart from these, the reward owner is always an allowed recipient.
     * @param _recipients The new allowed recipients. All old recipients will be deleted and replaced by these.
     */    
    function setAllowedClaimRecipients(address[] memory _recipients) external;
    
    /**
     * @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 Allows reward claiming
     */
    function active() external view returns (bool);

    /**
     * @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 fee percentage of `_dataProvider` at `_rewardEpoch`
     * @param _dataProvider         address representing data provider
     * @param _rewardEpoch          reward epoch number
     */
    function getDataProviderFeePercentage(
        address _dataProvider,
        uint256 _rewardEpoch
    )
        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);

    /**
     * @notice Return reward epoch vote power block
     * @param _rewardEpoch          reward epoch number
     */
    function getRewardEpochVotePowerBlock(uint256 _rewardEpoch) external view returns (uint256);

    /**
     * @notice Return current reward epoch number
     */
    function getCurrentRewardEpoch() external view returns (uint256);

    /**
     * @notice Return initial reward epoch number
     */
    function getInitialRewardEpoch() external view returns (uint256);

    /**
     * @notice Returns the information on rewards and initial vote power of `_dataProvider` for `_rewardEpoch`
     * @param _rewardEpoch                      reward epoch number
     * @param _dataProvider                     address representing the data provider
     * @return _rewardAmount                    number representing the amount of rewards
     * @return _votePowerIgnoringRevocation     number representing the vote power ignoring revocations
     */
    function getDataProviderPerformanceInfo(
        uint256 _rewardEpoch,
        address _dataProvider
    )
        external view 
        returns (
            uint256 _rewardAmount,
            uint256 _votePowerIgnoringRevocation
        );

    /**
     * Get the addresses of executors, who are allowed to call claimAndWrapRewardByExecutor
     * and claimAndWrapRewardFromDataProvidersByExecutor.
     */    
    function claimExecutors(address _rewardOwner) external view returns (address[] memory);
    
    /**
     * Get the addresses of allowed recipients in the methods claimAndWrapRewardByExecutor
     * and claimAndWrapRewardFromDataProvidersByExecutor.
     * Apart from these, the reward owner is always an allowed recipient.
     */    
    function allowedClaimRecipients(address _rewardOwner) external view returns (address[] memory);
}
          

contracts/addressUpdater/implementation/AddressUpdatable.sol

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

import "../interface/IIAddressUpdatable.sol";


abstract contract AddressUpdatable is IIAddressUpdatable {

    // https://docs.soliditylang.org/en/v0.8.7/contracts.html#constant-and-immutable-state-variables
    // No storage slot is allocated
    bytes32 internal constant ADDRESS_STORAGE_POSITION = 
        keccak256("flare.diamond.AddressUpdatable.ADDRESS_STORAGE_POSITION");

    modifier onlyAddressUpdater() {
        require (msg.sender == getAddressUpdater(), "only address updater");
        _;
    }

    constructor(address _addressUpdater) {
        setAddressUpdaterValue(_addressUpdater);
    }

    function getAddressUpdater() public view returns (address _addressUpdater) {
        // Only direct constants are allowed in inline assembly, so we assign it here
        bytes32 position = ADDRESS_STORAGE_POSITION;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            _addressUpdater := sload(position)
        }
    }

    /**
     * @notice external method called from AddressUpdater only
     */
    function updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    )
        external override
        onlyAddressUpdater
    {
        // update addressUpdater address
        setAddressUpdaterValue(_getContractAddress(_contractNameHashes, _contractAddresses, "AddressUpdater"));
        // update all other addresses
        _updateContractAddresses(_contractNameHashes, _contractAddresses);
    }

    /**
     * @notice virtual method that a contract extending AddressUpdatable must implement
     */
    function _updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    ) internal virtual;

    /**
     * @notice helper method to get contract address
     * @dev it reverts if contract name does not exist
     */
    function _getContractAddress(
        bytes32[] memory _nameHashes,
        address[] memory _addresses,
        string memory _nameToFind
    )
        internal pure
        returns(address)
    {
        bytes32 nameHash = keccak256(abi.encode(_nameToFind));
        address a = address(0);
        for (uint256 i = 0; i < _nameHashes.length; i++) {
            if (nameHash == _nameHashes[i]) {
                a = _addresses[i];
                break;
            }
        }
        require(a != address(0), "address zero");
        return a;
    }

    function setAddressUpdaterValue(address _addressUpdater) internal {
        // Only direct constants are allowed in inline assembly, so we assign it here
        bytes32 position = ADDRESS_STORAGE_POSITION;
        // solhint-disable-next-line no-inline-assembly  
        assembly {
            sstore(position, _addressUpdater)
        }
    }
}
          

contracts/genesis/interface/IFtsoGenesis.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


interface IFtsoGenesis {

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

contracts/token/interface/IIVPToken.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import "../../userInterfaces/IVPToken.sol";
import "../../userInterfaces/IGovernanceVotePower.sol";
import "./IIVPContract.sol";
import "./IIGovernanceVotePower.sol";
import "./IICleanable.sol";

interface IIVPToken is IVPToken, IICleanable {
    /**
     * Set the contract that is allowed to set cleanupBlockNumber.
     * Usually this will be an instance of CleanupBlockNumberManager.
     */
    function setCleanupBlockNumberManager(address _cleanupBlockNumberManager) external;
    
    /**
     * 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);
}
          

contracts/genesis/interface/IFtsoManagerGenesis.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;


interface IFtsoManagerGenesis {

    function getCurrentPriceEpochId() external view returns (uint256 _priceEpochId);

}
          

contracts/userInterfaces/IGovernanceSettings.sol

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


/**
 * A special contract that holds Flare governance address.
 * This contract enables updating governance address and timelock only by hard forking the network,
 * meaning only by updating validator code.
 */
interface IGovernanceSettings {
    /**
     * Get the governance account address.
     * The governance address can only be changed by a hardfork.
     */
    function getGovernanceAddress() external view returns (address);
    
    /**
     * Get the time in seconds that must pass between a governance call and execution.
     * The timelock value can only be changed by a hardfork.
     */
    function getTimelock() external view returns (uint256);
    
    /**
     * Get the addresses of the accounts that are allowed to execute the timelocked governance calls
     * once the timelock period expires.
     * Executors can be changed without a hardfork, via a normal governance call.
     */
    function getExecutors() external view returns (address[] memory);
    
    /**
     * Check whether an address is one of the executors.
     */
    function isExecutor(address _address) external view returns (bool);
}
          

contracts/userInterfaces/IGovernanceVotePower.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

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 of `msg.sender``.
     **/
    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 _votePower    Governance vote power of `_who` at `_blockNumber`.
    */
    function votePowerOfAt(address _who, uint256 _blockNumber) external view returns(uint256);

    /**
    * @notice Get the vote power of `account` at the current block.
    * @param account The address to get voting power.
    * @return Vote power of `account` at the current block number.
    */    
    function getVotes(address account) external view returns (uint256);

    /**
    * @notice Get the delegate's address of `_who` at block `_blockNumber`
    * @param _who The address to get delegate's address.
    * @param _blockNumber The block number at which to fetch.
    * @return Delegate's address of `_who` at `_blockNumber`.
    */
    function getDelegateOfAt(address _who, uint256 _blockNumber) external view returns (address);

    /**
    * @notice Get the delegate's address of `_who` at the current block.
    * @param _who The address to get delegate's address.
    * @return Delegate's address of `_who` at the current block number.
    */    
    function getDelegateOfAtNow(address _who) external  view returns (address);

}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

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

contracts/userInterfaces/IVPToken.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IGovernanceVotePower} from "./IGovernanceVotePower.sol";
import {IVPContractEvents} from "./IVPContractEvents.sol";

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 Undelegate all percentage delegations from teh sender and then delegate corresponding 
     *   `_bips` percentage of voting power from the sender to each member of `_delegatees`.
     * @param _delegatees The addresses of the new recipients.
     * @param _bips The percentages of voting power to be delegated expressed in basis points (1/100 of one percent).
     *   Total of all `_bips` values must be at most 10000.
     **/
    function batchDelegate(address[] memory _delegatees, uint256[] memory _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 vote power of `_owner` at block `_blockNumber`, ignoring revocation information (and cache).
    * @param _owner The address to get voting power.
    * @param _blockNumber The block number at which to fetch.
    * @return Vote power of `_owner` at `_blockNumber`. Result doesn't change if vote power is revoked.
    */
    function votePowerOfAtIgnoringRevocation(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);
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_governance","internalType":"address"},{"type":"address","name":"_flareDaemon","internalType":"contract FlareDaemon"},{"type":"address","name":"_addressUpdater","internalType":"address"},{"type":"address","name":"_priceSubmitter","internalType":"contract IIPriceSubmitter"},{"type":"address","name":"_oldFtsoManager","internalType":"contract IIFtsoManagerV1"},{"type":"uint256","name":"_firstPriceEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_priceEpochDurationSeconds","internalType":"uint256"},{"type":"uint256","name":"_revealEpochDurationSeconds","internalType":"uint256"},{"type":"uint256","name":"_firstRewardEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_rewardEpochDurationSeconds","internalType":"uint256"},{"type":"uint256","name":"_votePowerIntervalFraction","internalType":"uint256"}]},{"type":"event","name":"AccruingUnearnedRewardsFailed","inputs":[{"type":"uint256","name":"epochId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CleanupBlockNumberManagerFailedForBlock","inputs":[{"type":"uint256","name":"blockNumber","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ClosingExpiredRewardEpochFailed","inputs":[{"type":"uint256","name":"rewardEpoch","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ContractRevertError","inputs":[{"type":"address","name":"theContract","internalType":"address","indexed":false},{"type":"uint256","name":"atBlock","internalType":"uint256","indexed":false},{"type":"string","name":"theMessage","internalType":"string","indexed":false}],"anonymous":false},{"type":"event","name":"DistributingRewardsFailed","inputs":[{"type":"address","name":"ftso","internalType":"address","indexed":false},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"FallbackMode","inputs":[{"type":"bool","name":"fallbackMode","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"FinalizingPriceEpochFailed","inputs":[{"type":"address","name":"ftso","internalType":"contract IIFtso","indexed":false},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":false},{"type":"uint8","name":"failingType","internalType":"enum IFtso.PriceFinalizationType","indexed":false}],"anonymous":false},{"type":"event","name":"FtsoAdded","inputs":[{"type":"address","name":"ftso","internalType":"contract IIFtso","indexed":false},{"type":"bool","name":"add","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"FtsoDeactivationFailed","inputs":[{"type":"address","name":"ftso","internalType":"contract IIFtso","indexed":false}],"anonymous":false},{"type":"event","name":"FtsoFallbackMode","inputs":[{"type":"address","name":"ftso","internalType":"contract IIFtso","indexed":false},{"type":"bool","name":"fallbackMode","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceCallTimelocked","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256","indexed":false},{"type":"bytes","name":"encodedCall","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceInitialised","inputs":[{"type":"address","name":"initialGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"GovernedProductionModeEntered","inputs":[{"type":"address","name":"governanceSettings","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"InitializingCurrentEpochStateForRevealFailed","inputs":[{"type":"address","name":"ftso","internalType":"contract IIFtso","indexed":false},{"type":"uint256","name":"epochId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"PriceEpochFinalized","inputs":[{"type":"address","name":"chosenFtso","internalType":"address","indexed":false},{"type":"uint256","name":"rewardEpochId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RewardEpochFinalized","inputs":[{"type":"uint256","name":"votepowerBlock","internalType":"uint256","indexed":false},{"type":"uint256","name":"startBlock","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallCanceled","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"TimelockedGovernanceCallExecuted","inputs":[{"type":"bytes4","name":"selector","internalType":"bytes4","indexed":false},{"type":"uint256","name":"timestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatingActiveValidatorsTriggerFailed","inputs":[{"type":"uint256","name":"rewardEpoch","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_TRUSTED_ADDRESSES_LENGTH","inputs":[]},{"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":"addFtso","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addFtsosBulk","inputs":[{"type":"address[]","name":"_ftsos","internalType":"contract IIFtso[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract CleanupBlockNumberManager"}],"name":"cleanupBlockNumberManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"currentRewardEpochEnds","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"daemonize","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deactivateFtsos","inputs":[{"type":"address[]","name":"_ftsos","internalType":"contract IIFtso[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint192","name":"totalRevertedErrors","internalType":"uint192"},{"type":"uint64","name":"lastErrorTypeIndex","internalType":"uint64"}],"name":"errorData","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract FlareDaemon"}],"name":"flareDaemon","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"ftsoRegistry","internalType":"contract IIFtsoRegistry"},{"type":"address","name":"voterWhitelister","internalType":"contract IIVoterWhitelister"}],"name":"ftsoManagement","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtsoRegistry"}],"name":"ftsoRegistry","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_addressUpdater","internalType":"address"}],"name":"getAddressUpdater","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"getContractName","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_priceEpochId","internalType":"uint256"},{"type":"uint256","name":"_priceEpochStartTimestamp","internalType":"uint256"},{"type":"uint256","name":"_priceEpochEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"_priceEpochRevealEndTimestamp","internalType":"uint256"},{"type":"uint256","name":"_currentTimestamp","internalType":"uint256"}],"name":"getCurrentPriceEpochData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_priceEpochId","internalType":"uint256"}],"name":"getCurrentPriceEpochId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentRewardEpoch","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"_fallbackMode","internalType":"bool"},{"type":"address[]","name":"_ftsos","internalType":"contract IIFtso[]"},{"type":"bool[]","name":"_ftsoInFallbackMode","internalType":"bool[]"}],"name":"getFallbackMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"_ftsos","internalType":"contract IIFtso[]"}],"name":"getFtsos","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_maxVotePowerNatThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_maxVotePowerAssetThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_lowAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_lowNatTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_rewardExpiryOffsetSeconds","internalType":"uint256"},{"type":"address[]","name":"_trustedAddresses","internalType":"address[]"},{"type":"bool","name":"_initialized","internalType":"bool"},{"type":"bool","name":"_changed","internalType":"bool"}],"name":"getGovernanceParameters","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_lastUnprocessedPriceEpoch","internalType":"uint256"},{"type":"uint256","name":"_lastUnprocessedPriceEpochRevealEnds","internalType":"uint256"},{"type":"bool","name":"_lastUnprocessedPriceEpochInitialized","internalType":"bool"}],"name":"getLastUnprocessedPriceEpochData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_firstPriceEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_priceEpochDurationSeconds","internalType":"uint256"},{"type":"uint256","name":"_revealEpochDurationSeconds","internalType":"uint256"}],"name":"getPriceEpochConfiguration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIPriceSubmitter"}],"name":"getPriceSubmitter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_firstRewardEpochStartTs","internalType":"uint256"},{"type":"uint256","name":"_rewardEpochDurationSeconds","internalType":"uint256"}],"name":"getRewardEpochConfiguration","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct IIFtsoManager.RewardEpochData","components":[{"type":"uint256","name":"votepowerBlock","internalType":"uint256"},{"type":"uint256","name":"startBlock","internalType":"uint256"},{"type":"uint256","name":"startTimestamp","internalType":"uint256"}]}],"name":"getRewardEpochData","inputs":[{"type":"uint256","name":"_rewardEpochId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRewardEpochToExpireNext","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_votepowerBlock","internalType":"uint256"}],"name":"getRewardEpochVotePowerBlock","inputs":[{"type":"uint256","name":"_rewardEpoch","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getVotePowerIntervalFraction","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IGovernanceSettings"}],"name":"governanceSettings","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialise","inputs":[{"type":"address","name":"_initialGovernance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"lastRewardedFtsoAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"notInitializedFtsos","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtsoManagerV1"}],"name":"oldFtsoManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIPriceSubmitter"}],"name":"priceSubmitter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"productionMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeFtso","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"replaceFtso","inputs":[{"type":"address","name":"_ftsoToAdd","internalType":"contract IIFtso"},{"type":"bool","name":"_copyCurrentPrice","internalType":"bool"},{"type":"bool","name":"_copyAssetOrAssetFtsos","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"replaceFtsosBulk","inputs":[{"type":"address[]","name":"_ftsosToAdd","internalType":"contract IIFtso[]"},{"type":"bool","name":"_copyCurrentPrice","internalType":"bool"},{"type":"bool","name":"_copyAssetOrAssetFtsos","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardEpochDurationSeconds","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_votepowerBlock","internalType":"uint256"},{"type":"uint256","name":"_startBlock","internalType":"uint256"},{"type":"uint256","name":"_startTimestamp","internalType":"uint256"}],"name":"rewardEpochs","inputs":[{"type":"uint256","name":"_rewardEpochId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardEpochsStartTs","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIFtsoRewardManager"}],"name":"rewardManager","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFallbackMode","inputs":[{"type":"bool","name":"_fallbackMode","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFtsoAsset","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"},{"type":"address","name":"_asset","internalType":"contract IIVPToken"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFtsoAssetFtsos","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"},{"type":"address[]","name":"_assetFtsos","internalType":"contract IIFtso[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFtsoFallbackMode","inputs":[{"type":"address","name":"_ftso","internalType":"contract IIFtso"},{"type":"bool","name":"_fallbackMode","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setGovernanceParameters","inputs":[{"type":"uint256","name":"_maxVotePowerNatThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_maxVotePowerAssetThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"_lowAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"_highAssetTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_lowNatTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"_rewardExpiryOffsetSeconds","internalType":"uint256"},{"type":"address[]","name":"_trustedAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setInitialRewardData","inputs":[{"type":"uint256","name":"_nextRewardEpochToExpire","internalType":"uint256"},{"type":"uint256","name":"_rewardEpochsLength","internalType":"uint256"},{"type":"uint256","name":"_currentRewardEpochEnds","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardEpochDurationSeconds","inputs":[{"type":"uint256","name":"_rewardEpochDurationSeconds","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setUpdateOnRewardEpochSwitchover","inputs":[{"type":"address","name":"_updateValidators","internalType":"contract IUpdateValidators"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setVotePowerIntervalFraction","inputs":[{"type":"uint256","name":"_votePowerIntervalFraction","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"maxVotePowerNatThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"maxVotePowerAssetThresholdFraction","internalType":"uint256"},{"type":"uint256","name":"lowAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"highAssetUSDThreshold","internalType":"uint256"},{"type":"uint256","name":"highAssetTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"lowNatTurnoutThresholdBIPS","internalType":"uint256"},{"type":"uint256","name":"rewardExpiryOffsetSeconds","internalType":"uint256"},{"type":"bool","name":"changed","internalType":"bool"},{"type":"bool","name":"initialized","internalType":"bool"}],"name":"settings","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"_lastErrorBlock","internalType":"uint256[]"},{"type":"uint256[]","name":"_numErrors","internalType":"uint256[]"},{"type":"string[]","name":"_errorString","internalType":"string[]"},{"type":"address[]","name":"_erroringContract","internalType":"address[]"},{"type":"uint256","name":"_totalRevertedErrors","internalType":"uint256"}],"name":"showLastRevertedError","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"_lastErrorBlock","internalType":"uint256[]"},{"type":"uint256[]","name":"_numErrors","internalType":"uint256[]"},{"type":"string[]","name":"_errorString","internalType":"string[]"},{"type":"address[]","name":"_erroringContract","internalType":"address[]"},{"type":"uint256","name":"_totalRevertedErrors","internalType":"uint256"}],"name":"showRevertedErrors","inputs":[{"type":"uint256","name":"startIndex","internalType":"uint256"},{"type":"uint256","name":"numErrorTypesToShow","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IISupply"}],"name":"supply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"switchToFallbackMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"switchToProductionMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"allowedAfterTimestamp","internalType":"uint256"},{"type":"bytes","name":"encodedCall","internalType":"bytes"}],"name":"timelockedCalls","inputs":[{"type":"bytes4","name":"","internalType":"bytes4"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateContractAddresses","inputs":[{"type":"bytes32[]","name":"_contractNameHashes","internalType":"bytes32[]"},{"type":"address[]","name":"_contractAddresses","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IUpdateValidators"}],"name":"updateOnRewardEpochSwitchover","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIVoterWhitelister"}],"name":"voterWhitelister","inputs":[]}]
              

Contract Creation Code

0x6101606040523480156200001257600080fd5b5060405162005e4138038062005e41833981016040819052620000359162000548565b888b8b81806001600160a01b038116156200005557620000558162000461565b506001600160a01b038116620000a5576040805162461bcd60e51b815260206004820152601060248201526f5f676f7665726e616e6365207a65726f60801b604482015290519081900360640190fd5b506001600160a01b038116620000f6576040805162461bcd60e51b8152602060048201526011602482015270666c617265206461656d6f6e207a65726f60781b604482015290519081900360640190fd5b60601b6001600160601b03191660805250620001128162000524565b50854210156040518060400160405280601e81526020017f46697273742065706f636820737461727420747320696e20667574757265000081525090620001775760405162461bcd60e51b81526004016200016e919062000604565b60405180910390fd5b5060408051808201909152600e81526d05265776172642065706f636820360941b602082015282620001be5760405162461bcd60e51b81526004016200016e919062000604565b5060408051808201909152600d81526c050726963652065706f6368203609c1b602082015285620002045760405162461bcd60e51b81526004016200016e919062000604565b5060408051808201909152601481527f52657665616c2070726963652065706f636820300000000000000000000000006020820152846200025a5760405162461bcd60e51b81526004016200016e919062000604565b5060408051808201909152601e81527f566f746520706f77657220696e74657276616c206672616374696f6e20300000602082015281620002b05760405162461bcd60e51b81526004016200016e919062000604565b5060408051808201909152601b81527f52657665616c2070726963652065706f636820746f6f206c6f6e6700000000006020820152858510620003085760405162461bcd60e51b81526004016200016e919062000604565b508284870111156040518060400160405280601b81526020017f5265776172642065706f636820737461727420746f6f20736f6f6e000000000081525090620003665760405162461bcd60e51b81526004016200016e919062000604565b50848685850303816200037557fe5b0660001460405180606001604052806024815260200162005e1d6024913990620003b45760405162461bcd60e51b81526004016200016e919062000604565b50848281620003bf57fe5b0660001460405180606001604052806027815260200162005df66027913990620003fe5760405162461bcd60e51b81526004016200016e919062000604565b50610100839052601b829055601c81905560a086905260c085905260e0849052601983905584868403816200042f57fe5b046018555050505050606092831b6001600160601b0319908116610120529190921b1661014052506200067392505050565b600054600160a01b900460ff1615620004c1576040805162461bcd60e51b815260206004820152601460248201527f696e697469616c6973656420213d2066616c7365000000000000000000000000604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b60008060008060008060008060008060006101608c8e0312156200056a578687fd5b8b5162000577816200065a565b60208d0151909b506200058a816200065a565b60408d0151909a506200059d816200065a565b60608d0151909950620005b0816200065a565b60808d0151909850620005c3816200065a565b8097505060a08c0151955060c08c0151945060e08c015193506101008c015192506101208c015191506101408c015190509295989b509295989b9093969950565b6000602080835283518082850152825b81811015620006325785810183015185820160400152820162000614565b81811115620006445783604083870101525b50601f01601f1916929092016040019392505050565b6001600160a01b03811681146200067057600080fd5b50565b60805160601c60a05160c05160e051610100516101205160601c6101405160601c6156a46200075260003980611ef15280613dd5525080610830528061232a52806129745280612efe528061357f525080610a155280611c1f52806127b252806128cd5250806109ec5280611b155280613f0352806146c852508061092d52806109cb5280611ae452806123aa5280612c175280613ee15280614050528061426952806146a55250806109aa5280611ac352806123cb5280613ebf528061402b52806146805250806113895280611bfb528061206e52506156a46000f3fe608060405234801561001057600080fd5b50600436106103db5760003560e01c806374e6310e1161020a578063b4dba0f311610125578063e22fdece116100b8578063f2edab5a11610087578063f2edab5a146107bb578063f5a98383146107ce578063f5f5ba72146107d6578063f937d6ad146107eb578063ff882fbb146107f3576103db565b8063e22fdece14610775578063e371aef01461077d578063e5399da314610793578063e7c830d4146107b3576103db565b8063d89c39e6116100f4578063d89c39e614610735578063e06174e41461073d578063e080a9701461075a578063e17f212e1461076d576103db565b8063b4dba0f3146106fd578063c2b0d47b14610705578063ce69f8331461070d578063d429cfe514610722576103db565b8063a10775321161019d578063a7d2acfa1161016c578063a7d2acfa146106ae578063a93a6f42146106c4578063af946af7146106d7578063b00c0b76146106ea576103db565b8063a107753214610678578063a578f55b14610680578063a670ff8714610688578063a795f4091461069b576103db565b80638de306b1116101d95780638de306b1146106265780639131205b1461063957806393a790251461064c5780639d6a890f14610665576103db565b806374e6310e146105d7578063758ff1da146105f8578063823033a91461060b57806385f3c9c91461061e576103db565b80633fdeb7e1116102fa57806360f2c5b21161028d5780636b65cc341161025c5780636b65cc34146105925780636ca051e6146105a55780636d0e8c34146105bc5780636ea0aa31146105c4576103db565b806360f2c5b21461056757806362354e031461056f57806367fc40291461057757806369b11ac61461058a576103db565b80635835cf30116102c95780635835cf30146105265780635904089a146105445780635aa6e6751461054c5780635ff2707914610554576103db565b80633fdeb7e1146104ec5780634b48dd5e146104ff5780634eac870f146105165780635267a15d1461051e576103db565b80631cb513f711610372578063361b545911610341578063361b5459146104b65780633758e679146104c957806338b5f869146104dc5780633e7ff857146104e4576103db565b80631cb513f71461046c5780632663f1b4146104825780632b3c41a4146104955780632fd8eb7d146104ae576103db565b80630f15f4c0116103ae5780630f15f4c0146104305780630f4ef8a61461043a578063132c7e1f14610442578063144e159114610455576103db565b806302fb0c5e146103e0578063047fc9aa146103fe57806308a7f402146104135780630e063d7d14610428575b600080fd5b6103e8610806565b6040516103f59190615188565b60405180910390f35b61040661080f565b6040516103f59190614fe1565b61041b61081e565b6040516103f5919061537c565b61040661082e565b610438610852565b005b6104066108a0565b610438610450366004614dc2565b6108af565b61045d6109a8565b6040516103f5939291906153c4565b610474610a10565b6040516103f592919061539e565b6104386104903660046149d3565b610a37565b61049d610a76565b6040516103f59594939291906150e9565b610406610ab1565b6104386104c4366004614dc2565b610ac0565b6104386104d7366004614d5d565b610b4c565b610406610b9b565b61041b610baa565b6104386104fa3660046149d3565b610bb0565b610507610bff565b6040516103f593929190615193565b610406610cde565b610406610ced565b61052e610d12565b6040516103f59a99989796959493929190615441565b610406610dd9565b610406610de8565b610438610562366004614cbe565b610e7c565b61041b6111d1565b6104066111d7565b610438610585366004614cbe565b6111e2565b61041b6112c5565b6104386105a0366004614d8a565b6112ca565b6105ad61136b565b6040516103f5939291906153ac565b6103e861137c565b61049d6105d2366004614df2565b611487565b6105ea6105e5366004614cbe565b6117e2565b6040516103f5929190615385565b610438610606366004614c49565b611888565b6103e86106193660046149d3565b6118ef565b61041b611911565b610438610634366004614b77565b611917565b610438610647366004614e6b565b6119b5565b610654611aad565b6040516103f59594939291906153da565b6104386106733660046149d3565b611b40565b610406611bf9565b61041b611c1d565b6104386106963660046149d3565b611c41565b61045d6106a9366004614dc2565b611caa565b6106b6611cd3565b6040516103f59291906151f1565b6104386106d2366004614ce6565b611ce9565b6104386106e5366004614d29565b611d80565b6104386106f8366004614ac1565b611e48565b610406611eef565b610406611f13565b610715611f22565b6040516103f591906150d6565b610438610730366004614b77565b611f2c565b61041b611f93565b610745611f99565b6040516103f5999897969594939291906154a0565b610438610768366004614e13565b611fc0565b6103e8612051565b6103e8612061565b61078561213f565b6040516103f592919061535a565b6107a66107a1366004614dc2565b612160565b6040516103f59190615278565b61041b6121d1565b61041b6107c9366004614dc2565b612237565b610438612249565b6107de612303565b6040516103f59190615265565b610406612328565b610438610801366004614ca4565b61234c565b60055460ff1681565b601f546001600160a01b031681565b60006108286123a6565b90505b90565b7f000000000000000000000000000000000000000000000000000000000000000090565b600054600160b01b900460ff16806108745750600054600160a81b900460ff16155b15610893576108816123f9565b6005805460ff1916600117905561089e565b61089e60003661242e565b565b601e546001600160a01b031681565b600054600160b01b900460ff16806108d15750600054600160a81b900460ff16155b1561099a576108de6123f9565b60408051808201909152600e81526d05265776172642065706f636820360941b60208201528161092a5760405162461bcd60e51b81526004016109219190615265565b60405180910390fd5b507f0000000000000000000000000000000000000000000000000000000000000000818161095457fe5b06600014604051806060016040528060278152602001615627602791399061098f5760405162461bcd60e51b81526004016109219190615265565b50601b8190556109a5565b6109a560003661242e565b50565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000909192565b601b547f000000000000000000000000000000000000000000000000000000000000000091565b600054600160b01b900460ff1680610a595750600054600160a81b900460ff16155b1561099a57610a666123f9565b610a718160016125b1565b6109a5565b600454606090819081908190600090610aa090600160c01b90046001600160401b03166001611487565b945094509450945094509091929394565b6007546001600160a01b031681565b600054600160b01b900460ff1680610ae25750600054600160a81b900460ff16155b1561099a57610aef6123f9565b60408051808201909152601e81527f566f746520706f77657220696e74657276616c206672616374696f6e20300000602082015281610b415760405162461bcd60e51b81526004016109219190615265565b50601c8190556109a5565b600054600160b01b900460ff1680610b6e5750600054600160a81b900460ff16155b15610b8b57610b7b6123f9565b610b86838383612630565b610b96565b610b9660003661242e565b505050565b6016546001600160a01b031690565b601d5490565b600054600160b01b900460ff1680610bd25750600054600160a81b900460ff16155b1561099a57610bdf6123f9565b602180546001600160a01b0319166001600160a01b0383161790556109a5565b602154600160a01b900460ff16606080610c176126b3565b8051909250806001600160401b0381118015610c3257600080fd5b50604051908082528060200260200182016040528015610c5c578160200160208202803683370190505b50915060005b81811015610cd75760136002016000858381518110610c7d57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900460ff16838281518110610cbf57fe5b91151560209283029190910190910152600101610c62565b5050909192565b6020546001600160a01b031681565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b600a54600b54600c54600d54600e54600f54601054601254601180546040805160208084028201810190925282815260009b8c9b8c9b8c9b8c9b8c9b8c9b60609b8d9b8c9b989a979996989597949693959294909360ff61010084048116949316929091859190830182828015610db257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610d94575b50505050509250995099509950995099509950995099509950995090919293949596979899565b6021546001600160a01b031681565b60008054600160a81b900460ff16610e0b576000546001600160a01b0316610828565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4b57600080fd5b505afa158015610e5f573d6000803e3d6000fd5b505050506040513d6020811015610e7557600080fd5b5051905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b158015610ec057600080fd5b505afa158015610ed4573d6000803e3d6000fd5b505050506040513d6020811015610eea57600080fd5b5051610f2d576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610f99576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610fef576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156110895780601f1061105e57610100808354040283529160200191611089565b820191906000526020600020905b81548152906001019060200180831161106c57829003601f168201915b5050506001600160e01b03198616600090815260016020819052604082208281559495509092506110bd9150830182614799565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106111055780518252601f1990920191602091820191016110e6565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611167576040519150601f19603f3d011682016040523d82523d6000602084013e61116c565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a16111cb81612734565b50505050565b601c5490565b60076001609c1b0181565b6111ea612751565b6001600160e01b03198116600090815260016020526040902054611255576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b03198116600090815260016020819052604082208281559190610b9690830182614799565b600581565b600054600160b01b900460ff16806112ec5750600054600160a81b900460ff16155b1561135c576112f96123f9565b60405163d0d552dd60e01b81526001600160a01b0383169063d0d552dd90611325908490600401614fe1565b600060405180830381600087803b15801561133f57600080fd5b505af1158015611353573d6000803e3d6000fd5b50505050611367565b61136760003661242e565b5050565b601854601954601a5460ff16909192565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146113ef576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333630b932903230b2b6b7b760791b604482015290519081900360640190fd5b60055460ff166114015750600061082b565b600854611415576114106127b0565b611481565b601a5460ff16801561142957504260195411155b15611436576114106128f4565b601a5460ff1615801561144b57504260095411155b1561147057611458612ee5565b611460613132565b6114686132be565b611410613415565b42601954116114815761148161355d565b50600190565b606080606080600060038054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b815250906114de5760405162461bcd60e51b81526004016109219190615265565b5060035460009088880111156114f9576003548890036114fb565b865b9050806001600160401b038111801561151357600080fd5b5060405190808252806020026020018201604052801561153d578160200160208202803683370190505b509550806001600160401b038111801561155657600080fd5b50604051908082528060200260200182016040528015611580578160200160208202803683370190505b509450806001600160401b038111801561159957600080fd5b506040519080825280602002602001820160405280156115cd57816020015b60608152602001906001900390816115b85790505b509350806001600160401b03811180156115e657600080fd5b50604051908082528060200260200182016040528015611610578160200160208202803683370190505b50925060005b818110156117c65760006003828b018154811061162f57fe5b6000918252602080832090910154808352600290915260409091205489519192506001600160c01b03169089908490811061166657fe5b6020026020010181815250506002600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b03168783815181106116b157fe5b602090810291909101810191909152600082815260028083526040918290208101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156117535780601f1061172857610100808354040283529160200191611753565b820191906000526020600020905b81548152906001019060200180831161173657829003601f168201915b505050505086838151811061176457fe5b60200260200101819052506002600082815260200190815260200160002060010160009054906101000a90046001600160a01b03168583815181106117a557fe5b6001600160a01b039092166020928302919091019091015250600101611616565b50506004549497939650919450926001600160c01b0316919050565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f8101859004850286018501909652858552909491939290919083018282801561187e5780601f106118535761010080835404028352916020019161187e565b820191906000526020600020905b81548152906001019060200180831161186157829003601f168201915b5050505050905082565b600054600160b01b900460ff16806118aa5750600054600160a81b900460ff16155b15610b8b576118b76123f9565b60005b83518110156118e9576118e18482815181106118d257fe5b60200260200101518484612630565b6001016118ba565b50610b96565b6001600160a01b03811660009081526014602052604090205460ff165b919050565b601b5481565b600054600160b01b900460ff16806119395750600054600160a81b900460ff16155b1561099a576119466123f9565b60405163984626c360e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e9063984626c390611980906013908590600401615299565b60006040518083038186803b15801561199857600080fd5b505af41580156119ac573d6000803e3d6000fd5b505050506109a5565b600054600160b01b900460ff16806119d75750600054600160a81b900460ff16155b15611a98576119e46123f9565b6000881180156119f45750600087115b8015611a005750858510155b8015611a0e57506127108411155b8015611a1c57506127108311155b8015611a285750600082115b8015611a3657506005815111155b6040518060400160405280601381526020017211dbdd8b881c185c985b5cc81a5b9d985b1a59606a1b81525090611a805760405162461bcd60e51b81526004016109219190615265565b50611a93600a898989898989898961388c565b611aa3565b611aa360003661242e565b5050505050505050565b600080600080600080611abe6123a6565b9550507f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000808702820195506001870102019250507f000000000000000000000000000000000000000000000000000000000000000082019050429091929394565b600054600160a01b900460ff1615611b96576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b600054600160b01b900460ff1680611c635750600054600160a81b900460ff16155b1561099a57611c706123f9565b604051635d9fe31360e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e90635d9fe313906119809060139085906004016152b2565b600080600080611cb985612160565b805160208201516040909201519097919650945092505050565b6016546017546001600160a01b03918216911682565b600054600160b01b900460ff1680611d0b5750600054600160a81b900460ff16155b1561135c57611d186123f9565b604051636904d3eb60e11b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e9063d209a7d690611d5490601390869086906004016152c9565b60006040518083038186803b158015611d6c57600080fd5b505af4158015611353573d6000803e3d6000fd5b611d88612751565b6001600160a01b0382166000908152601360209081526040918290205482518084019093526009835268139bdd08199bdd5b9960ba1b9183019190915260ff16611de55760405162461bcd60e51b81526004016109219190615265565b506001600160a01b03821660009081526015602052604090819020805460ff1916831515179055517f24462ede4d3e8e5a69fecec6290d42a311016ca752216d9a3d681e284791b7ac90611e3c908490849061520b565b60405180910390a15050565b611e50610ced565b6001600160a01b0316336001600160a01b031614611eac576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b611ee5611ee083836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b815250613a85565b613bb2565b6113678282613bd6565b7f000000000000000000000000000000000000000000000000000000000000000081565b6017546001600160a01b031690565b60606108286126b3565b600054600160b01b900460ff1680611f4e5750600054600160a81b900460ff16155b1561099a57611f5b6123f9565b60005b8151811015611f8d57611f85828281518110611f7657fe5b602002602001015160016125b1565b600101611f5e565b506109a5565b60095481565b600a54600b54600c54600d54600e54600f5460105460125460ff8082169161010090041689565b600054600160b01b900460ff1680611fe25750600054600160a81b900460ff16155b15610b8b57611fef6123f9565b600554604080518082019091526011815270105b1c9958591e481858dd1a5d985d1959607a1b60208201529060ff161561203c5760405162461bcd60e51b81526004016109219190615265565b50601d83905560088290556009819055610b96565b600054600160a81b900460ff1681565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146120d4576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333630b932903230b2b6b7b760791b604482015290519081900360640190fd5b602154600160a01b900460ff16612139576021805460ff60a01b1916600160a01b1790556040517f217a37a37fc40a97159886f80c3d45986e6fc4330ce6ad7283478b5e5ab705bc9061212990600190615188565b60405180910390a150600161082b565b50600090565b6004546001600160c01b03811690600160c01b90046001600160401b031682565b6121686147dd565b60085482106040518060400160405280602081526020017f5265776172642065706f6368206e6f7420696e697469616c697a656420796574815250906121c15760405162461bcd60e51b81526004016109219190615265565b506121cb82613d8c565b92915050565b6000600854600014156040518060400160405280602081526020017f5265776172642065706f6368206e6f7420696e697469616c697a6564207965748152509061222e5760405162461bcd60e51b81526004016109219190615265565b50610828613e91565b600061224282612160565b5192915050565b612251612751565b600054600160a81b900460ff16156122b0576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b60408051808201909152600b81526a233a39b7a6b0b730b3b2b960a91b602082015290565b7f000000000000000000000000000000000000000000000000000000000000000081565b612354612751565b6021805460ff60a01b1916600160a01b831515021790556040517f217a37a37fc40a97159886f80c3d45986e6fc4330ce6ad7283478b5e5ab705bc9061239b908390615188565b60405180910390a150565b60007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000004203816123f357fe5b04905090565b600054600160b01b900460ff16156124265733301461241457fe5b6000805460ff60b01b1916905561089e565b61089e612751565b612436612751565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561247e57600080fd5b505afa158015612492573d6000803e3d6000fd5b505050506040513d60208110156124a857600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061252c939285019201906147fe565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b601a546040516306b8c18560e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e916306b8c185916125f791601391600a918891889160ff16906004016152f3565b60006040518083038186803b15801561260f57600080fd5b505af4158015612623573d6000803e3d6000fd5b5050505061136782613e9b565b601a5460405163169ffdc760e21b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e91635a7ff71c9161267a91601391600a9189918991899160ff90911690600401615323565b60006040518083038186803b15801561269257600080fd5b505af41580156126a6573d6000803e3d6000fd5b50505050610b9683613e9b565b60165460408051635200305d60e11b815290516060926001600160a01b03169163a40060ba916004808301926000929190829003018186803b1580156126f857600080fd5b505afa15801561270c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108289190810190614bb1565b3d604051818101604052816000823e821561274d578181f35b8181fd5b612759610de8565b6001600160a01b0316336001600160a01b03161461089e576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b7f0000000000000000000000000000000000000000000000000000000000000000421061089e5760006127e16126b3565b80516040805160608101825243600019810182526020808301918252428385019081526008805460009081526006909352948220845181559251600180850191909155905160029093019290925583549091019092559293509091905b828110156128c65783818151811061285257fe5b60200260200101516001600160a01b031663e536f39683600001516040518263ffffffff1660e01b8152600401612889919061537c565b600060405180830381600087803b1580156128a357600080fd5b505af11580156128b7573d6000803e3d6000fd5b5050505080600101905061283e565b5050601b547f0000000000000000000000000000000000000000000000000000000000000000016009555050565b60006128fe6126b3565b8051909150801580159061291c5750602154600160a01b900460ff16155b15612dfb576007546000906001600160a01b03166129705781444260405160200161294892919061539e565b6040516020818303038152906040528051906020012060001c8161296857fe5b069050612a3e565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d89601fd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156129cb57600080fd5b505afa1580156129df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a039190614dda565b9050828142604051602001612a1992919061539e565b6040516020818303038152906040528051906020012060001c81612a3957fe5b069150505b60608060008080805b87811015612be35760008882890181612a5c57fe5b06905060008a8281518110612a6d57fe5b6020908102919091018101516001600160a01b0381166000908152601490925260409091205490915060ff1615612ac3576001600160a01b03166000908152601460205260409020805460ff1916905550612bdb565b6018546040516340462a2d60e01b81526001600160a01b038316916340462a2d91612af49190891590600401615255565b600060405180830381600087803b158015612b0e57600080fd5b505af1925050508015612b4357506040513d6000823e601f3d908101601f19168201604052612b4091908101906149f6565b60015b612bae57612b4f615529565b80612b5a5750612b6a565b612b648282613fd8565b50612ba9565b612ba9816040518060400160405280601881526020017f6572722066696e616c697a652070726963652065706f63680000000000000000815250613fd8565b612bd8565b87158015612bbd575060008351115b15612bd45791995097509550600194509250828787875b5050505b50505b600101612a47565b506000612bee613e91565b90508215612d9257601e546018546001600160a01b039091169063a9b79e1790889088908890877f0000000000000000000000000000000000000000000000000000000000000000886001612c4285614029565b03612c4c8b613d8c565b516040516001600160e01b031960e08c901b168152612c7699989796959493929190600401615059565b600060405180830381600087803b158015612c9057600080fd5b505af1925050508015612ca1575060015b612d8d57612cad615529565b80612cb85750612d0f565b7f175a1d13d190d6a1e14461c214b3ecf6118b828797750b7bffd7c4f2c1eba54c83601854604051612ceb929190614ff5565b60405180910390a1601e54612d09906001600160a01b031682614076565b50612d8d565b7f175a1d13d190d6a1e14461c214b3ecf6118b828797750b7bffd7c4f2c1eba54c82601854604051612d42929190614ff5565b60405180910390a1601e546040805180820190915260168152756572722064697374726962757465207265776172647360501b6020820152612d8d916001600160a01b031690614076565b612d9a565b612d9a61424f565b600780546001600160a01b0319166001600160a01b0384161790556040517f98b050a4042fbd1b89934ef40b9342e593f15081a348af940573a0179031f4ad90612de79084908490614ff5565b60405180910390a150505050505050612ed7565b60005b81811015612e7c576000838281518110612e1457fe5b6020908102919091018101516001600160a01b0381166000908152601490925260409091205490915060ff1615612e69576001600160a01b03166000908152601460205260409020805460ff19169055612e74565b612e72816143d1565b505b600101612dfe565b50612e8561424f565b600780546001600160a01b03191690557f98b050a4042fbd1b89934ef40b9342e593f15081a348af940573a0179031f4ad6000612ec0613e91565b604051612ece929190614ff5565b60405180910390a15b5050601a805460ff19169055565b6000612eef6126b3565b905060008151905060004290507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d89601fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612f5557600080fd5b505afa158015612f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8d9190614dda565b604051910190612fa190829060200161537c565b6040516020818303038152906040528051906020012060001c90506000601c54612fd1612fcc613e91565b613d8c565b60200151430381612fde57fe5b04905080612fea575060015b6000818381612ff557fe5b06905080613001575060015b60408051606081018252438381038252602080830191825242838501908152600880546000908152600690935294822084518155925160018085019190915590516002909301929092558354909101909255905b858110156130de5786818151811061306957fe5b60200260200101516001600160a01b031663e536f39683600001516040518263ffffffff1660e01b81526004016130a0919061537c565b600060405180830381600087803b1580156130ba57600080fd5b505af11580156130ce573d6000803e3d6000fd5b5050600190920191506130559050565b50805160208201516040517f1813f880dc24666c8b69c9d771a487ea620a27fde1514be3112847056c0c53229261311692909161539e565b60405180910390a15050601b5460098054909101905550505050565b600061313c613e91565b60105490915042035b81601d54108015613167575080613160601d54600101613d8c565b6040015111155b1561136757601e54601d54604051636b60edf760e11b81526001600160a01b039092169163d6c1dbee9161319d9160040161537c565b600060405180830381600087803b1580156131b757600080fd5b505af19250505080156131c8575060015b6132b0576131d4615529565b806131df5750613234565b7fa819a21065ad87bdde9e6d398d3213e0d3634afd87aceb7092236483f5d7ca8d601d54604051613210919061537c565b60405180910390a1601e5461322e906001600160a01b031682614076565b50611367565b7fa819a21065ad87bdde9e6d398d3213e0d3634afd87aceb7092236483f5d7ca8d601d54604051613265919061537c565b60405180910390a1601e54604080518082019091526011815270195c9c8818db1bdcd948195e1c1a5c9959607a1b60208201526132ab916001600160a01b031690614076565b611367565b601d80546001019055613145565b60006132cb601d54613d8c565b5160205460405163cbc31cf760e01b81529192506001600160a01b03169063cbc31cf7906132fd90849060040161537c565b600060405180830381600087803b15801561331757600080fd5b505af1925050508015613328575060015b6109a557613334615529565b8061333f5750613392565b7f9f874ea08c7014cce74622bfe71434f81aba7598ad65126a6aea86945bdfa18d8260405161336e919061537c565b60405180910390a160205461338c906001600160a01b031682614076565b50610a71565b7f9f874ea08c7014cce74622bfe71434f81aba7598ad65126a6aea86945bdfa18d816040516133c1919061537c565b60405180910390a1610a71602060009054906101000a90046001600160a01b0316604051806040016040528060158152602001746572722073657420636c65616e757020626c6f636b60581b815250614076565b6021546001600160a01b03161561089e576000613430613e91565b9050602160009054906101000a90046001600160a01b03166001600160a01b03166321eb1a956040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561348257600080fd5b505af1925050508015613493575060015b6109a55761349f615529565b806134aa57506134f7565b7ff7c7d6681321cc290eb89e8c96dba504436073b8bb277945cc32177e5181dd84826040516134d9919061537c565b60405180910390a160215461338c906001600160a01b031682614076565b7ff7c7d6681321cc290eb89e8c96dba504436073b8bb277945cc32177e5181dd8481604051613526919061537c565b60405180910390a160215460408051606081019091526022808252610a71926001600160a01b031691906156056020830139614076565b60125460ff16156135e857604051639ec2b58160e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639ec2b581906135b5906011906004016150c3565b600060405180830381600087803b1580156135cf57600080fd5b505af11580156135e3573d6000803e3d6000fd5b505050505b60006135f26126b3565b8051909150600081158015906136125750602154600160a01b900460ff16155b156136ab576000613624612fcc613e91565b51601f546040516237b08960e41b81529192506001600160a01b03169063037b08909061365590849060040161537c565b602060405180830381600087803b15801561366f57600080fd5b505af1158015613683573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a79190614dda565b9150505b60005b828110156138515760008482815181106136c457fe5b602090810291909101015160125490915060ff161561375757600a54600b54600c54600d54600e54600f5460405163f7dba1f560e01b81526001600160a01b0388169663f7dba1f5966137249691959094919390926011906004016153fd565b600060405180830381600087803b15801561373e57600080fd5b505af1158015613752573d6000803e3d6000fd5b505050505b806001600160a01b031663f670ebe384602160149054906101000a900460ff168061379a57506001600160a01b03841660009081526015602052604090205460ff165b6040518363ffffffff1660e01b81526004016137b7929190615255565b600060405180830381600087803b1580156137d157600080fd5b505af19250505080156137e2575060015b613848576137ee615529565b806137f95750613809565b613803828261446d565b50613848565b613848816040518060400160405280601981526020017f65727220696e69742065706f636820666f722072657665616c0000000000000081525061446d565b506001016136ae565b506012805460ff1916905560006138666123a6565b601881905590506138768161467e565b6019555050601a805460ff191660011790555050565b885488146138a75760088901805460ff191660011790558789555b868960010154146138ca5760088901805460ff1916600190811790915589018790555b858960020154146138ec5760088901805460ff19166001179055600289018690555b8489600301541461390e5760088901805460ff19166001179055600389018590555b838960040154146139305760088901805460ff19166001179055600489018490555b828960050154146139525760088901805460ff19166001179055600589018390555b818960060154146139745760088901805460ff19166001179055600689018290555b805160078a0154146139aa5780516139959060078b0190602084019061488a565b5060088901805460ff19166001179055613a69565b60005b8151811015613a67578181815181106139c257fe5b60200260200101516001600160a01b03168a60070182815481106139e257fe5b6000918252602090912001546001600160a01b031614613a5f5760088a01805460ff191660011790558151829082908110613a1957fe5b60200260200101518a6007018281548110613a3057fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6001016139ad565b505b5050506008909501805461ff0019166101001790555050505050565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b83811015613ac9578181015183820152602001613ab1565b50505050905090810190601f168015613af65780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b8651811015613b5e57868181518110613b2c57fe5b6020026020010151831415613b5657858181518110613b4757fe5b60200260200101519150613b5e565b600101613b17565b506001600160a01b038116613ba9576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b613c0a828260405180604001604052806011815260200170233a39b7a932bbb0b93226b0b730b3b2b960791b815250613a85565b601e60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613c59828260405180604001604052806006815260200165537570706c7960d01b815250613a85565b601f60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613cbf82826040518060400160405280601981526020017f436c65616e7570426c6f636b4e756d6265724d616e6167657200000000000000815250613a85565b602060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613d1482826040518060400160405280600c81526020016b4674736f526567697374727960a01b815250613a85565b601680546001600160a01b0319166001600160a01b039290921691909117905560408051808201909152601081526f2b37ba32b92bb434ba32b634b9ba32b960811b6020820152613d689083908390613a85565b601780546001600160a01b0319166001600160a01b03929092169190911790555050565b613d946147dd565b50600081815260066020908152604091829020825160608101845281548152600182015492810192909252600201549181018290529061190c5760008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a795f409866040518263ffffffff1660e01b8152600401613e1f919061537c565b60606040518083038186803b158015613e3757600080fd5b505afa158015613e4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e6f9190614e3e565b6040805160608101825293845260208401929092529082015295945050505050565b6008546000190190565b604051630bc29bcf60e21b81526001600160a01b03821690632f0a6f3c90613f2b907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906004016153c4565b600060405180830381600087803b158015613f4557600080fd5b505af1158015613f59573d6000803e3d6000fd5b505050506008546000146109a557806001600160a01b031663e536f396613f81612fcc613e91565b516040516001600160e01b031960e084901b168152613fa3919060040161537c565b600060405180830381600087803b158015613fbd57600080fd5b505af1158015613fd1573d6000803e3d6000fd5b5050505050565b7f79f4c7cc43bfb79f5a3aad0d92f75b6fed7db061bb5cc2580a01c8132711b88182601854600160405161400e93929190615226565b60405180910390a16140208282614076565b611367826143d1565b7f0000000000000000000000000000000000000000000000000000000000000000600182017f00000000000000000000000000000000000000000000000000000000000000000201919050565b6000828260405160200161408b92919061500e565b60408051601f198184030181528282528051602091820120600081815260029092529190208054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b031916919091179091559092507f1a601cf5e0efbd558b2778b7389af04741d1c49bcab104c40daa2da1945936179161411f9186918690615032565b60405180910390a1600480546001600160c01b0319811660016001600160c01b03928316810190921617909155600082815260026020526040902054600160c01b90046001600160401b031611156141775750611367565b6003805460018082019092557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01829055600082815260026020818152604090922092830180546001600160a01b0319166001600160a01b03881617905584516141e793909101918501906147fe565b50600354600091825260026020526040909120600101805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0390811684029190911791829055600480546001600160c01b03169390920416600160c01b029190911790555050565b601e546018546001600160a01b03909116906367dcac53907f0000000000000000000000000000000000000000000000000000000000000000600161429383614029565b036040518463ffffffff1660e01b81526004016142b2939291906153c4565b600060405180830381600087803b1580156142cc57600080fd5b505af19250505080156142dd575060015b61089e576142e9615529565b806142f45750614349565b7f8eb60f903ef61e0e490d7d7ba6e5b85cd949ebece7a5e5b3346eb046c041413f601854604051614325919061537c565b60405180910390a1601e54614343906001600160a01b031682614076565b506143cc565b7f8eb60f903ef61e0e490d7d7ba6e5b85cd949ebece7a5e5b3346eb046c041413f60185460405161437a919061537c565b60405180910390a1601e5460408051808201909152601b81527f6572722061636372756520756e6561726e65642072657761726473000000000060208201526143cc916001600160a01b031690614076565b61089e565b60185460405163257ea88160e11b81526001600160a01b03831691634afd5102916143ff919060040161537c565b600060405180830381600087803b15801561441957600080fd5b505af192505050801561442a575060015b6109a557614436615529565b80614441575061444b565b61338c82826146ed565b610a718160405180606001604052806021815260200161564e602191396146ed565b7f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462826144976123a6565b6040516144a5929190614ff5565b60405180910390a16144b78282614076565b602154600160a01b900460ff16806144e757506001600160a01b03821660009081526015602052604090205460ff165b15614514576001600160a01b0382166000908152601460205260409020805460ff19166001179055611367565b60405163f670ebe360e01b81526001600160a01b0383169063f670ebe39061454490600090600190600401615255565b600060405180830381600087803b15801561455e57600080fd5b505af192505050801561456f575060015b6113675761457b615529565b8061458657506145f9565b6001600160a01b0383166000908152601460205260409020805460ff191660011790557f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462836145d36123a6565b6040516145e1929190614ff5565b60405180910390a16145f38382614076565b506132ab565b6001600160a01b0382166000908152601460205260409020805460ff191660011790557f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462826146466123a6565b604051614654929190614ff5565b60405180910390a16132ab826040518060600160405280602281526020016155e360229139614076565b7f0000000000000000000000000000000000000000000000000000000000000000600182017f000000000000000000000000000000000000000000000000000000000000000002017f000000000000000000000000000000000000000000000000000000000000000001919050565b7f79f4c7cc43bfb79f5a3aad0d92f75b6fed7db061bb5cc2580a01c8132711b88182601854600260405161472393929190615226565b60405180910390a16147358282614076565b60185460405163974d7a6b60e01b81526001600160a01b0384169163974d7a6b91614763919060040161537c565b600060405180830381600087803b15801561477d57600080fd5b505af1158015614791573d6000803e3d6000fd5b505050505050565b50805460018160011615610100020316600290046000825580601f106147bf57506109a5565b601f0160209004906000526020600020908101906109a591906148df565b60405180606001604052806000815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282614834576000855561487a565b82601f1061484d57805160ff191683800117855561487a565b8280016001018555821561487a579182015b8281111561487a57825182559160200191906001019061485f565b506148869291506148df565b5090565b82805482825590600052602060002090810192821561487a579160200282015b8281111561487a57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906148aa565b5b8082111561488657600081556001016148e0565b600082601f830112614904578081fd5b8135602061491961491483615506565b6154e3565b8281528181019085830183850287018401881015614935578586fd5b855b8581101561495c57813561494a816155cd565b84529284019290840190600101614937565b5090979650505050505050565b600082601f830112614979578081fd5b8151602061498961491483615506565b82815281810190858301838502870184018810156149a5578586fd5b855b8581101561495c578151845292840192908401906001016149a7565b8035801515811461190c57600080fd5b6000602082840312156149e4578081fd5b81356149ef816155cd565b9392505050565b600080600060608486031215614a0a578182fd5b83516001600160401b0380821115614a20578384fd5b818601915086601f830112614a33578384fd5b81516020614a4361491483615506565b82815281810190858301838502870184018c1015614a5f578889fd5b8896505b84871015614a8a578051614a76816155cd565b835260019690960195918301918301614a63565b5091890151919750909350505080821115614aa3578384fd5b50614ab086828701614969565b925050604084015190509250925092565b60008060408385031215614ad3578182fd5b82356001600160401b0380821115614ae9578384fd5b818501915085601f830112614afc578384fd5b81356020614b0c61491483615506565b82815281810190858301838502870184018b1015614b28578889fd5b8896505b84871015614b4a578035835260019690960195918301918301614b2c565b5096505086013592505080821115614b60578283fd5b50614b6d858286016148f4565b9150509250929050565b600060208284031215614b88578081fd5b81356001600160401b03811115614b9d578182fd5b614ba9848285016148f4565b949350505050565b60006020808385031215614bc3578182fd5b82516001600160401b03811115614bd8578283fd5b8301601f81018513614be8578283fd5b8051614bf661491482615506565b8181528381019083850185840285018601891015614c12578687fd5b8694505b83851015614c3d578051614c29816155cd565b835260019490940193918501918501614c16565b50979650505050505050565b600080600060608486031215614c5d578081fd5b83356001600160401b03811115614c72578182fd5b614c7e868287016148f4565b935050614c8d602085016149c3565b9150614c9b604085016149c3565b90509250925092565b600060208284031215614cb5578081fd5b6149ef826149c3565b600060208284031215614ccf578081fd5b81356001600160e01b0319811681146149ef578182fd5b60008060408385031215614cf8578182fd5b8235614d03816155cd565b915060208301356001600160401b03811115614d1d578182fd5b614b6d858286016148f4565b60008060408385031215614d3b578182fd5b8235614d46816155cd565b9150614d54602084016149c3565b90509250929050565b600080600060608486031215614d71578081fd5b8335614d7c816155cd565b9250614c8d602085016149c3565b60008060408385031215614d9c578182fd5b8235614da7816155cd565b91506020830135614db7816155cd565b809150509250929050565b600060208284031215614dd3578081fd5b5035919050565b600060208284031215614deb578081fd5b5051919050565b60008060408385031215614e04578182fd5b50508035926020909101359150565b600080600060608486031215614e27578081fd5b505081359360208301359350604090920135919050565b600080600060608486031215614e52578081fd5b8351925060208401519150604084015190509250925092565b600080600080600080600080610100898b031215614e87578586fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356001600160401b03811115614ecd578182fd5b614ed98b828c016148f4565b9150509295985092959890939650565b6000815180845260208085019450808401835b83811015614f215781516001600160a01b031687529582019590820190600101614efc565b509495945050505050565b6000815480845260208085019450838352808320835b83811015614f215781546001600160a01b031687529582019560019182019101614f42565b6000815180845260208085019450808401835b83811015614f2157815187529582019590820190600101614f7a565b60008151808452815b81811015614fbb57602081850181015186830182015201614f9f565b81811115614fcc5782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0383168152604060208201819052600090614ba990830184614f96565b600060018060a01b038516825283602083015260606040830152613ba96060830184614f96565b600061012080835261506d8184018d614ee9565b90508281036020840152615081818c614f67565b604084019a909a52505060608101969096526001600160a01b0394909416608086015260a085019290925260c084015260e08301526101009091015292915050565b6000602082526149ef6020830184614f2c565b6000602082526149ef6020830184614ee9565b600060a082526150fc60a0830188614f67565b60208382038185015261510f8289614f67565b848103604086015287518082529092508183019082810284018301838a01865b8381101561515d57601f1987840301855261514b838351614f96565b9486019492509085019060010161512f565b50508681036060880152615171818a614ee9565b955050505050508260808301529695505050505050565b901515815260200190565b6000841515825260206060818401526151af6060840186614ee9565b8381036040850152845180825282860191830190845b818110156151e35783511515835292840192918401916001016151c5565b509098975050505050505050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b038416815260208101839052606081016006831061524757fe5b826040830152949350505050565b9182521515602082015260400190565b6000602082526149ef6020830184614f96565b81518152602080830151908201526040918201519181019190915260600190565b600083825260406020830152614ba96040830184614ee9565b9182526001600160a01b0316602082015260400190565b8381526001600160a01b0383166020820152606060408201819052600090613ba990830184614ee9565b94855260208501939093526001600160a01b03919091166040840152151560608301521515608082015260a00190565b95865260208601949094526001600160a01b039290921660408501521515606084015215156080830152151560a082015260c00190565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b600083825260406020830152614ba96040830184614f96565b918252602082015260400190565b92835260208301919091521515604082015260600190565b9283526020830191909152604082015260600190565b948552602085019390935260408401919091526060830152608082015260a00190565b60008882528760208301528660408301528560608301528460808301528360a083015260e060c083015261543460e0830184614f2c565b9998505050505050505050565b60006101408c83528b60208401528a60408401528960608401528860808401528760a08401528660c08401528060e084015261547f81840187614ee9565b94151561010084015250509015156101209091015298975050505050505050565b988952602089019790975260408801959095526060870193909352608086019190915260a085015260c0840152151560e083015215156101008201526101200190565b6040518181016001600160401b03811182821017156154fe57fe5b604052919050565b60006001600160401b0382111561551957fe5b5060209081020190565b60e01c90565b600060443d10156155395761082b565b600481823e6308c379a061554d8251615523565b146155575761082b565b6040513d600319016004823e80513d6001600160401b038160248401118184111715615586575050505061082b565b828401925082519150808211156155a0575050505061082b565b503d830160208284010111156155b85750505061082b565b601f01601f1916810160200160405291505090565b6001600160a01b03811681146109a557600080fdfe6572722066616c6c6261636b20696e69742065706f636820666f722072657665616c6572722063616c6c696e672075706461746541637469766556616c696461746f72735265776172642065706f6368206475726174696f6e20636f6e646974696f6e20696e76616c69646572722066616c6c6261636b2066696e616c697a652070726963652065706f6368a2646970667358221220ecf6e57ae61845a2bd7a19441a33f0535c5296d449ac52253f962fd65261ce0a64736f6c634300070600335265776172642065706f6368206475726174696f6e20636f6e646974696f6e20696e76616c69645265776172642065706f636820737461727420636f6e646974696f6e20696e76616c69640000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d290000000000000000000000001000000000000000000000000000000000000002000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b000000000000000000000000100000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062cf1e4600000000000000000000000000000000000000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000062d9a2300000000000000000000000000000000000000000000000000000000000049d400000000000000000000000000000000000000000000000000000000000000002

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106103db5760003560e01c806374e6310e1161020a578063b4dba0f311610125578063e22fdece116100b8578063f2edab5a11610087578063f2edab5a146107bb578063f5a98383146107ce578063f5f5ba72146107d6578063f937d6ad146107eb578063ff882fbb146107f3576103db565b8063e22fdece14610775578063e371aef01461077d578063e5399da314610793578063e7c830d4146107b3576103db565b8063d89c39e6116100f4578063d89c39e614610735578063e06174e41461073d578063e080a9701461075a578063e17f212e1461076d576103db565b8063b4dba0f3146106fd578063c2b0d47b14610705578063ce69f8331461070d578063d429cfe514610722576103db565b8063a10775321161019d578063a7d2acfa1161016c578063a7d2acfa146106ae578063a93a6f42146106c4578063af946af7146106d7578063b00c0b76146106ea576103db565b8063a107753214610678578063a578f55b14610680578063a670ff8714610688578063a795f4091461069b576103db565b80638de306b1116101d95780638de306b1146106265780639131205b1461063957806393a790251461064c5780639d6a890f14610665576103db565b806374e6310e146105d7578063758ff1da146105f8578063823033a91461060b57806385f3c9c91461061e576103db565b80633fdeb7e1116102fa57806360f2c5b21161028d5780636b65cc341161025c5780636b65cc34146105925780636ca051e6146105a55780636d0e8c34146105bc5780636ea0aa31146105c4576103db565b806360f2c5b21461056757806362354e031461056f57806367fc40291461057757806369b11ac61461058a576103db565b80635835cf30116102c95780635835cf30146105265780635904089a146105445780635aa6e6751461054c5780635ff2707914610554576103db565b80633fdeb7e1146104ec5780634b48dd5e146104ff5780634eac870f146105165780635267a15d1461051e576103db565b80631cb513f711610372578063361b545911610341578063361b5459146104b65780633758e679146104c957806338b5f869146104dc5780633e7ff857146104e4576103db565b80631cb513f71461046c5780632663f1b4146104825780632b3c41a4146104955780632fd8eb7d146104ae576103db565b80630f15f4c0116103ae5780630f15f4c0146104305780630f4ef8a61461043a578063132c7e1f14610442578063144e159114610455576103db565b806302fb0c5e146103e0578063047fc9aa146103fe57806308a7f402146104135780630e063d7d14610428575b600080fd5b6103e8610806565b6040516103f59190615188565b60405180910390f35b61040661080f565b6040516103f59190614fe1565b61041b61081e565b6040516103f5919061537c565b61040661082e565b610438610852565b005b6104066108a0565b610438610450366004614dc2565b6108af565b61045d6109a8565b6040516103f5939291906153c4565b610474610a10565b6040516103f592919061539e565b6104386104903660046149d3565b610a37565b61049d610a76565b6040516103f59594939291906150e9565b610406610ab1565b6104386104c4366004614dc2565b610ac0565b6104386104d7366004614d5d565b610b4c565b610406610b9b565b61041b610baa565b6104386104fa3660046149d3565b610bb0565b610507610bff565b6040516103f593929190615193565b610406610cde565b610406610ced565b61052e610d12565b6040516103f59a99989796959493929190615441565b610406610dd9565b610406610de8565b610438610562366004614cbe565b610e7c565b61041b6111d1565b6104066111d7565b610438610585366004614cbe565b6111e2565b61041b6112c5565b6104386105a0366004614d8a565b6112ca565b6105ad61136b565b6040516103f5939291906153ac565b6103e861137c565b61049d6105d2366004614df2565b611487565b6105ea6105e5366004614cbe565b6117e2565b6040516103f5929190615385565b610438610606366004614c49565b611888565b6103e86106193660046149d3565b6118ef565b61041b611911565b610438610634366004614b77565b611917565b610438610647366004614e6b565b6119b5565b610654611aad565b6040516103f59594939291906153da565b6104386106733660046149d3565b611b40565b610406611bf9565b61041b611c1d565b6104386106963660046149d3565b611c41565b61045d6106a9366004614dc2565b611caa565b6106b6611cd3565b6040516103f59291906151f1565b6104386106d2366004614ce6565b611ce9565b6104386106e5366004614d29565b611d80565b6104386106f8366004614ac1565b611e48565b610406611eef565b610406611f13565b610715611f22565b6040516103f591906150d6565b610438610730366004614b77565b611f2c565b61041b611f93565b610745611f99565b6040516103f5999897969594939291906154a0565b610438610768366004614e13565b611fc0565b6103e8612051565b6103e8612061565b61078561213f565b6040516103f592919061535a565b6107a66107a1366004614dc2565b612160565b6040516103f59190615278565b61041b6121d1565b61041b6107c9366004614dc2565b612237565b610438612249565b6107de612303565b6040516103f59190615265565b610406612328565b610438610801366004614ca4565b61234c565b60055460ff1681565b601f546001600160a01b031681565b60006108286123a6565b90505b90565b7f000000000000000000000000100000000000000000000000000000000000000390565b600054600160b01b900460ff16806108745750600054600160a81b900460ff16155b15610893576108816123f9565b6005805460ff1916600117905561089e565b61089e60003661242e565b565b601e546001600160a01b031681565b600054600160b01b900460ff16806108d15750600054600160a81b900460ff16155b1561099a576108de6123f9565b60408051808201909152600e81526d05265776172642065706f636820360941b60208201528161092a5760405162461bcd60e51b81526004016109219190615265565b60405180910390fd5b507f00000000000000000000000000000000000000000000000000000000000000b4818161095457fe5b06600014604051806060016040528060278152602001615627602791399061098f5760405162461bcd60e51b81526004016109219190615265565b50601b8190556109a5565b6109a560003661242e565b50565b7f0000000000000000000000000000000000000000000000000000000062cf1e467f00000000000000000000000000000000000000000000000000000000000000b47f000000000000000000000000000000000000000000000000000000000000005a909192565b601b547f0000000000000000000000000000000000000000000000000000000062d9a23091565b600054600160b01b900460ff1680610a595750600054600160a81b900460ff16155b1561099a57610a666123f9565b610a718160016125b1565b6109a5565b600454606090819081908190600090610aa090600160c01b90046001600160401b03166001611487565b945094509450945094509091929394565b6007546001600160a01b031681565b600054600160b01b900460ff1680610ae25750600054600160a81b900460ff16155b1561099a57610aef6123f9565b60408051808201909152601e81527f566f746520706f77657220696e74657276616c206672616374696f6e20300000602082015281610b415760405162461bcd60e51b81526004016109219190615265565b50601c8190556109a5565b600054600160b01b900460ff1680610b6e5750600054600160a81b900460ff16155b15610b8b57610b7b6123f9565b610b86838383612630565b610b96565b610b9660003661242e565b505050565b6016546001600160a01b031690565b601d5490565b600054600160b01b900460ff1680610bd25750600054600160a81b900460ff16155b1561099a57610bdf6123f9565b602180546001600160a01b0319166001600160a01b0383161790556109a5565b602154600160a01b900460ff16606080610c176126b3565b8051909250806001600160401b0381118015610c3257600080fd5b50604051908082528060200260200182016040528015610c5c578160200160208202803683370190505b50915060005b81811015610cd75760136002016000858381518110610c7d57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a900460ff16838281518110610cbf57fe5b91151560209283029190910190910152600101610c62565b5050909192565b6020546001600160a01b031681565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b600a54600b54600c54600d54600e54600f54601054601254601180546040805160208084028201810190925282815260009b8c9b8c9b8c9b8c9b8c9b8c9b60609b8d9b8c9b989a979996989597949693959294909360ff61010084048116949316929091859190830182828015610db257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610d94575b50505050509250995099509950995099509950995099509950995090919293949596979899565b6021546001600160a01b031681565b60008054600160a81b900460ff16610e0b576000546001600160a01b0316610828565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b158015610e4b57600080fd5b505afa158015610e5f573d6000803e3d6000fd5b505050506040513d6020811015610e7557600080fd5b5051905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b158015610ec057600080fd5b505afa158015610ed4573d6000803e3d6000fd5b505050506040513d6020811015610eea57600080fd5b5051610f2d576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610f99576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610fef576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156110895780601f1061105e57610100808354040283529160200191611089565b820191906000526020600020905b81548152906001019060200180831161106c57829003601f168201915b5050506001600160e01b03198616600090815260016020819052604082208281559495509092506110bd9150830182614799565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b602083106111055780518252601f1990920191602091820191016110e6565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611167576040519150601f19603f3d011682016040523d82523d6000602084013e61116c565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a16111cb81612734565b50505050565b601c5490565b60076001609c1b0181565b6111ea612751565b6001600160e01b03198116600090815260016020526040902054611255576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b03198116600090815260016020819052604082208281559190610b9690830182614799565b600581565b600054600160b01b900460ff16806112ec5750600054600160a81b900460ff16155b1561135c576112f96123f9565b60405163d0d552dd60e01b81526001600160a01b0383169063d0d552dd90611325908490600401614fe1565b600060405180830381600087803b15801561133f57600080fd5b505af1158015611353573d6000803e3d6000fd5b50505050611367565b61136760003661242e565b5050565b601854601954601a5460ff16909192565b6000336001600160a01b037f000000000000000000000000100000000000000000000000000000000000000216146113ef576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333630b932903230b2b6b7b760791b604482015290519081900360640190fd5b60055460ff166114015750600061082b565b600854611415576114106127b0565b611481565b601a5460ff16801561142957504260195411155b15611436576114106128f4565b601a5460ff1615801561144b57504260095411155b1561147057611458612ee5565b611460613132565b6114686132be565b611410613415565b42601954116114815761148161355d565b50600190565b606080606080600060038054905087106040518060400160405280601081526020016f0e6e8c2e4e840d2dcc8caf040d0d2ced60831b815250906114de5760405162461bcd60e51b81526004016109219190615265565b5060035460009088880111156114f9576003548890036114fb565b865b9050806001600160401b038111801561151357600080fd5b5060405190808252806020026020018201604052801561153d578160200160208202803683370190505b509550806001600160401b038111801561155657600080fd5b50604051908082528060200260200182016040528015611580578160200160208202803683370190505b509450806001600160401b038111801561159957600080fd5b506040519080825280602002602001820160405280156115cd57816020015b60608152602001906001900390816115b85790505b509350806001600160401b03811180156115e657600080fd5b50604051908082528060200260200182016040528015611610578160200160208202803683370190505b50925060005b818110156117c65760006003828b018154811061162f57fe5b6000918252602080832090910154808352600290915260409091205489519192506001600160c01b03169089908490811061166657fe5b6020026020010181815250506002600082815260200190815260200160002060000160189054906101000a90046001600160401b03166001600160401b03168783815181106116b157fe5b602090810291909101810191909152600082815260028083526040918290208101805483516001821615610100026000190190911692909204601f810185900485028301850190935282825290929091908301828280156117535780601f1061172857610100808354040283529160200191611753565b820191906000526020600020905b81548152906001019060200180831161173657829003601f168201915b505050505086838151811061176457fe5b60200260200101819052506002600082815260200190815260200160002060010160009054906101000a90046001600160a01b03168583815181106117a557fe5b6001600160a01b039092166020928302919091019091015250600101611616565b50506004549497939650919450926001600160c01b0316919050565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f8101859004850286018501909652858552909491939290919083018282801561187e5780601f106118535761010080835404028352916020019161187e565b820191906000526020600020905b81548152906001019060200180831161186157829003601f168201915b5050505050905082565b600054600160b01b900460ff16806118aa5750600054600160a81b900460ff16155b15610b8b576118b76123f9565b60005b83518110156118e9576118e18482815181106118d257fe5b60200260200101518484612630565b6001016118ba565b50610b96565b6001600160a01b03811660009081526014602052604090205460ff165b919050565b601b5481565b600054600160b01b900460ff16806119395750600054600160a81b900460ff16155b1561099a576119466123f9565b60405163984626c360e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e9063984626c390611980906013908590600401615299565b60006040518083038186803b15801561199857600080fd5b505af41580156119ac573d6000803e3d6000fd5b505050506109a5565b600054600160b01b900460ff16806119d75750600054600160a81b900460ff16155b15611a98576119e46123f9565b6000881180156119f45750600087115b8015611a005750858510155b8015611a0e57506127108411155b8015611a1c57506127108311155b8015611a285750600082115b8015611a3657506005815111155b6040518060400160405280601381526020017211dbdd8b881c185c985b5cc81a5b9d985b1a59606a1b81525090611a805760405162461bcd60e51b81526004016109219190615265565b50611a93600a898989898989898961388c565b611aa3565b611aa360003661242e565b5050505050505050565b600080600080600080611abe6123a6565b9550507f0000000000000000000000000000000000000000000000000000000062cf1e467f00000000000000000000000000000000000000000000000000000000000000b4808702820195506001870102019250507f000000000000000000000000000000000000000000000000000000000000005a82019050429091929394565b600054600160a01b900460ff1615611b96576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b7f000000000000000000000000100000000000000000000000000000000000000281565b7f0000000000000000000000000000000000000000000000000000000062d9a23081565b600054600160b01b900460ff1680611c635750600054600160a81b900460ff16155b1561099a57611c706123f9565b604051635d9fe31360e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e90635d9fe313906119809060139085906004016152b2565b600080600080611cb985612160565b805160208201516040909201519097919650945092505050565b6016546017546001600160a01b03918216911682565b600054600160b01b900460ff1680611d0b5750600054600160a81b900460ff16155b1561135c57611d186123f9565b604051636904d3eb60e11b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e9063d209a7d690611d5490601390869086906004016152c9565b60006040518083038186803b158015611d6c57600080fd5b505af4158015611353573d6000803e3d6000fd5b611d88612751565b6001600160a01b0382166000908152601360209081526040918290205482518084019093526009835268139bdd08199bdd5b9960ba1b9183019190915260ff16611de55760405162461bcd60e51b81526004016109219190615265565b506001600160a01b03821660009081526015602052604090819020805460ff1916831515179055517f24462ede4d3e8e5a69fecec6290d42a311016ca752216d9a3d681e284791b7ac90611e3c908490849061520b565b60405180910390a15050565b611e50610ced565b6001600160a01b0316336001600160a01b031614611eac576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b611ee5611ee083836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b815250613a85565b613bb2565b6113678282613bd6565b7f000000000000000000000000000000000000000000000000000000000000000081565b6017546001600160a01b031690565b60606108286126b3565b600054600160b01b900460ff1680611f4e5750600054600160a81b900460ff16155b1561099a57611f5b6123f9565b60005b8151811015611f8d57611f85828281518110611f7657fe5b602002602001015160016125b1565b600101611f5e565b506109a5565b60095481565b600a54600b54600c54600d54600e54600f5460105460125460ff8082169161010090041689565b600054600160b01b900460ff1680611fe25750600054600160a81b900460ff16155b15610b8b57611fef6123f9565b600554604080518082019091526011815270105b1c9958591e481858dd1a5d985d1959607a1b60208201529060ff161561203c5760405162461bcd60e51b81526004016109219190615265565b50601d83905560088290556009819055610b96565b600054600160a81b900460ff1681565b6000336001600160a01b037f000000000000000000000000100000000000000000000000000000000000000216146120d4576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333630b932903230b2b6b7b760791b604482015290519081900360640190fd5b602154600160a01b900460ff16612139576021805460ff60a01b1916600160a01b1790556040517f217a37a37fc40a97159886f80c3d45986e6fc4330ce6ad7283478b5e5ab705bc9061212990600190615188565b60405180910390a150600161082b565b50600090565b6004546001600160c01b03811690600160c01b90046001600160401b031682565b6121686147dd565b60085482106040518060400160405280602081526020017f5265776172642065706f6368206e6f7420696e697469616c697a656420796574815250906121c15760405162461bcd60e51b81526004016109219190615265565b506121cb82613d8c565b92915050565b6000600854600014156040518060400160405280602081526020017f5265776172642065706f6368206e6f7420696e697469616c697a6564207965748152509061222e5760405162461bcd60e51b81526004016109219190615265565b50610828613e91565b600061224282612160565b5192915050565b612251612751565b600054600160a81b900460ff16156122b0576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b60408051808201909152600b81526a233a39b7a6b0b730b3b2b960a91b602082015290565b7f000000000000000000000000100000000000000000000000000000000000000381565b612354612751565b6021805460ff60a01b1916600160a01b831515021790556040517f217a37a37fc40a97159886f80c3d45986e6fc4330ce6ad7283478b5e5ab705bc9061239b908390615188565b60405180910390a150565b60007f00000000000000000000000000000000000000000000000000000000000000b47f0000000000000000000000000000000000000000000000000000000062cf1e464203816123f357fe5b04905090565b600054600160b01b900460ff16156124265733301461241457fe5b6000805460ff60b01b1916905561089e565b61089e612751565b612436612751565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561247e57600080fd5b505afa158015612492573d6000803e3d6000fd5b505050506040513d60208110156124a857600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b0319861681526001602081815260409092208451815584830151805191945061252c939285019201906147fe565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b601a546040516306b8c18560e01b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e916306b8c185916125f791601391600a918891889160ff16906004016152f3565b60006040518083038186803b15801561260f57600080fd5b505af4158015612623573d6000803e3d6000fd5b5050505061136782613e9b565b601a5460405163169ffdc760e21b8152736d5d9dbbeff7e96c778ebc2f39756851fe6f624e91635a7ff71c9161267a91601391600a9189918991899160ff90911690600401615323565b60006040518083038186803b15801561269257600080fd5b505af41580156126a6573d6000803e3d6000fd5b50505050610b9683613e9b565b60165460408051635200305d60e11b815290516060926001600160a01b03169163a40060ba916004808301926000929190829003018186803b1580156126f857600080fd5b505afa15801561270c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108289190810190614bb1565b3d604051818101604052816000823e821561274d578181f35b8181fd5b612759610de8565b6001600160a01b0316336001600160a01b03161461089e576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b7f0000000000000000000000000000000000000000000000000000000062d9a230421061089e5760006127e16126b3565b80516040805160608101825243600019810182526020808301918252428385019081526008805460009081526006909352948220845181559251600180850191909155905160029093019290925583549091019092559293509091905b828110156128c65783818151811061285257fe5b60200260200101516001600160a01b031663e536f39683600001516040518263ffffffff1660e01b8152600401612889919061537c565b600060405180830381600087803b1580156128a357600080fd5b505af11580156128b7573d6000803e3d6000fd5b5050505080600101905061283e565b5050601b547f0000000000000000000000000000000000000000000000000000000062d9a230016009555050565b60006128fe6126b3565b8051909150801580159061291c5750602154600160a01b900460ff16155b15612dfb576007546000906001600160a01b03166129705781444260405160200161294892919061539e565b6040516020818303038152906040528051906020012060001c8161296857fe5b069050612a3e565b60007f00000000000000000000000010000000000000000000000000000000000000036001600160a01b031663d89601fd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156129cb57600080fd5b505afa1580156129df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a039190614dda565b9050828142604051602001612a1992919061539e565b6040516020818303038152906040528051906020012060001c81612a3957fe5b069150505b60608060008080805b87811015612be35760008882890181612a5c57fe5b06905060008a8281518110612a6d57fe5b6020908102919091018101516001600160a01b0381166000908152601490925260409091205490915060ff1615612ac3576001600160a01b03166000908152601460205260409020805460ff1916905550612bdb565b6018546040516340462a2d60e01b81526001600160a01b038316916340462a2d91612af49190891590600401615255565b600060405180830381600087803b158015612b0e57600080fd5b505af1925050508015612b4357506040513d6000823e601f3d908101601f19168201604052612b4091908101906149f6565b60015b612bae57612b4f615529565b80612b5a5750612b6a565b612b648282613fd8565b50612ba9565b612ba9816040518060400160405280601881526020017f6572722066696e616c697a652070726963652065706f63680000000000000000815250613fd8565b612bd8565b87158015612bbd575060008351115b15612bd45791995097509550600194509250828787875b5050505b50505b600101612a47565b506000612bee613e91565b90508215612d9257601e546018546001600160a01b039091169063a9b79e1790889088908890877f00000000000000000000000000000000000000000000000000000000000000b4886001612c4285614029565b03612c4c8b613d8c565b516040516001600160e01b031960e08c901b168152612c7699989796959493929190600401615059565b600060405180830381600087803b158015612c9057600080fd5b505af1925050508015612ca1575060015b612d8d57612cad615529565b80612cb85750612d0f565b7f175a1d13d190d6a1e14461c214b3ecf6118b828797750b7bffd7c4f2c1eba54c83601854604051612ceb929190614ff5565b60405180910390a1601e54612d09906001600160a01b031682614076565b50612d8d565b7f175a1d13d190d6a1e14461c214b3ecf6118b828797750b7bffd7c4f2c1eba54c82601854604051612d42929190614ff5565b60405180910390a1601e546040805180820190915260168152756572722064697374726962757465207265776172647360501b6020820152612d8d916001600160a01b031690614076565b612d9a565b612d9a61424f565b600780546001600160a01b0319166001600160a01b0384161790556040517f98b050a4042fbd1b89934ef40b9342e593f15081a348af940573a0179031f4ad90612de79084908490614ff5565b60405180910390a150505050505050612ed7565b60005b81811015612e7c576000838281518110612e1457fe5b6020908102919091018101516001600160a01b0381166000908152601490925260409091205490915060ff1615612e69576001600160a01b03166000908152601460205260409020805460ff19169055612e74565b612e72816143d1565b505b600101612dfe565b50612e8561424f565b600780546001600160a01b03191690557f98b050a4042fbd1b89934ef40b9342e593f15081a348af940573a0179031f4ad6000612ec0613e91565b604051612ece929190614ff5565b60405180910390a15b5050601a805460ff19169055565b6000612eef6126b3565b905060008151905060004290507f00000000000000000000000010000000000000000000000000000000000000036001600160a01b031663d89601fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612f5557600080fd5b505afa158015612f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8d9190614dda565b604051910190612fa190829060200161537c565b6040516020818303038152906040528051906020012060001c90506000601c54612fd1612fcc613e91565b613d8c565b60200151430381612fde57fe5b04905080612fea575060015b6000818381612ff557fe5b06905080613001575060015b60408051606081018252438381038252602080830191825242838501908152600880546000908152600690935294822084518155925160018085019190915590516002909301929092558354909101909255905b858110156130de5786818151811061306957fe5b60200260200101516001600160a01b031663e536f39683600001516040518263ffffffff1660e01b81526004016130a0919061537c565b600060405180830381600087803b1580156130ba57600080fd5b505af11580156130ce573d6000803e3d6000fd5b5050600190920191506130559050565b50805160208201516040517f1813f880dc24666c8b69c9d771a487ea620a27fde1514be3112847056c0c53229261311692909161539e565b60405180910390a15050601b5460098054909101905550505050565b600061313c613e91565b60105490915042035b81601d54108015613167575080613160601d54600101613d8c565b6040015111155b1561136757601e54601d54604051636b60edf760e11b81526001600160a01b039092169163d6c1dbee9161319d9160040161537c565b600060405180830381600087803b1580156131b757600080fd5b505af19250505080156131c8575060015b6132b0576131d4615529565b806131df5750613234565b7fa819a21065ad87bdde9e6d398d3213e0d3634afd87aceb7092236483f5d7ca8d601d54604051613210919061537c565b60405180910390a1601e5461322e906001600160a01b031682614076565b50611367565b7fa819a21065ad87bdde9e6d398d3213e0d3634afd87aceb7092236483f5d7ca8d601d54604051613265919061537c565b60405180910390a1601e54604080518082019091526011815270195c9c8818db1bdcd948195e1c1a5c9959607a1b60208201526132ab916001600160a01b031690614076565b611367565b601d80546001019055613145565b60006132cb601d54613d8c565b5160205460405163cbc31cf760e01b81529192506001600160a01b03169063cbc31cf7906132fd90849060040161537c565b600060405180830381600087803b15801561331757600080fd5b505af1925050508015613328575060015b6109a557613334615529565b8061333f5750613392565b7f9f874ea08c7014cce74622bfe71434f81aba7598ad65126a6aea86945bdfa18d8260405161336e919061537c565b60405180910390a160205461338c906001600160a01b031682614076565b50610a71565b7f9f874ea08c7014cce74622bfe71434f81aba7598ad65126a6aea86945bdfa18d816040516133c1919061537c565b60405180910390a1610a71602060009054906101000a90046001600160a01b0316604051806040016040528060158152602001746572722073657420636c65616e757020626c6f636b60581b815250614076565b6021546001600160a01b03161561089e576000613430613e91565b9050602160009054906101000a90046001600160a01b03166001600160a01b03166321eb1a956040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561348257600080fd5b505af1925050508015613493575060015b6109a55761349f615529565b806134aa57506134f7565b7ff7c7d6681321cc290eb89e8c96dba504436073b8bb277945cc32177e5181dd84826040516134d9919061537c565b60405180910390a160215461338c906001600160a01b031682614076565b7ff7c7d6681321cc290eb89e8c96dba504436073b8bb277945cc32177e5181dd8481604051613526919061537c565b60405180910390a160215460408051606081019091526022808252610a71926001600160a01b031691906156056020830139614076565b60125460ff16156135e857604051639ec2b58160e01b81526001600160a01b037f00000000000000000000000010000000000000000000000000000000000000031690639ec2b581906135b5906011906004016150c3565b600060405180830381600087803b1580156135cf57600080fd5b505af11580156135e3573d6000803e3d6000fd5b505050505b60006135f26126b3565b8051909150600081158015906136125750602154600160a01b900460ff16155b156136ab576000613624612fcc613e91565b51601f546040516237b08960e41b81529192506001600160a01b03169063037b08909061365590849060040161537c565b602060405180830381600087803b15801561366f57600080fd5b505af1158015613683573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136a79190614dda565b9150505b60005b828110156138515760008482815181106136c457fe5b602090810291909101015160125490915060ff161561375757600a54600b54600c54600d54600e54600f5460405163f7dba1f560e01b81526001600160a01b0388169663f7dba1f5966137249691959094919390926011906004016153fd565b600060405180830381600087803b15801561373e57600080fd5b505af1158015613752573d6000803e3d6000fd5b505050505b806001600160a01b031663f670ebe384602160149054906101000a900460ff168061379a57506001600160a01b03841660009081526015602052604090205460ff165b6040518363ffffffff1660e01b81526004016137b7929190615255565b600060405180830381600087803b1580156137d157600080fd5b505af19250505080156137e2575060015b613848576137ee615529565b806137f95750613809565b613803828261446d565b50613848565b613848816040518060400160405280601981526020017f65727220696e69742065706f636820666f722072657665616c0000000000000081525061446d565b506001016136ae565b506012805460ff1916905560006138666123a6565b601881905590506138768161467e565b6019555050601a805460ff191660011790555050565b885488146138a75760088901805460ff191660011790558789555b868960010154146138ca5760088901805460ff1916600190811790915589018790555b858960020154146138ec5760088901805460ff19166001179055600289018690555b8489600301541461390e5760088901805460ff19166001179055600389018590555b838960040154146139305760088901805460ff19166001179055600489018490555b828960050154146139525760088901805460ff19166001179055600589018390555b818960060154146139745760088901805460ff19166001179055600689018290555b805160078a0154146139aa5780516139959060078b0190602084019061488a565b5060088901805460ff19166001179055613a69565b60005b8151811015613a67578181815181106139c257fe5b60200260200101516001600160a01b03168a60070182815481106139e257fe5b6000918252602090912001546001600160a01b031614613a5f5760088a01805460ff191660011790558151829082908110613a1957fe5b60200260200101518a6007018281548110613a3057fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6001016139ad565b505b5050506008909501805461ff0019166101001790555050505050565b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b83811015613ac9578181015183820152602001613ab1565b50505050905090810190601f168015613af65780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b8651811015613b5e57868181518110613b2c57fe5b6020026020010151831415613b5657858181518110613b4757fe5b60200260200101519150613b5e565b600101613b17565b506001600160a01b038116613ba9576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b613c0a828260405180604001604052806011815260200170233a39b7a932bbb0b93226b0b730b3b2b960791b815250613a85565b601e60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613c59828260405180604001604052806006815260200165537570706c7960d01b815250613a85565b601f60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613cbf82826040518060400160405280601981526020017f436c65616e7570426c6f636b4e756d6265724d616e6167657200000000000000815250613a85565b602060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613d1482826040518060400160405280600c81526020016b4674736f526567697374727960a01b815250613a85565b601680546001600160a01b0319166001600160a01b039290921691909117905560408051808201909152601081526f2b37ba32b92bb434ba32b634b9ba32b960811b6020820152613d689083908390613a85565b601780546001600160a01b0319166001600160a01b03929092169190911790555050565b613d946147dd565b50600081815260066020908152604091829020825160608101845281548152600182015492810192909252600201549181018290529061190c5760008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a795f409866040518263ffffffff1660e01b8152600401613e1f919061537c565b60606040518083038186803b158015613e3757600080fd5b505afa158015613e4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e6f9190614e3e565b6040805160608101825293845260208401929092529082015295945050505050565b6008546000190190565b604051630bc29bcf60e21b81526001600160a01b03821690632f0a6f3c90613f2b907f0000000000000000000000000000000000000000000000000000000062cf1e46907f00000000000000000000000000000000000000000000000000000000000000b4907f000000000000000000000000000000000000000000000000000000000000005a906004016153c4565b600060405180830381600087803b158015613f4557600080fd5b505af1158015613f59573d6000803e3d6000fd5b505050506008546000146109a557806001600160a01b031663e536f396613f81612fcc613e91565b516040516001600160e01b031960e084901b168152613fa3919060040161537c565b600060405180830381600087803b158015613fbd57600080fd5b505af1158015613fd1573d6000803e3d6000fd5b5050505050565b7f79f4c7cc43bfb79f5a3aad0d92f75b6fed7db061bb5cc2580a01c8132711b88182601854600160405161400e93929190615226565b60405180910390a16140208282614076565b611367826143d1565b7f0000000000000000000000000000000000000000000000000000000062cf1e46600182017f00000000000000000000000000000000000000000000000000000000000000b40201919050565b6000828260405160200161408b92919061500e565b60408051601f198184030181528282528051602091820120600081815260029092529190208054436001600160c01b038181166001600160401b03600160c01b80860482166001019091160291909316176001600160c01b031916919091179091559092507f1a601cf5e0efbd558b2778b7389af04741d1c49bcab104c40daa2da1945936179161411f9186918690615032565b60405180910390a1600480546001600160c01b0319811660016001600160c01b03928316810190921617909155600082815260026020526040902054600160c01b90046001600160401b031611156141775750611367565b6003805460018082019092557fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b01829055600082815260026020818152604090922092830180546001600160a01b0319166001600160a01b03881617905584516141e793909101918501906147fe565b50600354600091825260026020526040909120600101805467ffffffffffffffff60a01b1916600160a01b6000199093016001600160401b0390811684029190911791829055600480546001600160c01b03169390920416600160c01b029190911790555050565b601e546018546001600160a01b03909116906367dcac53907f00000000000000000000000000000000000000000000000000000000000000b4600161429383614029565b036040518463ffffffff1660e01b81526004016142b2939291906153c4565b600060405180830381600087803b1580156142cc57600080fd5b505af19250505080156142dd575060015b61089e576142e9615529565b806142f45750614349565b7f8eb60f903ef61e0e490d7d7ba6e5b85cd949ebece7a5e5b3346eb046c041413f601854604051614325919061537c565b60405180910390a1601e54614343906001600160a01b031682614076565b506143cc565b7f8eb60f903ef61e0e490d7d7ba6e5b85cd949ebece7a5e5b3346eb046c041413f60185460405161437a919061537c565b60405180910390a1601e5460408051808201909152601b81527f6572722061636372756520756e6561726e65642072657761726473000000000060208201526143cc916001600160a01b031690614076565b61089e565b60185460405163257ea88160e11b81526001600160a01b03831691634afd5102916143ff919060040161537c565b600060405180830381600087803b15801561441957600080fd5b505af192505050801561442a575060015b6109a557614436615529565b80614441575061444b565b61338c82826146ed565b610a718160405180606001604052806021815260200161564e602191396146ed565b7f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462826144976123a6565b6040516144a5929190614ff5565b60405180910390a16144b78282614076565b602154600160a01b900460ff16806144e757506001600160a01b03821660009081526015602052604090205460ff165b15614514576001600160a01b0382166000908152601460205260409020805460ff19166001179055611367565b60405163f670ebe360e01b81526001600160a01b0383169063f670ebe39061454490600090600190600401615255565b600060405180830381600087803b15801561455e57600080fd5b505af192505050801561456f575060015b6113675761457b615529565b8061458657506145f9565b6001600160a01b0383166000908152601460205260409020805460ff191660011790557f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462836145d36123a6565b6040516145e1929190614ff5565b60405180910390a16145f38382614076565b506132ab565b6001600160a01b0382166000908152601460205260409020805460ff191660011790557f61156899176547b8075bfa81fa2996c6057ce9c71320884b11c0179d9dc2e462826146466123a6565b604051614654929190614ff5565b60405180910390a16132ab826040518060600160405280602281526020016155e360229139614076565b7f0000000000000000000000000000000000000000000000000000000062cf1e46600182017f00000000000000000000000000000000000000000000000000000000000000b402017f000000000000000000000000000000000000000000000000000000000000005a01919050565b7f79f4c7cc43bfb79f5a3aad0d92f75b6fed7db061bb5cc2580a01c8132711b88182601854600260405161472393929190615226565b60405180910390a16147358282614076565b60185460405163974d7a6b60e01b81526001600160a01b0384169163974d7a6b91614763919060040161537c565b600060405180830381600087803b15801561477d57600080fd5b505af1158015614791573d6000803e3d6000fd5b505050505050565b50805460018160011615610100020316600290046000825580601f106147bf57506109a5565b601f0160209004906000526020600020908101906109a591906148df565b60405180606001604052806000815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282614834576000855561487a565b82601f1061484d57805160ff191683800117855561487a565b8280016001018555821561487a579182015b8281111561487a57825182559160200191906001019061485f565b506148869291506148df565b5090565b82805482825590600052602060002090810192821561487a579160200282015b8281111561487a57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906148aa565b5b8082111561488657600081556001016148e0565b600082601f830112614904578081fd5b8135602061491961491483615506565b6154e3565b8281528181019085830183850287018401881015614935578586fd5b855b8581101561495c57813561494a816155cd565b84529284019290840190600101614937565b5090979650505050505050565b600082601f830112614979578081fd5b8151602061498961491483615506565b82815281810190858301838502870184018810156149a5578586fd5b855b8581101561495c578151845292840192908401906001016149a7565b8035801515811461190c57600080fd5b6000602082840312156149e4578081fd5b81356149ef816155cd565b9392505050565b600080600060608486031215614a0a578182fd5b83516001600160401b0380821115614a20578384fd5b818601915086601f830112614a33578384fd5b81516020614a4361491483615506565b82815281810190858301838502870184018c1015614a5f578889fd5b8896505b84871015614a8a578051614a76816155cd565b835260019690960195918301918301614a63565b5091890151919750909350505080821115614aa3578384fd5b50614ab086828701614969565b925050604084015190509250925092565b60008060408385031215614ad3578182fd5b82356001600160401b0380821115614ae9578384fd5b818501915085601f830112614afc578384fd5b81356020614b0c61491483615506565b82815281810190858301838502870184018b1015614b28578889fd5b8896505b84871015614b4a578035835260019690960195918301918301614b2c565b5096505086013592505080821115614b60578283fd5b50614b6d858286016148f4565b9150509250929050565b600060208284031215614b88578081fd5b81356001600160401b03811115614b9d578182fd5b614ba9848285016148f4565b949350505050565b60006020808385031215614bc3578182fd5b82516001600160401b03811115614bd8578283fd5b8301601f81018513614be8578283fd5b8051614bf661491482615506565b8181528381019083850185840285018601891015614c12578687fd5b8694505b83851015614c3d578051614c29816155cd565b835260019490940193918501918501614c16565b50979650505050505050565b600080600060608486031215614c5d578081fd5b83356001600160401b03811115614c72578182fd5b614c7e868287016148f4565b935050614c8d602085016149c3565b9150614c9b604085016149c3565b90509250925092565b600060208284031215614cb5578081fd5b6149ef826149c3565b600060208284031215614ccf578081fd5b81356001600160e01b0319811681146149ef578182fd5b60008060408385031215614cf8578182fd5b8235614d03816155cd565b915060208301356001600160401b03811115614d1d578182fd5b614b6d858286016148f4565b60008060408385031215614d3b578182fd5b8235614d46816155cd565b9150614d54602084016149c3565b90509250929050565b600080600060608486031215614d71578081fd5b8335614d7c816155cd565b9250614c8d602085016149c3565b60008060408385031215614d9c578182fd5b8235614da7816155cd565b91506020830135614db7816155cd565b809150509250929050565b600060208284031215614dd3578081fd5b5035919050565b600060208284031215614deb578081fd5b5051919050565b60008060408385031215614e04578182fd5b50508035926020909101359150565b600080600060608486031215614e27578081fd5b505081359360208301359350604090920135919050565b600080600060608486031215614e52578081fd5b8351925060208401519150604084015190509250925092565b600080600080600080600080610100898b031215614e87578586fd5b883597506020890135965060408901359550606089013594506080890135935060a0890135925060c0890135915060e08901356001600160401b03811115614ecd578182fd5b614ed98b828c016148f4565b9150509295985092959890939650565b6000815180845260208085019450808401835b83811015614f215781516001600160a01b031687529582019590820190600101614efc565b509495945050505050565b6000815480845260208085019450838352808320835b83811015614f215781546001600160a01b031687529582019560019182019101614f42565b6000815180845260208085019450808401835b83811015614f2157815187529582019590820190600101614f7a565b60008151808452815b81811015614fbb57602081850181015186830182015201614f9f565b81811115614fcc5782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0383168152604060208201819052600090614ba990830184614f96565b600060018060a01b038516825283602083015260606040830152613ba96060830184614f96565b600061012080835261506d8184018d614ee9565b90508281036020840152615081818c614f67565b604084019a909a52505060608101969096526001600160a01b0394909416608086015260a085019290925260c084015260e08301526101009091015292915050565b6000602082526149ef6020830184614f2c565b6000602082526149ef6020830184614ee9565b600060a082526150fc60a0830188614f67565b60208382038185015261510f8289614f67565b848103604086015287518082529092508183019082810284018301838a01865b8381101561515d57601f1987840301855261514b838351614f96565b9486019492509085019060010161512f565b50508681036060880152615171818a614ee9565b955050505050508260808301529695505050505050565b901515815260200190565b6000841515825260206060818401526151af6060840186614ee9565b8381036040850152845180825282860191830190845b818110156151e35783511515835292840192918401916001016151c5565b509098975050505050505050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b038416815260208101839052606081016006831061524757fe5b826040830152949350505050565b9182521515602082015260400190565b6000602082526149ef6020830184614f96565b81518152602080830151908201526040918201519181019190915260600190565b600083825260406020830152614ba96040830184614ee9565b9182526001600160a01b0316602082015260400190565b8381526001600160a01b0383166020820152606060408201819052600090613ba990830184614ee9565b94855260208501939093526001600160a01b03919091166040840152151560608301521515608082015260a00190565b95865260208601949094526001600160a01b039290921660408501521515606084015215156080830152151560a082015260c00190565b6001600160c01b039290921682526001600160401b0316602082015260400190565b90815260200190565b600083825260406020830152614ba96040830184614f96565b918252602082015260400190565b92835260208301919091521515604082015260600190565b9283526020830191909152604082015260600190565b948552602085019390935260408401919091526060830152608082015260a00190565b60008882528760208301528660408301528560608301528460808301528360a083015260e060c083015261543460e0830184614f2c565b9998505050505050505050565b60006101408c83528b60208401528a60408401528960608401528860808401528760a08401528660c08401528060e084015261547f81840187614ee9565b94151561010084015250509015156101209091015298975050505050505050565b988952602089019790975260408801959095526060870193909352608086019190915260a085015260c0840152151560e083015215156101008201526101200190565b6040518181016001600160401b03811182821017156154fe57fe5b604052919050565b60006001600160401b0382111561551957fe5b5060209081020190565b60e01c90565b600060443d10156155395761082b565b600481823e6308c379a061554d8251615523565b146155575761082b565b6040513d600319016004823e80513d6001600160401b038160248401118184111715615586575050505061082b565b828401925082519150808211156155a0575050505061082b565b503d830160208284010111156155b85750505061082b565b601f01601f1916810160200160405291505090565b6001600160a01b03811681146109a557600080fdfe6572722066616c6c6261636b20696e69742065706f636820666f722072657665616c6572722063616c6c696e672075706461746541637469766556616c696461746f72735265776172642065706f6368206475726174696f6e20636f6e646974696f6e20696e76616c69646572722066616c6c6261636b2066696e616c697a652070726963652065706f6368a2646970667358221220ecf6e57ae61845a2bd7a19441a33f0535c5296d449ac52253f962fd65261ce0a64736f6c63430007060033