false
false
0

Contract Address Details

0x0F45493e4C321b238e1fA242692BFFf3f30fBdfD

Contract Name
VoterWhitelister
Creator
0x4598a6–d10d29 at 0x144fd5–d0ebcd
Balance
0 FLR
Tokens
Fetching tokens...
Transactions
7,769 Transactions
Transfers
0 Transfers
Gas Used
7,780,014,597
Last Balance Update
23034993
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
VoterWhitelister




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




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

Constructor Arguments

0000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d29000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b00000000000000000000000010000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064

Arg [0] (address) : 0x4598a6c05910ab914f0cbaaca1911cd337d10d29
Arg [1] (address) : 0xbaf89d873d198ff78e72d2745b01cba3c6e5be6b
Arg [2] (address) : 0x1000000000000000000000000000000000000003
Arg [3] (uint256) : 100

              

./contracts/utils/implementation/VoterWhitelister.sol

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

import "../interface/IIVoterWhitelister.sol";
import "../../addressUpdater/implementation/AddressUpdatable.sol";
import "../../genesis/interface/IIPriceSubmitter.sol";
import "../../governance/implementation/Governed.sol";
import "../../token/interface/IIVPToken.sol";
import "../../userInterfaces/IFtsoRegistry.sol";
import "../../utils/implementation/SafePct.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";


contract VoterWhitelister is IIVoterWhitelister, Governed, AddressUpdatable {
    using SafeMath for uint256;
    using SafePct for uint256;

    uint256 internal constant TERA = 10 ** 12;                    // 10^12
    uint256 internal constant BIPS100 = 10 ** 4;                  // 100 * 100%

    uint256 public override defaultMaxVotersForFtso;
        
    /**
     * Maximum number of voters in a single ftso whitelist.
     * Adjustable separately for each ftsoIndex.
     */
    mapping (uint256 => uint256) public override maxVotersForFtso;
    
    // mapping: ftsoIndex => array of whitelisted voters for this ftso
    mapping (uint256 => address[]) internal whitelist;
    
    IIPriceSubmitter public immutable priceSubmitter;
    
    IFtsoRegistry public ftsoRegistry;

    address public ftsoManager;

    modifier onlyFtsoManager {
        require(msg.sender == ftsoManager, "only ftso manager");
        _;
    }
    
    constructor(
        address _governance,
        address _addressUpdater,
        IIPriceSubmitter _priceSubmitter,
        uint256 _defaultMaxVotersForFtso
    )
        Governed(_governance) AddressUpdatable(_addressUpdater)
    {
        priceSubmitter = _priceSubmitter;
        defaultMaxVotersForFtso = _defaultMaxVotersForFtso;
    }
    
    /**
     * 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 override 
        returns (
            uint256[] memory _supportedIndices,
            bool[] memory _success
        )
    {
        if (_isTrustedAddress(_voter)) {
            revert("trusted address");
        }

        _supportedIndices = ftsoRegistry.getSupportedIndices();
        uint256 len = _supportedIndices.length;
        _success = new bool[](len);
        for (uint256 i = 0; i < len; i++) {
            _success[i] = _requestWhitelistingVoter(_voter, _supportedIndices[i]);
        }
    }
    
    /**
     * 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 override {
        if (_isTrustedAddress(_voter)) {
            revert("trusted address");
        }
        
        bool success = _requestWhitelistingVoter(_voter, _ftsoIndex);
        require(success, "vote power too low");
    }

    /**
     * Set the maximum number of voters in the whitelist for FTSO at index `_ftsoIndex`.
     * Calling this function might remove several voters with the least votepower from the whitelist.
     */
    function setMaxVotersForFtso(uint256 _ftsoIndex, uint256 _newMaxVoters) external override onlyGovernance {
        maxVotersForFtso[_ftsoIndex] = _newMaxVoters;
        // need to remove any?
        address[] storage addressesForFtso = whitelist[_ftsoIndex];
        if (_newMaxVoters >= addressesForFtso.length) {
            return;
        }
        // remove voters with minimum vote power
        IIFtso ftso = ftsoRegistry.getFtso(_ftsoIndex);
        uint256[] memory votePowers = _getVotePowerWeights(ftso, addressesForFtso);
        uint256 length = votePowers.length;
        uint256 toRemove = length - _newMaxVoters;
        address[] memory removedVoters = new address[](toRemove);
        for (uint256 n = 0; n < toRemove; n++) {
            uint256 minIndex = _findMinimum(votePowers, length);
            removedVoters[n] = addressesForFtso[minIndex];
            if (minIndex < length - 1) {
                addressesForFtso[minIndex] = addressesForFtso[length - 1];
                votePowers[minIndex] = votePowers[length - 1];
            }
            addressesForFtso.pop();
            --length;
        }
        _votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
    }
    
    /**
     * Set the maximum number of voters in the whitelist for a new FTSO.
     */
    function setDefaultMaxVotersForFtso(uint256 _defaultMaxVotersForFtso) external override onlyGovernance {
        defaultMaxVotersForFtso = _defaultMaxVotersForFtso;
    }
    
    /**
     * Create whitelist with default size for ftso.
     */
    function addFtso(uint256 _ftsoIndex) external override onlyFtsoManager {
        require(maxVotersForFtso[_ftsoIndex] == 0, "whitelist already exist");
        maxVotersForFtso[_ftsoIndex] = defaultMaxVotersForFtso;
    }
    
    /**
     * Clear whitelist for ftso at `_ftsoIndex`.
     */
    function removeFtso(uint256 _ftsoIndex) external override onlyFtsoManager {
        _votersRemovedFromWhitelist(whitelist[_ftsoIndex], _ftsoIndex);
        delete whitelist[_ftsoIndex];
        delete maxVotersForFtso[_ftsoIndex];
    }

    /**
     * Remove `_trustedAddress` from whitelist for ftso at `_ftsoIndex`.
     */
    function removeTrustedAddressFromWhitelist(address _trustedAddress, uint256 _ftsoIndex) external override {
        if (!_isTrustedAddress(_trustedAddress)) {
            revert("not trusted address");
        }
        address[] storage addressesForFtso = whitelist[_ftsoIndex];
        uint256 length = addressesForFtso.length;

        // find index of _trustedAddress
        uint256 index = 0;
        for ( ; index < length; index++) {
            if (addressesForFtso[index] == _trustedAddress) {
                break;
            }
        }

        require(index < length, "trusted address not whitelisted");
        
        // kick the index out and replace it with the last one
        address[] memory removedVoters = new address[](1);
        removedVoters[0] = addressesForFtso[index];
        addressesForFtso[index] = addressesForFtso[length - 1];
        addressesForFtso.pop();
        _votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
    }

    /**
     * Get whitelisted price providers for ftso with `_symbol`
     */
    function getFtsoWhitelistedPriceProvidersBySymbol(
        string memory _symbol
    ) 
        external view override 
        returns (
            address[] memory
    ) 
    {
        uint256 ftsoIndex = ftsoRegistry.getFtsoIndex(_symbol);
        return getFtsoWhitelistedPriceProviders(ftsoIndex);
    }

    /**
     * Get whitelisted price providers for ftso at `_ftsoIndex`
     */
    function getFtsoWhitelistedPriceProviders(uint256 _ftsoIndex) public view override returns (address[] memory) {
        uint256 maxVoters = maxVotersForFtso[_ftsoIndex];
        require(maxVoters > 0, "FTSO index not supported");
        return whitelist[_ftsoIndex];
    }

    /**
     * @notice Implementation of the AddressUpdatable abstract method.
     */
    function _updateContractAddresses(
        bytes32[] memory _contractNameHashes,
        address[] memory _contractAddresses
    )
        internal override
    {
        ftsoRegistry = IFtsoRegistry(_getContractAddress(_contractNameHashes, _contractAddresses, "FtsoRegistry"));
        ftsoManager = _getContractAddress(_contractNameHashes, _contractAddresses, "FtsoManager");
    }

    /**
     * Request to whitelist `_voter` account to ftso at `_ftsoIndex` - implementation.
     */
    function _requestWhitelistingVoter(address _voter, uint256 _ftsoIndex) internal returns(bool) {
        uint256 maxVoters = maxVotersForFtso[_ftsoIndex];
        require(maxVoters > 0, "FTSO index not supported");
        
        address[] storage addressesForFtso = whitelist[_ftsoIndex];
        uint256 length = addressesForFtso.length;
        
        // copy to memory and check if it contains _voter
        address[] memory addresses = new address[](length + 1);
        for (uint256 i = 0; i < length; i++) {
            address addr = addressesForFtso[i];
            if (addr == _voter) {
                // _voter is already whitelisted, return
                return true;
            }
            addresses[i] = addr;
        }
        addresses[length] = _voter;
        
        // can we just add a new one?
        if (length < maxVoters) {
            addressesForFtso.push(_voter);
            _voterWhitelisted(_voter, _ftsoIndex);
            return true;
        }
        
        // find a candidate to kick out
        uint256 minIndex = _minVotePowerIndex(addresses, _ftsoIndex);
        if (minIndex == length) {
            // the new _voter has the minimum vote power, do nothing
            return false;
        }
        
        // kick the minIndex out and replace it with _voter
        address[] memory removedVoters = new address[](1);
        removedVoters[0] = addresses[minIndex];
        addressesForFtso[minIndex] = _voter;
        _votersRemovedFromWhitelist(removedVoters, _ftsoIndex);
        _voterWhitelisted(_voter, _ftsoIndex);

        return true;
    }
    
    /**
     * Find index of the element with minimum vote power weight.
     * In case of a tie, returns later index.
     */
    function _minVotePowerIndex(address[] memory _addresses, uint256 _ftsoIndex) internal returns (uint256) {
        IIFtso ftso = ftsoRegistry.getFtso(_ftsoIndex);
        uint256[] memory votePowers = _getVotePowerWeights(ftso, _addresses);
        return _findMinimum(votePowers, votePowers.length);
    }
    
    /**
     * Calculate vote power weights like FTSO.
     * Unlike FTSO, it calls VPToken vote power in a batch to limit gas consumption.
     * Another difference with FTSO is that voter turnout is ignored (it makes
     * no sense for whitelist, since it has to be initialized before any voting occurs).
     * Apart from turnout, the results should be equal as for FTSO.
     */
    function _getVotePowerWeights(IIFtso ftso, address[] memory _addresses) internal 
        returns (uint256[] memory _votePowers)
    {
        // get parameters
        IIVPToken[] memory assets;
        uint256[] memory assetMultipliers;
        uint256 totalVotePowerNat;
        uint256 totalVotePowerAsset;
        uint256 assetWeightRatio;
        uint256 votePowerBlock;
        (assets, assetMultipliers, totalVotePowerNat, totalVotePowerAsset, assetWeightRatio, votePowerBlock)
            = ftso.getVoteWeightingParameters();
        // nat vote powers
        uint256[] memory wNatVP = 
            _getNativeVotePowerWeights(ftso.wNat(), totalVotePowerNat, _addresses, votePowerBlock);
        // asset vote powers
        uint256[] memory combinedAssetVP = 
            _getAssetVotePowerWeights(assets, assetMultipliers, totalVotePowerAsset, _addresses, votePowerBlock);
        // combine asset and wNat
        return _computeWeightedSum(wNatVP, combinedAssetVP, assetWeightRatio);
    }
    
    /**
     * Calculate native vote power weights like FTSO.
     */
    function _getNativeVotePowerWeights(
        IIVPToken _wNat,
        uint256 _totalVotePowerNat,
        address[] memory _addresses, 
        uint256 _blockNumber
    )
        internal
        returns (uint256[] memory _wNatVP)
    {
        _wNatVP = _getVotePowers(_wNat, _addresses, _blockNumber);
        if (_totalVotePowerNat == 0) {
            return _wNatVP;  // if total is 0, all values must be 0, no division needed
        }
        for (uint256 i = 0; i < _addresses.length; i++) {
            _wNatVP[i] = _wNatVP[i].mulDiv(TERA, _totalVotePowerNat);
        }
    }
    
    /**
     * Calculate asset vote power weights like FTSO.
     */
    function _getAssetVotePowerWeights(
        IIVPToken[] memory _assets,
        uint256[] memory _assetMultipliers,
        uint256 _totalVotePowerAsset,
        address[] memory _addresses, 
        uint256 _blockNumber
    )
        internal 
        returns (uint256[] memory _combinedAssetVP)
    {
        _combinedAssetVP = new uint256[](_addresses.length);
        for (uint256 i = 0; i < _addresses.length; i++) {
            _combinedAssetVP[i] = 0;
        }
        if (_totalVotePowerAsset == 0) {
            return _combinedAssetVP;
        }
        uint256 divisor = _totalVotePowerAsset.mul(1e18);
        for (uint256 n = 0; n < _assets.length; n++) {
            IIVPToken asset = _assets[n];
            if (address(asset) != address(0)) {
                uint256[] memory assetVP = _getVotePowers(asset, _addresses, _blockNumber);
                // add
                for (uint256 i = 0; i < _addresses.length; i++) {
                    uint256 weightedVP = assetVP[i].mulDiv(_assetMultipliers[n], divisor);
                    _combinedAssetVP[i] = _combinedAssetVP[i].add(weightedVP);
                }
            }
        }
    }
    
    /**
     * Get vote powers from VPToken in a batch.
     * This is needed to avoid gas consumption of many cros-contract calls.
     */
    function _getVotePowers(
        IIVPToken _token, 
        address[] memory _addresses, 
        uint256 _blockNumber
    )
        internal 
        returns (uint256[] memory)
    {
        // warm up cache for new voter (in this way everyone pays cache storing price for himself)
        _token.votePowerOfAtCached(_addresses[_addresses.length - 1], _blockNumber);
        // get all vote powers in a batch
        return _token.batchVotePowerOfAt(_addresses, _blockNumber);
    }
    
    /**
     * Checks if _voter is trusted address
     */
    function _isTrustedAddress(address _voter) internal view returns(bool) {
        address[] memory trustedAddresses = priceSubmitter.getTrustedAddresses();
        for (uint256 i = 0; i < trustedAddresses.length; i++) {
            if (trustedAddresses[i] == _voter) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Update a voter whitelisting and emit an event.
     */    
    function _voterWhitelisted(address _voter, uint256 _ftsoIndex) private {
        emit VoterWhitelisted(_voter, _ftsoIndex);
        priceSubmitter.voterWhitelisted(_voter, _ftsoIndex);
    }
    
    /**
     * Update when a  voter is removed from the whitelist. And emit an event.
     */    
    function _votersRemovedFromWhitelist(address[] memory _removedVoters, uint256 _ftsoIndex) private {
        for (uint256 i = 0; i < _removedVoters.length; i++) {
            emit VoterRemovedFromWhitelist(_removedVoters[i], _ftsoIndex);
        }
        priceSubmitter.votersRemovedFromWhitelist(_removedVoters, _ftsoIndex);
    }

    /**
     * Calculate sum of all values in an array.
     */
    function _arraySum(uint256[] memory array) private pure returns (uint256) {
        uint256 result = 0;
        for (uint256 i = 0; i < array.length; i++) {
            result = result.add(array[i]);
        }
        return result;
    }
    
    /**
     * Calculate weighted sum of two arrays (like in FTSO):
     *  result[i] = (100% - _assetWeightRatio) * _weightsNat[i] + _assetWeightRatio * _weightsAsset[i]
     */
    function _computeWeightedSum(
        uint256[] memory _weightsNat,
        uint256[] memory _weightsAsset,
        uint256 _assetWeightRatio
    )
        private pure 
        returns (uint256[] memory _weights)
    {
        uint256 weightAssetSum = _arraySum(_weightsAsset);
        uint256 weightNatSum = _arraySum(_weightsNat);
        uint256 weightAssetShare = weightAssetSum > 0 ? _assetWeightRatio : 0;
        uint256 weightNatShare = weightNatSum > 0 ? BIPS100 - weightAssetShare : 0;
        _weights = new uint256[](_weightsNat.length);
        for (uint256 i = 0; i < _weightsNat.length; i++) {
            uint256 weightNat = 0;
            if (weightNatShare > 0) {
                weightNat = weightNatShare.mulDiv(TERA * _weightsNat[i], weightNatSum * BIPS100);
            }
            uint256 weightAsset = 0;
            if (weightAssetShare > 0) {
                weightAsset = weightAssetShare.mulDiv(TERA * _weightsAsset[i], weightAssetSum * BIPS100);
            }
            _weights[i] = weightNat + weightAsset;
        }
    }

    /**
     * Find minimum index of an uint256 array.
     * In case of a tie, returns later index.
     */
    function _findMinimum(uint256[] memory _votePowers, uint256 _length) private pure returns (uint256) {
        uint256 minIndex = 0;
        uint256 minVP = _votePowers[0];
        for (uint256 i = 0; i < _length; i++) {
            uint256 vp = _votePowers[i];
            // using `<=` ensures that later index is used if there is a tie
            // this is useful because in requestWhitelistingVoter, the new voter is last
            // also, when removing, it is cheaper to remove last element
            if (vp <= minVP) {
                minIndex = i;
                minVP = vp;
            }
        }
        return minIndex;
    }
}
        

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

}
          

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

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

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

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

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_governance","internalType":"address"},{"type":"address","name":"_addressUpdater","internalType":"address"},{"type":"address","name":"_priceSubmitter","internalType":"contract IIPriceSubmitter"},{"type":"uint256","name":"_defaultMaxVotersForFtso","internalType":"uint256"}]},{"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":"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":"VoterRemovedFromWhitelist","inputs":[{"type":"address","name":"voter","internalType":"address","indexed":false},{"type":"uint256","name":"ftsoIndex","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"VoterWhitelisted","inputs":[{"type":"address","name":"voter","internalType":"address","indexed":false},{"type":"uint256","name":"ftsoIndex","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addFtso","inputs":[{"type":"uint256","name":"_ftsoIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"defaultMaxVotersForFtso","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeGovernanceCall","inputs":[{"type":"bytes4","name":"_selector","internalType":"bytes4"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"ftsoManager","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IFtsoRegistry"}],"name":"ftsoRegistry","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_addressUpdater","internalType":"address"}],"name":"getAddressUpdater","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getFtsoWhitelistedPriceProviders","inputs":[{"type":"uint256","name":"_ftsoIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getFtsoWhitelistedPriceProvidersBySymbol","inputs":[{"type":"string","name":"_symbol","internalType":"string"}]},{"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":"uint256","name":"","internalType":"uint256"}],"name":"maxVotersForFtso","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"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":"uint256","name":"_ftsoIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeTrustedAddressFromWhitelist","inputs":[{"type":"address","name":"_trustedAddress","internalType":"address"},{"type":"uint256","name":"_ftsoIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"_supportedIndices","internalType":"uint256[]"},{"type":"bool[]","name":"_success","internalType":"bool[]"}],"name":"requestFullVoterWhitelisting","inputs":[{"type":"address","name":"_voter","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestWhitelistingVoter","inputs":[{"type":"address","name":"_voter","internalType":"address"},{"type":"uint256","name":"_ftsoIndex","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setDefaultMaxVotersForFtso","inputs":[{"type":"uint256","name":"_defaultMaxVotersForFtso","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMaxVotersForFtso","inputs":[{"type":"uint256","name":"_ftsoIndex","internalType":"uint256"},{"type":"uint256","name":"_newMaxVoters","internalType":"uint256"}]},{"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[]"}]}]
              

Contract Creation Code

0x60a06040523480156200001157600080fd5b50604051620032ed380380620032ed833981810160405260808110156200003757600080fd5b50805160208201516040830151606090930151919290918284806001600160a01b038116156200006c576200006c81620000e8565b506001600160a01b038116620000bc576040805162461bcd60e51b815260206004820152601060248201526f5f676f7665726e616e6365207a65726f60801b604482015290519081900360640190fd5b50620000c881620001ab565b5060609190911b6001600160601b03191660805260025550620001cf9050565b600054600160a01b900460ff161562000148576040805162461bcd60e51b815260206004820152601460248201527f696e697469616c6973656420213d2066616c7365000000000000000000000000604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b60805160601c6130f1620001fc60003980611a505280611c3152806123f852806126f452506130f16000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c806374e6310e116100c3578063b00c0b761161007c578063b00c0b76146104ba578063b06cbaf7146105dd578063d87361711461069c578063e17f212e146106b9578063f5a98383146106d5578063f937d6ad146106dd57610158565b806374e6310e146102de5780637ecfcfa31461038457806398dccfc2146103a75780639d6a890f146103c45780639dc950ab146103ea578063aa89dfd41461041657610158565b806347ed51b11161011557806347ed51b11461025e5780635267a15d146102785780635aa6e675146102805780635ff270791461028857806362354e03146102af57806367fc4029146102b757610158565b806309fcb4001461015d57806311a7aaaa146101ca5780632ee96140146101ee578063345705a41461020d57806338b5f8691461022a5780633de2cb1c14610232575b600080fd5b61017a6004803603602081101561017357600080fd5b50356106e5565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156101b657818101518382015260200161019e565b505050509050019250505060405180910390f35b6101d26107b0565b604080516001600160a01b039092168252519081900360200190f35b61020b6004803603602081101561020457600080fd5b50356107bf565b005b61020b6004803603602081101561022357600080fd5b5035610806565b6101d26108cf565b61020b6004803603604081101561024857600080fd5b506001600160a01b0381351690602001356108de565b610266610985565b60408051918252519081900360200190f35b6101d261098b565b6101d26109b0565b61020b6004803603602081101561029e57600080fd5b50356001600160e01b031916610a45565b6101d2610d9a565b61020b600480360360208110156102cd57600080fd5b50356001600160e01b031916610da5565b610305600480360360208110156102f457600080fd5b50356001600160e01b031916610e88565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610348578181015183820152602001610330565b50505050905090810190601f1680156103755780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b61020b6004803603604081101561039a57600080fd5b5080359060200135610f2e565b610266600480360360208110156103bd57600080fd5b5035611227565b61020b600480360360208110156103da57600080fd5b50356001600160a01b0316611239565b61020b6004803603604081101561040057600080fd5b506001600160a01b0381351690602001356112f2565b61017a6004803603602081101561042c57600080fd5b810190602081018135600160201b81111561044657600080fd5b82018360208201111561045857600080fd5b803590602001918460018302840111600160201b8311171561047957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611503945050505050565b61020b600480360360408110156104d057600080fd5b810190602081018135600160201b8111156104ea57600080fd5b8201836020820111156104fc57600080fd5b803590602001918460208302840111600160201b8311171561051d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561056c57600080fd5b82018360208201111561057e57600080fd5b803590602001918460208302840111600160201b8311171561059f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506115f2945050505050565b610603600480360360208110156105f357600080fd5b50356001600160a01b0316611699565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561064757818101518382015260200161062f565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561068657818101518382015260200161066e565b5050505090500194505050505060405180910390f35b61020b600480360360208110156106b257600080fd5b503561189d565b6106c1611984565b604080519115158252519081900360200190f35b61020b611994565b6101d2611a4e565b60008181526003602052604090205460609080610744576040805162461bcd60e51b8152602060048201526018602482015277119514d3c81a5b99195e081b9bdd081cdd5c1c1bdc9d195960421b604482015290519081900360640190fd5b600083815260046020908152604091829020805483518184028101840190945280845290918301828280156107a257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610784575b50505050509150505b919050565b6006546001600160a01b031681565b600054600160b01b900460ff16806107e15750600054600160a81b900460ff16155b156107f8576107ee611a72565b6002819055610803565b610803600036611aa9565b50565b6006546001600160a01b03163314610859576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333a39b79036b0b730b3b2b960791b604482015290519081900360640190fd5b600081815260036020526040902054156108ba576040805162461bcd60e51b815260206004820152601760248201527f77686974656c69737420616c7265616479206578697374000000000000000000604482015290519081900360640190fd5b60025460009182526003602052604090912055565b6005546001600160a01b031681565b6108e782611c2c565b1561092b576040805162461bcd60e51b815260206004820152600f60248201526e74727573746564206164647265737360881b604482015290519081900360640190fd5b60006109378383611da7565b905080610980576040805162461bcd60e51b8152602060048201526012602482015271766f746520706f77657220746f6f206c6f7760701b604482015290519081900360640190fd5b505050565b60025481565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff166109d3576000546001600160a01b0316610a40565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b158015610a1357600080fd5b505afa158015610a27573d6000803e3d6000fd5b505050506040513d6020811015610a3d57600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b158015610a8957600080fd5b505afa158015610a9d573d6000803e3d6000fd5b505050506040513d6020811015610ab357600080fd5b5051610af6576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610b62576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610bb8576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c525780601f10610c2757610100808354040283529160200191610c52565b820191906000526020600020905b815481529060010190602001808311610c3557829003601f168201915b5050506001600160e01b0319861660009081526001602081905260408220828155949550909250610c869150830182612f97565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b60208310610cce5780518252601f199092019160209182019101610caf565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610d30576040519150601f19603f3d011682016040523d82523d6000602084013e610d35565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a1610d9481612033565b50505050565b60076001609c1b0181565b610dad612050565b6001600160e01b03198116600090815260016020526040902054610e18576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061098090830182612f97565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f81018590048502860185019096528585529094919392909190830182828015610f245780601f10610ef957610100808354040283529160200191610f24565b820191906000526020600020905b815481529060010190602001808311610f0757829003601f168201915b5050505050905082565b600054600160b01b900460ff1680610f505750600054600160a81b900460ff16155b1561121857610f5d611a72565b60008281526003602090815260408083208490556004909152902080548210610f865750611213565b6005546040805163d75f6d8160e01b81526004810186905290516000926001600160a01b03169163d75f6d81916024808301926020929190829003018186803b158015610fd257600080fd5b505afa158015610fe6573d6000803e3d6000fd5b505050506040513d6020811015610ffc57600080fd5b505182546040805160208381028201810190925282815292935060009261106592859287919083018282801561105b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161103d575b50505050506120af565b805190915084810360008167ffffffffffffffff8111801561108657600080fd5b506040519080825280602002602001820160405280156110b0578160200160208202803683370190505b50905060005b828110156112015760006110ca8686612322565b90508781815481106110d857fe5b9060005260206000200160009054906101000a90046001600160a01b031683838151811061110257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600185038110156111c45787600186038154811061113c57fe5b9060005260206000200160009054906101000a90046001600160a01b031688828154811061116657fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508560018603815181106111a357fe5b60200260200101518682815181106111b757fe5b6020026020010181815250505b878054806111ce57fe5b600082815260209020810160001990810180546001600160a01b03191690559081019091559490940193506001016110b6565b5061120c8189612386565b5050505050505b611223565b611223600036611aa9565b5050565b60036020526000908152604090205481565b600054600160a01b900460ff161561128f576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b6112fb82611c2c565b611342576040805162461bcd60e51b81526020600482015260136024820152726e6f742074727573746564206164647265737360681b604482015290519081900360640190fd5b6000818152600460205260408120805490915b8181101561139b57846001600160a01b031683828154811061137357fe5b6000918252602090912001546001600160a01b031614156113935761139b565b600101611355565b8181106113ef576040805162461bcd60e51b815260206004820152601f60248201527f747275737465642061646472657373206e6f742077686974656c697374656400604482015290519081900360640190fd5b6040805160018082528183019092526000916020808301908036833701905050905083828154811061141d57fe5b600091825260208220015482516001600160a01b0390911691839161143e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083600184038154811061146d57fe5b9060005260206000200160009054906101000a90046001600160a01b031684838154811061149757fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838054806114cf57fe5b600082815260209020810160001990810180546001600160a01b03191690550190556114fb8186612386565b505050505050565b600554604051630e848da360e41b81526020600482018181528451602484015284516060946000946001600160a01b039091169363e848da3093889390928392604490910191908501908083838b5b8381101561156a578181015183820152602001611552565b50505050905090810190601f1680156115975780820380516001836020036101000a031916815260200191505b509250505060206040518083038186803b1580156115b457600080fd5b505afa1580156115c8573d6000803e3d6000fd5b505050506040513d60208110156115de57600080fd5b505190506115eb816106e5565b9392505050565b6115fa61098b565b6001600160a01b0316336001600160a01b031614611656576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b61168f61168a83836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b8152506124b6565b6125e3565b6112238282612607565b6060806116a583611c2c565b156116e9576040805162461bcd60e51b815260206004820152600f60248201526e74727573746564206164647265737360881b604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b031663798aac5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561173757600080fd5b505afa15801561174b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561177457600080fd5b8101908080516040519392919084600160201b82111561179357600080fd5b9083019060208201858111156117a857600080fd5b82518660208202830111600160201b821117156117c457600080fd5b82525081516020918201928201910280838360005b838110156117f15781810151838201526020016117d9565b5050505090500160405250505091506000825190508067ffffffffffffffff8111801561181d57600080fd5b50604051908082528060200260200182016040528015611847578160200160208202803683370190505b50915060005b81811015611896576118728585838151811061186557fe5b6020026020010151611da7565b83828151811061187e57fe5b9115156020928302919091019091015260010161184d565b5050915091565b6006546001600160a01b031633146118f0576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333a39b79036b0b730b3b2b960791b604482015290519081900360640190fd5b600081815260046020908152604091829020805483518184028101840190945280845261195c939283018282801561195157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611933575b505050505082612386565b600081815260046020526040812061197391612fdb565b600090815260036020526040812055565b600054600160a81b900460ff1681565b61199c612050565b600054600160a81b900460ff16156119fb576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b7f000000000000000000000000000000000000000000000000000000000000000081565b600054600160b01b900460ff1615611a9f57333014611a8d57fe5b6000805460ff60b01b19169055611aa7565b611aa7612050565b565b611ab1612050565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611af957600080fd5b505afa158015611b0d573d6000803e3d6000fd5b505050506040513d6020811015611b2357600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b03198616815260016020818152604090922084518155848301518051919450611ba793928501920190612ff9565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ffacb84e6040518163ffffffff1660e01b815260040160006040518083038186803b158015611c8857600080fd5b505afa158015611c9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015611cc557600080fd5b8101908080516040519392919084600160201b821115611ce457600080fd5b908301906020820185811115611cf957600080fd5b82518660208202830111600160201b82111715611d1557600080fd5b82525081516020918201928201910280838360005b83811015611d42578181015183820152602001611d2a565b50505050905001604052505050905060005b8151811015611d9d57836001600160a01b0316828281518110611d7357fe5b60200260200101516001600160a01b03161415611d95576001925050506107ab565b600101611d54565b5060009392505050565b60008181526003602052604081205480611e03576040805162461bcd60e51b8152602060048201526018602482015277119514d3c81a5b99195e081b9bdd081cdd5c1c1bdc9d195960421b604482015290519081900360640190fd5b6000838152600460205260408120805490916001820167ffffffffffffffff81118015611e2f57600080fd5b50604051908082528060200260200182016040528015611e59578160200160208202803683370190505b50905060005b82811015611ed4576000848281548110611e7557fe5b6000918252602090912001546001600160a01b0390811691508916811415611ea6576001965050505050505061202d565b80838381518110611eb357fe5b6001600160a01b039092166020928302919091019091015250600101611e5f565b5086818381518110611ee257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083821015611f4c5782546001810184556000848152602090200180546001600160a01b0319166001600160a01b038916179055611f3f87876126ae565b600194505050505061202d565b6000611f588288612769565b905082811415611f705760009550505050505061202d565b60408051600180825281830190925260009160208083019080368337019050509050828281518110611f9e57fe5b602002602001015181600081518110611fb357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505088858381548110611fe057fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506120188189612386565b61202289896126ae565b600196505050505050505b92915050565b3d604051818101604052816000823e821561204c578181f35b8181fd5b6120586109b0565b6001600160a01b0316336001600160a01b031614611aa7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6060806060600080600080886001600160a01b0316638357d08c6040518163ffffffff1660e01b815260040160006040518083038186803b1580156120f357600080fd5b505afa158015612107573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260c081101561213057600080fd5b8101908080516040519392919084600160201b82111561214f57600080fd5b90830190602082018581111561216457600080fd5b82518660208202830111600160201b8211171561218057600080fd5b82525081516020918201928201910280838360005b838110156121ad578181015183820152602001612195565b5050505090500160405260200180516040519392919084600160201b8211156121d557600080fd5b9083019060208201858111156121ea57600080fd5b82518660208202830111600160201b8211171561220657600080fd5b82525081516020918201928201910280838360005b8381101561223357818101518382015260200161221b565b5050505090500160405260200180519060200190929190805190602001909291908051906020019092919080519060200190929190505050809650819750829850839950849a50859b5050505050505060006122f58a6001600160a01b0316639edbf0076040518163ffffffff1660e01b815260040160206040518083038186803b1580156122c157600080fd5b505afa1580156122d5573d6000803e3d6000fd5b505050506040513d60208110156122eb57600080fd5b5051868b85612801565b905060006123068888878d8761287c565b90506123138282866129ef565b9b9a5050505050505050505050565b6000806000905060008460008151811061233857fe5b6020026020010151905060005b8481101561237c57600086828151811061235b57fe5b60200260200101519050828111612373578193508092505b50600101612345565b5090949350505050565b60005b82518110156123f5577f33359f2769756ca8d0da4683f25ee440744d6f18bfb166dbfb59315a8c62b0168382815181106123bf57fe5b60200260200101518360405180836001600160a01b031681526020018281526020019250505060405180910390a1600101612389565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166376794efb83836040518363ffffffff1660e01b81526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b8381101561247c578181015183820152602001612464565b505050509050019350505050600060405180830381600087803b1580156124a257600080fd5b505af11580156114fb573d6000803e3d6000fd5b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b838110156124fa5781810151838201526020016124e2565b50505050905090810190601f1680156125275780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b865181101561258f5786818151811061255d57fe5b60200260200101518314156125875785818151811061257857fe5b6020026020010151915061258f565b600101612548565b506001600160a01b0381166125da576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b61263682826040518060400160405280600c81526020016b4674736f526567697374727960a01b8152506124b6565b600560006101000a8154816001600160a01b0302191690836001600160a01b0316021790555061268a82826040518060400160405280600b81526020016a233a39b7a6b0b730b3b2b960a91b8152506124b6565b600680546001600160a01b0319166001600160a01b03929092169190911790555050565b604080516001600160a01b03841681526020810183905281517f66a8b13abe95391d1851f5bc319f3dde54ce8f2f40a5fe226aa3251d805832e3929181900390910190a17f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639d986f9183836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156124a257600080fd5b6005546040805163d75f6d8160e01b815260048101849052905160009283926001600160a01b039091169163d75f6d8191602480820192602092909190829003018186803b1580156127ba57600080fd5b505afa1580156127ce573d6000803e3d6000fd5b505050506040513d60208110156127e457600080fd5b5051905060006127f482866120af565b90506125da818251612322565b606061280e858484612b34565b90508361281a57612874565b60005b83518110156128725761285364e8d4a510008684848151811061283c57fe5b6020026020010151612d2c9092919063ffffffff16565b82828151811061285f57fe5b602090810291909101015260010161281d565b505b949350505050565b6060825167ffffffffffffffff8111801561289657600080fd5b506040519080825280602002602001820160405280156128c0578160200160208202803683370190505b50905060005b83518110156128f05760008282815181106128dd57fe5b60209081029190910101526001016128c6565b50836128fb576125da565b600061290f85670de0b6b3a7640000612e38565b905060005b87518110156129e457600088828151811061292b57fe5b6020026020010151905060006001600160a01b0316816001600160a01b0316146129db57600061295c828888612b34565b905060005b87518110156129d85760006129908b868151811061297b57fe5b60200260200101518785858151811061283c57fe5b90506129b8818884815181106129a257fe5b6020026020010151612e9190919063ffffffff16565b8783815181106129c457fe5b602090810291909101015250600101612961565b50505b50600101612914565b505095945050505050565b606060006129fc84612eeb565b90506000612a0986612eeb565b90506000808311612a1b576000612a1d565b845b90506000808311612a2f576000612a35565b81612710035b9050875167ffffffffffffffff81118015612a4f57600080fd5b50604051908082528060200260200182016040528015612a79578160200160208202803683370190505b50945060005b8851811015612b285760008215612ac657612ac38a8381518110612a9f57fe5b602002602001015164e8d4a5100002612710870285612d2c9092919063ffffffff16565b90505b60008415612b0457612b018a8481518110612add57fe5b602002602001015164e8d4a5100002612710890287612d2c9092919063ffffffff16565b90505b808201888481518110612b1357fe5b60209081029190910101525050600101612a7f565b50505050509392505050565b6060836001600160a01b031663e587497e84600186510381518110612b5557fe5b6020026020010151846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612ba457600080fd5b505af1158015612bb8573d6000803e3d6000fd5b505050506040513d6020811015612bce57600080fd5b5050604080516349e3c7e560e01b815260248101849052600481019182528451604482015284516001600160a01b038716926349e3c7e592879287929182916064909101906020868101910280838360005b83811015612c38578181015183820152602001612c20565b50505050905001935050505060006040518083038186803b158015612c5c57600080fd5b505afa158015612c70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015612c9957600080fd5b8101908080516040519392919084600160201b821115612cb857600080fd5b908301906020820185811115612ccd57600080fd5b82518660208202830111600160201b82111715612ce957600080fd5b82525081516020918201928201910280838360005b83811015612d16578181015183820152602001612cfe565b5050505090500160405250505090509392505050565b6000808211612d75576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83612d82575060006115eb565b83830283858281612d8f57fe5b041415612da857828181612d9f57fe5b049150506115eb565b6000838681612db357fe5b0490506000848781612dc157fe5b0690506000858781612dcf57fe5b0490506000868881612ddd57fe5b069050612e2b612df788612df18685612e38565b90612f30565b612e25612e048686612e38565b612e25612e118987612e38565b612e258d612e1f8c8b612e38565b90612e38565b90612e91565b9998505050505050505050565b600082612e475750600061202d565b82820282848281612e5457fe5b04146115eb5760405162461bcd60e51b815260040180806020018281038252602181526020018061309b6021913960400191505060405180910390fd5b6000828201838110156115eb576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080805b8351811015612f2957612f1f848281518110612f0857fe5b602002602001015183612e9190919063ffffffff16565b9150600101612ef0565b5092915050565b6000808211612f86576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381612f8f57fe5b049392505050565b50805460018160011615610100020316600290046000825580601f10612fbd5750610803565b601f0160209004906000526020600020908101906108039190613085565b50805460008255906000526020600020908101906108039190613085565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261302f5760008555613075565b82601f1061304857805160ff1916838001178555613075565b82800160010185558215613075579182015b8281111561307557825182559160200191906001019061305a565b50613081929150613085565b5090565b5b80821115613081576000815560010161308656fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220f7379d851047b174397b3e8f8f856b490f7d8712c93f9d29140d5205623211c464736f6c634300070600330000000000000000000000004598a6c05910ab914f0cbaaca1911cd337d10d29000000000000000000000000baf89d873d198ff78e72d2745b01cba3c6e5be6b00000000000000000000000010000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106101585760003560e01c806374e6310e116100c3578063b00c0b761161007c578063b00c0b76146104ba578063b06cbaf7146105dd578063d87361711461069c578063e17f212e146106b9578063f5a98383146106d5578063f937d6ad146106dd57610158565b806374e6310e146102de5780637ecfcfa31461038457806398dccfc2146103a75780639d6a890f146103c45780639dc950ab146103ea578063aa89dfd41461041657610158565b806347ed51b11161011557806347ed51b11461025e5780635267a15d146102785780635aa6e675146102805780635ff270791461028857806362354e03146102af57806367fc4029146102b757610158565b806309fcb4001461015d57806311a7aaaa146101ca5780632ee96140146101ee578063345705a41461020d57806338b5f8691461022a5780633de2cb1c14610232575b600080fd5b61017a6004803603602081101561017357600080fd5b50356106e5565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156101b657818101518382015260200161019e565b505050509050019250505060405180910390f35b6101d26107b0565b604080516001600160a01b039092168252519081900360200190f35b61020b6004803603602081101561020457600080fd5b50356107bf565b005b61020b6004803603602081101561022357600080fd5b5035610806565b6101d26108cf565b61020b6004803603604081101561024857600080fd5b506001600160a01b0381351690602001356108de565b610266610985565b60408051918252519081900360200190f35b6101d261098b565b6101d26109b0565b61020b6004803603602081101561029e57600080fd5b50356001600160e01b031916610a45565b6101d2610d9a565b61020b600480360360208110156102cd57600080fd5b50356001600160e01b031916610da5565b610305600480360360208110156102f457600080fd5b50356001600160e01b031916610e88565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610348578181015183820152602001610330565b50505050905090810190601f1680156103755780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b61020b6004803603604081101561039a57600080fd5b5080359060200135610f2e565b610266600480360360208110156103bd57600080fd5b5035611227565b61020b600480360360208110156103da57600080fd5b50356001600160a01b0316611239565b61020b6004803603604081101561040057600080fd5b506001600160a01b0381351690602001356112f2565b61017a6004803603602081101561042c57600080fd5b810190602081018135600160201b81111561044657600080fd5b82018360208201111561045857600080fd5b803590602001918460018302840111600160201b8311171561047957600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550611503945050505050565b61020b600480360360408110156104d057600080fd5b810190602081018135600160201b8111156104ea57600080fd5b8201836020820111156104fc57600080fd5b803590602001918460208302840111600160201b8311171561051d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561056c57600080fd5b82018360208201111561057e57600080fd5b803590602001918460208302840111600160201b8311171561059f57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295506115f2945050505050565b610603600480360360208110156105f357600080fd5b50356001600160a01b0316611699565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561064757818101518382015260200161062f565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561068657818101518382015260200161066e565b5050505090500194505050505060405180910390f35b61020b600480360360208110156106b257600080fd5b503561189d565b6106c1611984565b604080519115158252519081900360200190f35b61020b611994565b6101d2611a4e565b60008181526003602052604090205460609080610744576040805162461bcd60e51b8152602060048201526018602482015277119514d3c81a5b99195e081b9bdd081cdd5c1c1bdc9d195960421b604482015290519081900360640190fd5b600083815260046020908152604091829020805483518184028101840190945280845290918301828280156107a257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610784575b50505050509150505b919050565b6006546001600160a01b031681565b600054600160b01b900460ff16806107e15750600054600160a81b900460ff16155b156107f8576107ee611a72565b6002819055610803565b610803600036611aa9565b50565b6006546001600160a01b03163314610859576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333a39b79036b0b730b3b2b960791b604482015290519081900360640190fd5b600081815260036020526040902054156108ba576040805162461bcd60e51b815260206004820152601760248201527f77686974656c69737420616c7265616479206578697374000000000000000000604482015290519081900360640190fd5b60025460009182526003602052604090912055565b6005546001600160a01b031681565b6108e782611c2c565b1561092b576040805162461bcd60e51b815260206004820152600f60248201526e74727573746564206164647265737360881b604482015290519081900360640190fd5b60006109378383611da7565b905080610980576040805162461bcd60e51b8152602060048201526012602482015271766f746520706f77657220746f6f206c6f7760701b604482015290519081900360640190fd5b505050565b60025481565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e77195490565b60008054600160a81b900460ff166109d3576000546001600160a01b0316610a40565b60076001609c1b016001600160a01b031663732524946040518163ffffffff1660e01b815260040160206040518083038186803b158015610a1357600080fd5b505afa158015610a27573d6000803e3d6000fd5b505050506040513d6020811015610a3d57600080fd5b50515b905090565b60408051630debfda360e41b8152336004820152905160076001609c1b019163debfda30916024808301926020929190829003018186803b158015610a8957600080fd5b505afa158015610a9d573d6000803e3d6000fd5b505050506040513d6020811015610ab357600080fd5b5051610af6576040805162461bcd60e51b815260206004820152600d60248201526c37b7363c9032bc32b1baba37b960991b604482015290519081900360640190fd5b6001600160e01b0319811660009081526001602052604090208054610b62576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b8054421015610bb8576040805162461bcd60e51b815260206004820152601960248201527f74696d656c6f636b3a206e6f7420616c6c6f7765642079657400000000000000604482015290519081900360640190fd5b6000816001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c525780601f10610c2757610100808354040283529160200191610c52565b820191906000526020600020905b815481529060010190602001808311610c3557829003601f168201915b5050506001600160e01b0319861660009081526001602081905260408220828155949550909250610c869150830182612f97565b50506000805460ff60b01b1916600160b01b178155604051825130918491819060208401908083835b60208310610cce5780518252601f199092019160209182019101610caf565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610d30576040519150601f19603f3d011682016040523d82523d6000602084013e610d35565b606091505b50506000805460ff60b01b19169055604080516001600160e01b03198716815242602082015281519293507fa7326b57fc9cfe267aaea5e7f0b01757154d265620a0585819416ee9ddd2c438929081900390910190a1610d9481612033565b50505050565b60076001609c1b0181565b610dad612050565b6001600160e01b03198116600090815260016020526040902054610e18576040805162461bcd60e51b815260206004820152601a60248201527f74696d656c6f636b3a20696e76616c69642073656c6563746f72000000000000604482015290519081900360640190fd5b604080516001600160e01b03198316815242602082015281517f7735b2391c38a81419c513e30ca578db7158eadd7101511b23e221c654d19cf8929181900390910190a16001600160e01b0319811660009081526001602081905260408220828155919061098090830182612f97565b600160208181526000928352604092839020805481840180548651600296821615610100026000190190911695909504601f81018590048502860185019096528585529094919392909190830182828015610f245780601f10610ef957610100808354040283529160200191610f24565b820191906000526020600020905b815481529060010190602001808311610f0757829003601f168201915b5050505050905082565b600054600160b01b900460ff1680610f505750600054600160a81b900460ff16155b1561121857610f5d611a72565b60008281526003602090815260408083208490556004909152902080548210610f865750611213565b6005546040805163d75f6d8160e01b81526004810186905290516000926001600160a01b03169163d75f6d81916024808301926020929190829003018186803b158015610fd257600080fd5b505afa158015610fe6573d6000803e3d6000fd5b505050506040513d6020811015610ffc57600080fd5b505182546040805160208381028201810190925282815292935060009261106592859287919083018282801561105b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161103d575b50505050506120af565b805190915084810360008167ffffffffffffffff8111801561108657600080fd5b506040519080825280602002602001820160405280156110b0578160200160208202803683370190505b50905060005b828110156112015760006110ca8686612322565b90508781815481106110d857fe5b9060005260206000200160009054906101000a90046001600160a01b031683838151811061110257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600185038110156111c45787600186038154811061113c57fe5b9060005260206000200160009054906101000a90046001600160a01b031688828154811061116657fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055508560018603815181106111a357fe5b60200260200101518682815181106111b757fe5b6020026020010181815250505b878054806111ce57fe5b600082815260209020810160001990810180546001600160a01b03191690559081019091559490940193506001016110b6565b5061120c8189612386565b5050505050505b611223565b611223600036611aa9565b5050565b60036020526000908152604090205481565b600054600160a01b900460ff161561128f576040805162461bcd60e51b8152602060048201526014602482015273696e697469616c6973656420213d2066616c736560601b604482015290519081900360640190fd5b60008054600160a01b60ff60a01b19909116176001600160a01b0319166001600160a01b03831690811790915560408051918252517f9789733827840833afc031fb2ef9ab6894271f77bad2085687cf4ae5c7bee4db916020908290030190a150565b6112fb82611c2c565b611342576040805162461bcd60e51b81526020600482015260136024820152726e6f742074727573746564206164647265737360681b604482015290519081900360640190fd5b6000818152600460205260408120805490915b8181101561139b57846001600160a01b031683828154811061137357fe5b6000918252602090912001546001600160a01b031614156113935761139b565b600101611355565b8181106113ef576040805162461bcd60e51b815260206004820152601f60248201527f747275737465642061646472657373206e6f742077686974656c697374656400604482015290519081900360640190fd5b6040805160018082528183019092526000916020808301908036833701905050905083828154811061141d57fe5b600091825260208220015482516001600160a01b0390911691839161143e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083600184038154811061146d57fe5b9060005260206000200160009054906101000a90046001600160a01b031684838154811061149757fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550838054806114cf57fe5b600082815260209020810160001990810180546001600160a01b03191690550190556114fb8186612386565b505050505050565b600554604051630e848da360e41b81526020600482018181528451602484015284516060946000946001600160a01b039091169363e848da3093889390928392604490910191908501908083838b5b8381101561156a578181015183820152602001611552565b50505050905090810190601f1680156115975780820380516001836020036101000a031916815260200191505b509250505060206040518083038186803b1580156115b457600080fd5b505afa1580156115c8573d6000803e3d6000fd5b505050506040513d60208110156115de57600080fd5b505190506115eb816106e5565b9392505050565b6115fa61098b565b6001600160a01b0316336001600160a01b031614611656576040805162461bcd60e51b815260206004820152601460248201527337b7363c9030b2323932b9b9903ab83230ba32b960611b604482015290519081900360640190fd5b61168f61168a83836040518060400160405280600e81526020016d20b2323932b9b9aab83230ba32b960911b8152506124b6565b6125e3565b6112238282612607565b6060806116a583611c2c565b156116e9576040805162461bcd60e51b815260206004820152600f60248201526e74727573746564206164647265737360881b604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b031663798aac5b6040518163ffffffff1660e01b815260040160006040518083038186803b15801561173757600080fd5b505afa15801561174b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561177457600080fd5b8101908080516040519392919084600160201b82111561179357600080fd5b9083019060208201858111156117a857600080fd5b82518660208202830111600160201b821117156117c457600080fd5b82525081516020918201928201910280838360005b838110156117f15781810151838201526020016117d9565b5050505090500160405250505091506000825190508067ffffffffffffffff8111801561181d57600080fd5b50604051908082528060200260200182016040528015611847578160200160208202803683370190505b50915060005b81811015611896576118728585838151811061186557fe5b6020026020010151611da7565b83828151811061187e57fe5b9115156020928302919091019091015260010161184d565b5050915091565b6006546001600160a01b031633146118f0576040805162461bcd60e51b815260206004820152601160248201527037b7363c90333a39b79036b0b730b3b2b960791b604482015290519081900360640190fd5b600081815260046020908152604091829020805483518184028101840190945280845261195c939283018282801561195157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611933575b505050505082612386565b600081815260046020526040812061197391612fdb565b600090815260036020526040812055565b600054600160a81b900460ff1681565b61199c612050565b600054600160a81b900460ff16156119fb576040805162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2070726f64756374696f6e206d6f6465000000000000604482015290519081900360640190fd5b60008054600161ff0160a01b031916600160a81b1790556040805160076001609c1b01815290517f83af113638b5422f9e977cebc0aaf0eaf2188eb9a8baae7f9d46c42b33a1560c9181900360200190a1565b7f000000000000000000000000100000000000000000000000000000000000000381565b600054600160b01b900460ff1615611a9f57333014611a8d57fe5b6000805460ff60b01b19169055611aa7565b611aa7612050565b565b611ab1612050565b600082359050600060076001609c1b016001600160a01b0316636221a54b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611af957600080fd5b505afa158015611b0d573d6000803e3d6000fd5b505050506040513d6020811015611b2357600080fd5b505160408051808201825242830180825282516020601f89018190048102820181019094528781529394509290918281019190889088908190840183828082843760009201829052509390945250506001600160e01b03198616815260016020818152604090922084518155848301518051919450611ba793928501920190612ff9565b509050507fed948300a3694aa01d4a6b258bfd664350193d770c0b51f8387277f6d83ea3b68382878760405180856001600160e01b0319168152602001848152602001806020018281038252848482818152602001925080828437600083820152604051601f909101601f191690920182900397509095505050505050a15050505050565b6000807f00000000000000000000000010000000000000000000000000000000000000036001600160a01b031663ffacb84e6040518163ffffffff1660e01b815260040160006040518083038186803b158015611c8857600080fd5b505afa158015611c9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015611cc557600080fd5b8101908080516040519392919084600160201b821115611ce457600080fd5b908301906020820185811115611cf957600080fd5b82518660208202830111600160201b82111715611d1557600080fd5b82525081516020918201928201910280838360005b83811015611d42578181015183820152602001611d2a565b50505050905001604052505050905060005b8151811015611d9d57836001600160a01b0316828281518110611d7357fe5b60200260200101516001600160a01b03161415611d95576001925050506107ab565b600101611d54565b5060009392505050565b60008181526003602052604081205480611e03576040805162461bcd60e51b8152602060048201526018602482015277119514d3c81a5b99195e081b9bdd081cdd5c1c1bdc9d195960421b604482015290519081900360640190fd5b6000838152600460205260408120805490916001820167ffffffffffffffff81118015611e2f57600080fd5b50604051908082528060200260200182016040528015611e59578160200160208202803683370190505b50905060005b82811015611ed4576000848281548110611e7557fe5b6000918252602090912001546001600160a01b0390811691508916811415611ea6576001965050505050505061202d565b80838381518110611eb357fe5b6001600160a01b039092166020928302919091019091015250600101611e5f565b5086818381518110611ee257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083821015611f4c5782546001810184556000848152602090200180546001600160a01b0319166001600160a01b038916179055611f3f87876126ae565b600194505050505061202d565b6000611f588288612769565b905082811415611f705760009550505050505061202d565b60408051600180825281830190925260009160208083019080368337019050509050828281518110611f9e57fe5b602002602001015181600081518110611fb357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505088858381548110611fe057fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506120188189612386565b61202289896126ae565b600196505050505050505b92915050565b3d604051818101604052816000823e821561204c578181f35b8181fd5b6120586109b0565b6001600160a01b0316336001600160a01b031614611aa7576040805162461bcd60e51b815260206004820152600f60248201526e6f6e6c7920676f7665726e616e636560881b604482015290519081900360640190fd5b6060806060600080600080886001600160a01b0316638357d08c6040518163ffffffff1660e01b815260040160006040518083038186803b1580156120f357600080fd5b505afa158015612107573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260c081101561213057600080fd5b8101908080516040519392919084600160201b82111561214f57600080fd5b90830190602082018581111561216457600080fd5b82518660208202830111600160201b8211171561218057600080fd5b82525081516020918201928201910280838360005b838110156121ad578181015183820152602001612195565b5050505090500160405260200180516040519392919084600160201b8211156121d557600080fd5b9083019060208201858111156121ea57600080fd5b82518660208202830111600160201b8211171561220657600080fd5b82525081516020918201928201910280838360005b8381101561223357818101518382015260200161221b565b5050505090500160405260200180519060200190929190805190602001909291908051906020019092919080519060200190929190505050809650819750829850839950849a50859b5050505050505060006122f58a6001600160a01b0316639edbf0076040518163ffffffff1660e01b815260040160206040518083038186803b1580156122c157600080fd5b505afa1580156122d5573d6000803e3d6000fd5b505050506040513d60208110156122eb57600080fd5b5051868b85612801565b905060006123068888878d8761287c565b90506123138282866129ef565b9b9a5050505050505050505050565b6000806000905060008460008151811061233857fe5b6020026020010151905060005b8481101561237c57600086828151811061235b57fe5b60200260200101519050828111612373578193508092505b50600101612345565b5090949350505050565b60005b82518110156123f5577f33359f2769756ca8d0da4683f25ee440744d6f18bfb166dbfb59315a8c62b0168382815181106123bf57fe5b60200260200101518360405180836001600160a01b031681526020018281526020019250505060405180910390a1600101612389565b507f00000000000000000000000010000000000000000000000000000000000000036001600160a01b03166376794efb83836040518363ffffffff1660e01b81526004018080602001838152602001828103825284818151815260200191508051906020019060200280838360005b8381101561247c578181015183820152602001612464565b505050509050019350505050600060405180830381600087803b1580156124a257600080fd5b505af11580156114fb573d6000803e3d6000fd5b600080826040516020018080602001828103825283818151815260200191508051906020019080838360005b838110156124fa5781810151838201526020016124e2565b50505050905090810190601f1680156125275780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040528051906020012090506000805b865181101561258f5786818151811061255d57fe5b60200260200101518314156125875785818151811061257857fe5b6020026020010151915061258f565b600101612548565b506001600160a01b0381166125da576040805162461bcd60e51b815260206004820152600c60248201526b61646472657373207a65726f60a01b604482015290519081900360640190fd5b95945050505050565b7f714f205b2abd25bef1d06a1af944e38c113fe6160375c4e1d6d5cf28848e771955565b61263682826040518060400160405280600c81526020016b4674736f526567697374727960a01b8152506124b6565b600560006101000a8154816001600160a01b0302191690836001600160a01b0316021790555061268a82826040518060400160405280600b81526020016a233a39b7a6b0b730b3b2b960a91b8152506124b6565b600680546001600160a01b0319166001600160a01b03929092169190911790555050565b604080516001600160a01b03841681526020810183905281517f66a8b13abe95391d1851f5bc319f3dde54ce8f2f40a5fe226aa3251d805832e3929181900390910190a17f00000000000000000000000010000000000000000000000000000000000000036001600160a01b0316639d986f9183836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b1580156124a257600080fd5b6005546040805163d75f6d8160e01b815260048101849052905160009283926001600160a01b039091169163d75f6d8191602480820192602092909190829003018186803b1580156127ba57600080fd5b505afa1580156127ce573d6000803e3d6000fd5b505050506040513d60208110156127e457600080fd5b5051905060006127f482866120af565b90506125da818251612322565b606061280e858484612b34565b90508361281a57612874565b60005b83518110156128725761285364e8d4a510008684848151811061283c57fe5b6020026020010151612d2c9092919063ffffffff16565b82828151811061285f57fe5b602090810291909101015260010161281d565b505b949350505050565b6060825167ffffffffffffffff8111801561289657600080fd5b506040519080825280602002602001820160405280156128c0578160200160208202803683370190505b50905060005b83518110156128f05760008282815181106128dd57fe5b60209081029190910101526001016128c6565b50836128fb576125da565b600061290f85670de0b6b3a7640000612e38565b905060005b87518110156129e457600088828151811061292b57fe5b6020026020010151905060006001600160a01b0316816001600160a01b0316146129db57600061295c828888612b34565b905060005b87518110156129d85760006129908b868151811061297b57fe5b60200260200101518785858151811061283c57fe5b90506129b8818884815181106129a257fe5b6020026020010151612e9190919063ffffffff16565b8783815181106129c457fe5b602090810291909101015250600101612961565b50505b50600101612914565b505095945050505050565b606060006129fc84612eeb565b90506000612a0986612eeb565b90506000808311612a1b576000612a1d565b845b90506000808311612a2f576000612a35565b81612710035b9050875167ffffffffffffffff81118015612a4f57600080fd5b50604051908082528060200260200182016040528015612a79578160200160208202803683370190505b50945060005b8851811015612b285760008215612ac657612ac38a8381518110612a9f57fe5b602002602001015164e8d4a5100002612710870285612d2c9092919063ffffffff16565b90505b60008415612b0457612b018a8481518110612add57fe5b602002602001015164e8d4a5100002612710890287612d2c9092919063ffffffff16565b90505b808201888481518110612b1357fe5b60209081029190910101525050600101612a7f565b50505050509392505050565b6060836001600160a01b031663e587497e84600186510381518110612b5557fe5b6020026020010151846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612ba457600080fd5b505af1158015612bb8573d6000803e3d6000fd5b505050506040513d6020811015612bce57600080fd5b5050604080516349e3c7e560e01b815260248101849052600481019182528451604482015284516001600160a01b038716926349e3c7e592879287929182916064909101906020868101910280838360005b83811015612c38578181015183820152602001612c20565b50505050905001935050505060006040518083038186803b158015612c5c57600080fd5b505afa158015612c70573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015612c9957600080fd5b8101908080516040519392919084600160201b821115612cb857600080fd5b908301906020820185811115612ccd57600080fd5b82518660208202830111600160201b82111715612ce957600080fd5b82525081516020918201928201910280838360005b83811015612d16578181015183820152602001612cfe565b5050505090500160405250505090509392505050565b6000808211612d75576040805162461bcd60e51b815260206004820152601060248201526f4469766973696f6e206279207a65726f60801b604482015290519081900360640190fd5b83612d82575060006115eb565b83830283858281612d8f57fe5b041415612da857828181612d9f57fe5b049150506115eb565b6000838681612db357fe5b0490506000848781612dc157fe5b0690506000858781612dcf57fe5b0490506000868881612ddd57fe5b069050612e2b612df788612df18685612e38565b90612f30565b612e25612e048686612e38565b612e25612e118987612e38565b612e258d612e1f8c8b612e38565b90612e38565b90612e91565b9998505050505050505050565b600082612e475750600061202d565b82820282848281612e5457fe5b04146115eb5760405162461bcd60e51b815260040180806020018281038252602181526020018061309b6021913960400191505060405180910390fd5b6000828201838110156115eb576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080805b8351811015612f2957612f1f848281518110612f0857fe5b602002602001015183612e9190919063ffffffff16565b9150600101612ef0565b5092915050565b6000808211612f86576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381612f8f57fe5b049392505050565b50805460018160011615610100020316600290046000825580601f10612fbd5750610803565b601f0160209004906000526020600020908101906108039190613085565b50805460008255906000526020600020908101906108039190613085565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261302f5760008555613075565b82601f1061304857805160ff1916838001178555613075565b82800160010185558215613075579182015b8281111561307557825182559160200191906001019061305a565b50613081929150613085565b5090565b5b80821115613081576000815560010161308656fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a2646970667358221220f7379d851047b174397b3e8f8f856b490f7d8712c93f9d29140d5205623211c464736f6c63430007060033