Moonbase Alpha Testnet

Contract

0x7a192E206dc1511d7735Ad27a618a5a5FB21a0FC

Overview

DEV Balance

Moonbase Alpha LogoMoonbase Alpha LogoMoonbase Alpha Logo0 DEV

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Value
Claim Fees19652862022-04-08 2:06:12721 days ago1649383572IN
0x7a192E20...5FB21a0FC
0 DEV0.001066673.5
Claim Fees19517082022-04-05 7:54:42724 days ago1649145282IN
0x7a192E20...5FB21a0FC
0 DEV0.002566753.5
Claim Fees19514932022-04-05 6:53:00724 days ago1649141580IN
0x7a192E20...5FB21a0FC
0 DEV0.002061643.5
Allocate Fee Rew...19514862022-04-05 6:51:06724 days ago1649141466IN
0x7a192E20...5FB21a0FC
0 DEV0.000625543.5
Allocate Fee Rew...19514522022-04-05 6:42:06724 days ago1649140926IN
0x7a192E20...5FB21a0FC
0 DEV0.000190233.5
Close Current Fe...19514412022-04-05 6:39:00724 days ago1649140740IN
0x7a192E20...5FB21a0FC
0 DEV0.000416433.5
Allocate Fee Rew...19514342022-04-05 6:36:54724 days ago1649140614IN
0x7a192E20...5FB21a0FC
0 DEV0.000555893.5
Close Current Fe...19223702022-03-30 7:41:54730 days ago1648626114IN
0x7a192E20...5FB21a0FC
0 DEV0.000416433.5
Distribute Fee R...19223682022-03-30 7:41:18730 days ago1648626078IN
0x7a192E20...5FB21a0FC
0 DEV0.00034233.5
Close Current Fe...19222982022-03-30 7:19:30730 days ago1648624770IN
0x7a192E20...5FB21a0FC
0 DEV0.000360713.5
0x6080604014283162021-12-28 3:04:54822 days ago1640660694IN
 Create: FeePool
0 DEV0.00919332

Latest 25 internal transactions (View All)

Parent Txn Hash Block From To Value
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899702022-04-13 3:05:48716 days ago1649819148
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899682022-04-13 3:05:18716 days ago1649819118
0x7a192E20...5FB21a0FC
0 DEV
19899592022-04-13 3:02:36716 days ago1649818956
0x7a192E20...5FB21a0FC
0 DEV
19899592022-04-13 3:02:36716 days ago1649818956
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898612022-04-13 2:35:36716 days ago1649817336
0x7a192E20...5FB21a0FC
0 DEV
19898592022-04-13 2:35:12716 days ago1649817312
0x7a192E20...5FB21a0FC
0 DEV
19898592022-04-13 2:35:12716 days ago1649817312
0x7a192E20...5FB21a0FC
0 DEV
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FeePool

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 1500 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at moonbase.moonscan.io on 2021-12-28
*/

/*
    ___            _       ___  _                          
    | .\ ___  _ _ <_> ___ | __><_>._ _  ___ ._ _  ___  ___ 
    |  _// ._>| '_>| ||___|| _> | || ' |<_> || ' |/ | '/ ._>
    |_|  \___.|_|  |_|     |_|  |_||_|_|<___||_|_|\_|_.\___.
    
* PeriFinance: FeePool.sol
*
* Latest source (may be newer): https://github.com/perifinance/peri-finance/blob/master/contracts/FeePool.sol
* Docs: Will be added in the future. 
* https://docs.peri.finance/contracts/source/contracts/FeePool
*
* Contract Dependencies: 
*	- EternalStorage
*	- IAddressResolver
*	- IFeePool
*	- LimitedSetup
*	- MixinResolver
*	- MixinSystemSettings
*	- Owned
*	- Proxyable
*	- State
* Libraries: 
*	- SafeDecimalMath
*	- SafeMath
*	- VestingEntries
*
* MIT License
* ===========
*
* Copyright (c) 2021 PeriFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/



pragma solidity 0.5.16;

// https://docs.peri.finance/contracts/source/contracts/owned
contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor(address _owner) public {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}


// Inheritance


// Internal references


// https://docs.peri.finance/contracts/source/contracts/proxy
contract Proxy is Owned {
    Proxyable public target;

    constructor(address _owner) public Owned(_owner) {}

    function setTarget(Proxyable _target) external onlyOwner {
        target = _target;
        emit TargetUpdated(_target);
    }

    function _emit(
        bytes calldata callData,
        uint numTopics,
        bytes32 topic1,
        bytes32 topic2,
        bytes32 topic3,
        bytes32 topic4
    ) external onlyTarget {
        uint size = callData.length;
        bytes memory _callData = callData;

        assembly {
            /* The first 32 bytes of callData contain its length (as specified by the abi).
             * Length is assumed to be a uint256 and therefore maximum of 32 bytes
             * in length. It is also leftpadded to be a multiple of 32 bytes.
             * This means moving call_data across 32 bytes guarantees we correctly access
             * the data itself. */
            switch numTopics
                case 0 {
                    log0(add(_callData, 32), size)
                }
                case 1 {
                    log1(add(_callData, 32), size, topic1)
                }
                case 2 {
                    log2(add(_callData, 32), size, topic1, topic2)
                }
                case 3 {
                    log3(add(_callData, 32), size, topic1, topic2, topic3)
                }
                case 4 {
                    log4(add(_callData, 32), size, topic1, topic2, topic3, topic4)
                }
        }
    }

    // solhint-disable no-complex-fallback
    function() external payable {
        // Mutable call setting Proxyable.messageSender as this is using call not delegatecall
        target.setMessageSender(msg.sender);

        assembly {
            let free_ptr := mload(0x40)
            calldatacopy(free_ptr, 0, calldatasize)

            /* We must explicitly forward ether to the underlying contract as well. */
            let result := call(gas, sload(target_slot), callvalue, free_ptr, calldatasize, 0, 0)
            returndatacopy(free_ptr, 0, returndatasize)

            if iszero(result) {
                revert(free_ptr, returndatasize)
            }
            return(free_ptr, returndatasize)
        }
    }

    modifier onlyTarget {
        require(Proxyable(msg.sender) == target, "Must be proxy target");
        _;
    }

    event TargetUpdated(Proxyable newTarget);
}


// Inheritance


// Internal references


// https://docs.peri.finance/contracts/source/contracts/proxyable
contract Proxyable is Owned {
    // This contract should be treated like an abstract contract

    /* The proxy this contract exists behind. */
    Proxy public proxy;
    Proxy public integrationProxy;

    /* The caller of the proxy, passed through to this contract.
     * Note that every function using this member must apply the onlyProxy or
     * optionalProxy modifiers, otherwise their invocations can use stale values. */
    address public messageSender;

    constructor(address payable _proxy) internal {
        // This contract is abstract, and thus cannot be instantiated directly
        require(owner != address(0), "Owner must be set");

        proxy = Proxy(_proxy);
        emit ProxyUpdated(_proxy);
    }

    function setProxy(address payable _proxy) external onlyOwner {
        proxy = Proxy(_proxy);
        emit ProxyUpdated(_proxy);
    }

    function setIntegrationProxy(address payable _integrationProxy) external onlyOwner {
        integrationProxy = Proxy(_integrationProxy);
    }

    function setMessageSender(address sender) external onlyProxy {
        messageSender = sender;
    }

    modifier onlyProxy {
        _onlyProxy();
        _;
    }

    function _onlyProxy() private view {
        require(Proxy(msg.sender) == proxy || Proxy(msg.sender) == integrationProxy, "Only the proxy can call");
    }

    modifier optionalProxy {
        _optionalProxy();
        _;
    }

    function _optionalProxy() private {
        if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
            messageSender = msg.sender;
        }
    }

    modifier optionalProxy_onlyOwner {
        _optionalProxy_onlyOwner();
        _;
    }

    // solhint-disable-next-line func-name-mixedcase
    function _optionalProxy_onlyOwner() private {
        if (Proxy(msg.sender) != proxy && Proxy(msg.sender) != integrationProxy && messageSender != msg.sender) {
            messageSender = msg.sender;
        }
        require(messageSender == owner, "Owner only function");
    }

    event ProxyUpdated(address proxyAddress);
}


// https://docs.peri.finance/contracts/source/contracts/limitedsetup
contract LimitedSetup {
    uint public setupExpiryTime;

    /**
     * @dev LimitedSetup Constructor.
     * @param setupDuration The time the setup period will last for.
     */
    constructor(uint setupDuration) internal {
        setupExpiryTime = now + setupDuration;
    }

    modifier onlyDuringSetup {
        require(now < setupExpiryTime, "Can only perform this action during setup");
        _;
    }
}


// https://docs.peri.finance/contracts/source/interfaces/iaddressresolver
interface IAddressResolver {
    function getAddress(bytes32 name) external view returns (address);

    function getPynth(bytes32 key) external view returns (address);

    function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address);
}


// https://docs.peri.finance/contracts/source/interfaces/ipynth
interface IPynth {
    // Views
    function currencyKey() external view returns (bytes32);

    function transferablePynths(address account) external view returns (uint);

    // Mutative functions
    function transferAndSettle(address to, uint value) external returns (bool);

    function transferFromAndSettle(
        address from,
        address to,
        uint value
    ) external returns (bool);

    // Restricted: used internally to PeriFinance
    function burn(address account, uint amount) external;

    function issue(address account, uint amount) external;
}


// https://docs.peri.finance/contracts/source/interfaces/iissuer
interface IIssuer {
    // Views
    function anyPynthOrPERIRateIsInvalid() external view returns (bool anyRateInvalid);

    function availableCurrencyKeys() external view returns (bytes32[] memory);

    function availablePynthCount() external view returns (uint);

    function availablePynths(uint index) external view returns (IPynth);

    function canBurnPynths(address account) external view returns (bool);

    function collateral(address account) external view returns (uint);

    function collateralisationRatio(address issuer) external view returns (uint);

    function collateralisationRatioAndAnyRatesInvalid(address _issuer)
        external
        view
        returns (uint cratio, bool anyRateIsInvalid);

    function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint debtBalance);

    function lastIssueEvent(address account) external view returns (uint);

    function maxIssuablePynths(address issuer) external view returns (uint maxIssuable);

    function externalTokenQuota(
        address _account,
        uint _addtionalpUSD,
        uint _addtionalExToken,
        bool _isIssue
    ) external view returns (uint);

    function remainingIssuablePynths(address issuer)
        external
        view
        returns (
            uint maxIssuable,
            uint alreadyIssued,
            uint totalSystemDebt
        );

    function pynths(bytes32 currencyKey) external view returns (IPynth);

    function getPynths(bytes32[] calldata currencyKeys) external view returns (IPynth[] memory);

    function pynthsByAddress(address pynthAddress) external view returns (bytes32);

    function totalIssuedPynths(bytes32 currencyKey, bool excludeEtherCollateral) external view returns (uint, bool);

    function transferablePeriFinanceAndAnyRateIsInvalid(address account, uint balance)
        external
        view
        returns (uint transferable, bool anyRateIsInvalid);

    function amountsToFitClaimable(address _account) external view returns (uint burnAmount, uint exTokenAmountToUnstake);

    // Restricted: used internally to PeriFinance
    function issuePynths(
        address _issuer,
        bytes32 _currencyKey,
        uint _issueAmount
    ) external;

    function issueMaxPynths(address _issuer) external;

    function issuePynthsToMaxQuota(address _issuer, bytes32 _currencyKey) external;

    function burnPynths(
        address _from,
        bytes32 _currencyKey,
        uint _burnAmount
    ) external;

    function fitToClaimable(address _from) external;

    function exit(address _from) external;

    function liquidateDelinquentAccount(
        address account,
        uint pusdAmount,
        address liquidator
    ) external returns (uint totalRedeemed, uint amountToLiquidate);
}


// Inheritance


// Internal references


// https://docs.peri.finance/contracts/source/contracts/addressresolver
contract AddressResolver is Owned, IAddressResolver {
    mapping(bytes32 => address) public repository;

    constructor(address _owner) public Owned(_owner) {}

    /* ========== RESTRICTED FUNCTIONS ========== */

    function importAddresses(bytes32[] calldata names, address[] calldata destinations) external onlyOwner {
        require(names.length == destinations.length, "Input lengths must match");

        for (uint i = 0; i < names.length; i++) {
            bytes32 name = names[i];
            address destination = destinations[i];
            repository[name] = destination;
            emit AddressImported(name, destination);
        }
    }

    /* ========= PUBLIC FUNCTIONS ========== */

    function rebuildCaches(MixinResolver[] calldata destinations) external {
        for (uint i = 0; i < destinations.length; i++) {
            destinations[i].rebuildCache();
        }
    }

    /* ========== VIEWS ========== */

    function areAddressesImported(bytes32[] calldata names, address[] calldata destinations) external view returns (bool) {
        for (uint i = 0; i < names.length; i++) {
            if (repository[names[i]] != destinations[i]) {
                return false;
            }
        }
        return true;
    }

    function getAddress(bytes32 name) external view returns (address) {
        return repository[name];
    }

    function requireAndGetAddress(bytes32 name, string calldata reason) external view returns (address) {
        address _foundAddress = repository[name];
        require(_foundAddress != address(0), reason);
        return _foundAddress;
    }

    function getPynth(bytes32 key) external view returns (address) {
        IIssuer issuer = IIssuer(repository["Issuer"]);
        require(address(issuer) != address(0), "Cannot find Issuer address");
        return address(issuer.pynths(key));
    }

    /* ========== EVENTS ========== */

    event AddressImported(bytes32 name, address destination);
}


// solhint-disable payable-fallback

// https://docs.peri.finance/contracts/source/contracts/readproxy
contract ReadProxy is Owned {
    address public target;

    constructor(address _owner) public Owned(_owner) {}

    function setTarget(address _target) external onlyOwner {
        target = _target;
        emit TargetUpdated(target);
    }

    function() external {
        // The basics of a proxy read call
        // Note that msg.sender in the underlying will always be the address of this contract.
        assembly {
            calldatacopy(0, 0, calldatasize)

            // Use of staticcall - this will revert if the underlying function mutates state
            let result := staticcall(gas, sload(target_slot), 0, calldatasize, 0, 0)
            returndatacopy(0, 0, returndatasize)

            if iszero(result) {
                revert(0, returndatasize)
            }
            return(0, returndatasize)
        }
    }

    event TargetUpdated(address newTarget);
}


// Inheritance


// Internal references


// https://docs.peri.finance/contracts/source/contracts/mixinresolver
contract MixinResolver {
    AddressResolver public resolver;

    mapping(bytes32 => address) private addressCache;

    constructor(address _resolver) internal {
        resolver = AddressResolver(_resolver);
    }

    /* ========== INTERNAL FUNCTIONS ========== */

    function combineArrays(bytes32[] memory first, bytes32[] memory second)
        internal
        pure
        returns (bytes32[] memory combination)
    {
        combination = new bytes32[](first.length + second.length);

        for (uint i = 0; i < first.length; i++) {
            combination[i] = first[i];
        }

        for (uint j = 0; j < second.length; j++) {
            combination[first.length + j] = second[j];
        }
    }

    /* ========== PUBLIC FUNCTIONS ========== */

    // Note: this function is public not external in order for it to be overridden and invoked via super in subclasses
    function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {}

    function rebuildCache() public {
        bytes32[] memory requiredAddresses = resolverAddressesRequired();
        // The resolver must call this function whenver it updates its state
        for (uint i = 0; i < requiredAddresses.length; i++) {
            bytes32 name = requiredAddresses[i];
            // Note: can only be invoked once the resolver has all the targets needed added
            address destination =
                resolver.requireAndGetAddress(name, string(abi.encodePacked("Resolver missing target: ", name)));
            addressCache[name] = destination;
            emit CacheUpdated(name, destination);
        }
    }

    /* ========== VIEWS ========== */

    function isResolverCached() external view returns (bool) {
        bytes32[] memory requiredAddresses = resolverAddressesRequired();
        for (uint i = 0; i < requiredAddresses.length; i++) {
            bytes32 name = requiredAddresses[i];
            // false if our cache is invalid or if the resolver doesn't have the required address
            if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
                return false;
            }
        }

        return true;
    }

    /* ========== INTERNAL FUNCTIONS ========== */

    function requireAndGetAddress(bytes32 name) internal view returns (address) {
        address _foundAddress = addressCache[name];
        require(_foundAddress != address(0), string(abi.encodePacked("Missing address: ", name)));
        return _foundAddress;
    }

    /* ========== EVENTS ========== */

    event CacheUpdated(bytes32 name, address destination);
}


// https://docs.peri.finance/contracts/source/interfaces/iflexiblestorage
interface IFlexibleStorage {
    // Views
    function getUIntValue(bytes32 contractName, bytes32 record) external view returns (uint);

    function getUIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (uint[] memory);

    function getIntValue(bytes32 contractName, bytes32 record) external view returns (int);

    function getIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (int[] memory);

    function getAddressValue(bytes32 contractName, bytes32 record) external view returns (address);

    function getAddressValues(bytes32 contractName, bytes32[] calldata records) external view returns (address[] memory);

    function getBoolValue(bytes32 contractName, bytes32 record) external view returns (bool);

    function getBoolValues(bytes32 contractName, bytes32[] calldata records) external view returns (bool[] memory);

    function getBytes32Value(bytes32 contractName, bytes32 record) external view returns (bytes32);

    function getBytes32Values(bytes32 contractName, bytes32[] calldata records) external view returns (bytes32[] memory);

    // Mutative functions
    function deleteUIntValue(bytes32 contractName, bytes32 record) external;

    function deleteIntValue(bytes32 contractName, bytes32 record) external;

    function deleteAddressValue(bytes32 contractName, bytes32 record) external;

    function deleteBoolValue(bytes32 contractName, bytes32 record) external;

    function deleteBytes32Value(bytes32 contractName, bytes32 record) external;

    function setUIntValue(
        bytes32 contractName,
        bytes32 record,
        uint value
    ) external;

    function setUIntValues(
        bytes32 contractName,
        bytes32[] calldata records,
        uint[] calldata values
    ) external;

    function setIntValue(
        bytes32 contractName,
        bytes32 record,
        int value
    ) external;

    function setIntValues(
        bytes32 contractName,
        bytes32[] calldata records,
        int[] calldata values
    ) external;

    function setAddressValue(
        bytes32 contractName,
        bytes32 record,
        address value
    ) external;

    function setAddressValues(
        bytes32 contractName,
        bytes32[] calldata records,
        address[] calldata values
    ) external;

    function setBoolValue(
        bytes32 contractName,
        bytes32 record,
        bool value
    ) external;

    function setBoolValues(
        bytes32 contractName,
        bytes32[] calldata records,
        bool[] calldata values
    ) external;

    function setBytes32Value(
        bytes32 contractName,
        bytes32 record,
        bytes32 value
    ) external;

    function setBytes32Values(
        bytes32 contractName,
        bytes32[] calldata records,
        bytes32[] calldata values
    ) external;
}


// Internal references


// https://docs.peri.finance/contracts/source/contracts/mixinsystemsettings
contract MixinSystemSettings is MixinResolver {
    bytes32 internal constant SETTING_CONTRACT_NAME = "SystemSettings";

    bytes32 internal constant SETTING_WAITING_PERIOD_SECS = "waitingPeriodSecs";
    bytes32 internal constant SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR = "priceDeviationThresholdFactor";
    bytes32 internal constant SETTING_ISSUANCE_RATIO = "issuanceRatio";
    bytes32 internal constant SETTING_FEE_PERIOD_DURATION = "feePeriodDuration";
    bytes32 internal constant SETTING_TARGET_THRESHOLD = "targetThreshold";
    bytes32 internal constant SETTING_LIQUIDATION_DELAY = "liquidationDelay";
    bytes32 internal constant SETTING_LIQUIDATION_RATIO = "liquidationRatio";
    bytes32 internal constant SETTING_LIQUIDATION_PENALTY = "liquidationPenalty";
    bytes32 internal constant SETTING_RATE_STALE_PERIOD = "rateStalePeriod";
    bytes32 internal constant SETTING_EXCHANGE_FEE_RATE = "exchangeFeeRate";
    bytes32 internal constant SETTING_MINIMUM_STAKE_TIME = "minimumStakeTime";
    bytes32 internal constant SETTING_AGGREGATOR_WARNING_FLAGS = "aggregatorWarningFlags";
    bytes32 internal constant SETTING_TRADING_REWARDS_ENABLED = "tradingRewardsEnabled";
    bytes32 internal constant SETTING_DEBT_SNAPSHOT_STALE_TIME = "debtSnapshotStaleTime";
    bytes32 internal constant SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT = "crossDomainDepositGasLimit";
    bytes32 internal constant SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT = "crossDomainEscrowGasLimit";
    bytes32 internal constant SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT = "crossDomainRewardGasLimit";
    bytes32 internal constant SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT = "crossDomainWithdrawalGasLimit";
    bytes32 internal constant SETTING_EXTERNAL_TOKEN_QUOTA = "externalTokenQuota";
    bytes32 internal constant SETTING_BRIDGE_TRANSFER_GAS_COST = "bridgeTransferGasCost";
    bytes32 internal constant SETTING_BRIDGE_CLAIM_GAS_COST = "bridgeClaimGasCost";

    bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";

    enum CrossDomainMessageGasLimits {Deposit, Escrow, Reward, Withdrawal}

    constructor(address _resolver) internal MixinResolver(_resolver) {}

    function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
        addresses = new bytes32[](1);
        addresses[0] = CONTRACT_FLEXIBLESTORAGE;
    }

    function flexibleStorage() internal view returns (IFlexibleStorage) {
        return IFlexibleStorage(requireAndGetAddress(CONTRACT_FLEXIBLESTORAGE));
    }

    function _getGasLimitSetting(CrossDomainMessageGasLimits gasLimitType) internal pure returns (bytes32) {
        if (gasLimitType == CrossDomainMessageGasLimits.Deposit) {
            return SETTING_CROSS_DOMAIN_DEPOSIT_GAS_LIMIT;
        } else if (gasLimitType == CrossDomainMessageGasLimits.Escrow) {
            return SETTING_CROSS_DOMAIN_ESCROW_GAS_LIMIT;
        } else if (gasLimitType == CrossDomainMessageGasLimits.Reward) {
            return SETTING_CROSS_DOMAIN_REWARD_GAS_LIMIT;
        } else if (gasLimitType == CrossDomainMessageGasLimits.Withdrawal) {
            return SETTING_CROSS_DOMAIN_WITHDRAWAL_GAS_LIMIT;
        } else {
            revert("Unknown gas limit type");
        }
    }

    function getCrossDomainMessageGasLimit(CrossDomainMessageGasLimits gasLimitType) internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, _getGasLimitSetting(gasLimitType));
    }

    function getTradingRewardsEnabled() internal view returns (bool) {
        return flexibleStorage().getBoolValue(SETTING_CONTRACT_NAME, SETTING_TRADING_REWARDS_ENABLED);
    }

    function getWaitingPeriodSecs() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_WAITING_PERIOD_SECS);
    }

    function getPriceDeviationThresholdFactor() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_PRICE_DEVIATION_THRESHOLD_FACTOR);
    }

    function getIssuanceRatio() internal view returns (uint) {
        // lookup on flexible storage directly for gas savings (rather than via SystemSettings)
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ISSUANCE_RATIO);
    }

    function getFeePeriodDuration() internal view returns (uint) {
        // lookup on flexible storage directly for gas savings (rather than via SystemSettings)
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_FEE_PERIOD_DURATION);
    }

    function getTargetThreshold() internal view returns (uint) {
        // lookup on flexible storage directly for gas savings (rather than via SystemSettings)
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_TARGET_THRESHOLD);
    }

    function getLiquidationDelay() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_DELAY);
    }

    function getLiquidationRatio() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_RATIO);
    }

    function getLiquidationPenalty() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_LIQUIDATION_PENALTY);
    }

    function getRateStalePeriod() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_RATE_STALE_PERIOD);
    }

    function getExchangeFeeRate(bytes32 currencyKey) internal view returns (uint) {
        return
            flexibleStorage().getUIntValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_EXCHANGE_FEE_RATE, currencyKey))
            );
    }

    function getMinimumStakeTime() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_MINIMUM_STAKE_TIME);
    }

    function getAggregatorWarningFlags() internal view returns (address) {
        return flexibleStorage().getAddressValue(SETTING_CONTRACT_NAME, SETTING_AGGREGATOR_WARNING_FLAGS);
    }

    function getDebtSnapshotStaleTime() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_DEBT_SNAPSHOT_STALE_TIME);
    }

    function getExternalTokenQuota() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_EXTERNAL_TOKEN_QUOTA);
    }

    function getBridgeTransferGasCost() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_BRIDGE_TRANSFER_GAS_COST);
    }

    function getBridgeClaimGasCost() internal view returns (uint) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_BRIDGE_CLAIM_GAS_COST);
    }
}


// https://docs.peri.finance/contracts/source/interfaces/ifeepool
interface IFeePool {
    // Views

    // solhint-disable-next-line func-name-mixedcase
    function FEE_ADDRESS() external view returns (address);

    function feesAvailable(address account) external view returns (uint, uint);

    function feePeriodDuration() external view returns (uint);

    function isFeesClaimable(address account) external view returns (bool);

    function targetThreshold() external view returns (uint);

    function totalFeesAvailable() external view returns (uint);

    function totalRewardsAvailable() external view returns (uint);

    function everDistributedFeeRewards() external view returns (bool);

    function everAllocatedFeeRewards() external view returns (bool);

    // Mutative Functions
    function claimFees() external returns (bool);

    function claimOnBehalf(address claimingForAddress) external returns (bool);

    function closeCurrentFeePeriod() external;

    // Restricted: used internally to PeriFinance
    function appendAccountIssuanceRecord(
        address account,
        uint lockedAmount,
        uint debtEntryIndex
    ) external;

    function recordFeePaid(uint pUSDAmount) external;

    function setRewardsToDistribute(uint amount) external;

    function distributeFeeRewards() external;

    function allocateFeeRewards(uint amount) external;
}


/**
 * @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, 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");
        uint256 c = a - b;

        return c;
    }

    /**
     * @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) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts 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) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts 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;
    }
}


// Libraries


// https://docs.peri.finance/contracts/source/libraries/safedecimalmath
library SafeDecimalMath {
    using SafeMath for uint;

    /* Number of decimal places in the representations. */
    uint8 public constant decimals = 18;
    uint8 public constant highPrecisionDecimals = 27;

    /* The number representing 1.0. */
    uint public constant UNIT = 10**uint(decimals);

    /* The number representing 1.0 for higher fidelity numbers. */
    uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals);
    uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals);

    /**
     * @return Provides an interface to UNIT.
     */
    function unit() external pure returns (uint) {
        return UNIT;
    }

    /**
     * @return Provides an interface to PRECISE_UNIT.
     */
    function preciseUnit() external pure returns (uint) {
        return PRECISE_UNIT;
    }

    /**
     * @return The result of multiplying x and y, interpreting the operands as fixed-point
     * decimals.
     *
     * @dev A unit factor is divided out after the product of x and y is evaluated,
     * so that product must be less than 2**256. As this is an integer division,
     * the internal division always rounds down. This helps save on gas. Rounding
     * is more expensive on gas.
     */
    function multiplyDecimal(uint x, uint y) internal pure returns (uint) {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        return x.mul(y) / UNIT;
    }

    /**
     * @return The result of safely multiplying x and y, interpreting the operands
     * as fixed-point decimals of the specified precision unit.
     *
     * @dev The operands should be in the form of a the specified unit factor which will be
     * divided out after the product of x and y is evaluated, so that product must be
     * less than 2**256.
     *
     * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
     * Rounding is useful when you need to retain fidelity for small decimal numbers
     * (eg. small fractions or percentages).
     */
    function _multiplyDecimalRound(
        uint x,
        uint y,
        uint precisionUnit
    ) private pure returns (uint) {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        uint quotientTimesTen = x.mul(y) / (precisionUnit / 10);

        if (quotientTimesTen % 10 >= 5) {
            quotientTimesTen += 10;
        }

        return quotientTimesTen / 10;
    }

    /**
     * @return The result of safely multiplying x and y, interpreting the operands
     * as fixed-point decimals of a precise unit.
     *
     * @dev The operands should be in the precise unit factor which will be
     * divided out after the product of x and y is evaluated, so that product must be
     * less than 2**256.
     *
     * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
     * Rounding is useful when you need to retain fidelity for small decimal numbers
     * (eg. small fractions or percentages).
     */
    function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
        return _multiplyDecimalRound(x, y, PRECISE_UNIT);
    }

    /**
     * @return The result of safely multiplying x and y, interpreting the operands
     * as fixed-point decimals of a standard unit.
     *
     * @dev The operands should be in the standard unit factor which will be
     * divided out after the product of x and y is evaluated, so that product must be
     * less than 2**256.
     *
     * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
     * Rounding is useful when you need to retain fidelity for small decimal numbers
     * (eg. small fractions or percentages).
     */
    function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) {
        return _multiplyDecimalRound(x, y, UNIT);
    }

    /**
     * @return The result of safely dividing x and y. The return value is a high
     * precision decimal.
     *
     * @dev y is divided after the product of x and the standard precision unit
     * is evaluated, so the product of x and UNIT must be less than 2**256. As
     * this is an integer division, the result is always rounded down.
     * This helps save on gas. Rounding is more expensive on gas.
     */
    function divideDecimal(uint x, uint y) internal pure returns (uint) {
        /* Reintroduce the UNIT factor that will be divided out by y. */
        return x.mul(UNIT).div(y);
    }

    /**
     * @return The result of safely dividing x and y. The return value is as a rounded
     * decimal in the precision unit specified in the parameter.
     *
     * @dev y is divided after the product of x and the specified precision unit
     * is evaluated, so the product of x and the specified precision unit must
     * be less than 2**256. The result is rounded to the nearest increment.
     */
    function _divideDecimalRound(
        uint x,
        uint y,
        uint precisionUnit
    ) private pure returns (uint) {
        uint resultTimesTen = x.mul(precisionUnit * 10).div(y);

        if (resultTimesTen % 10 >= 5) {
            resultTimesTen += 10;
        }

        return resultTimesTen / 10;
    }

    /**
     * @return The result of safely dividing x and y. The return value is as a rounded
     * standard precision decimal.
     *
     * @dev y is divided after the product of x and the standard precision unit
     * is evaluated, so the product of x and the standard precision unit must
     * be less than 2**256. The result is rounded to the nearest increment.
     */
    function divideDecimalRound(uint x, uint y) internal pure returns (uint) {
        return _divideDecimalRound(x, y, UNIT);
    }

    /**
     * @return The result of safely dividing x and y. The return value is as a rounded
     * high precision decimal.
     *
     * @dev y is divided after the product of x and the high precision unit
     * is evaluated, so the product of x and the high precision unit must
     * be less than 2**256. The result is rounded to the nearest increment.
     */
    function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
        return _divideDecimalRound(x, y, PRECISE_UNIT);
    }

    /**
     * @dev Convert a standard decimal representation to a high precision one.
     */
    function decimalToPreciseDecimal(uint i) internal pure returns (uint) {
        return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
    }

    /**
     * @dev Convert a high precision decimal to a standard decimal representation.
     */
    function preciseDecimalToDecimal(uint i) internal pure returns (uint) {
        uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);

        if (quotientTimesTen % 10 >= 5) {
            quotientTimesTen += 10;
        }

        return quotientTimesTen / 10;
    }

    /**
     * @dev Round down the value with given number
     */
    function roundDownDecimal(uint x, uint d) internal pure returns (uint) {
        return x.div(10**d).mul(10**d);
    }

    /**
     * @dev Round up the value with given number
     */
    function roundUpDecimal(uint x, uint d) internal pure returns (uint) {
        uint _decimal = 10**d;

        if (x % _decimal > 0) {
            x = x.add(10**d);
        }

        return x.div(_decimal).mul(_decimal);
    }
}


// https://docs.peri.finance/contracts/source/interfaces/ierc20
interface IERC20 {
    // ERC20 Optional Views
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function decimals() external view returns (uint8);

    // Views
    function totalSupply() external view returns (uint);

    function balanceOf(address owner) external view returns (uint);

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

    // Mutative functions
    function transfer(address to, uint value) external returns (bool);

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

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

    // Events
    event Transfer(address indexed from, address indexed to, uint value);

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


// https://docs.peri.finance/contracts/source/interfaces/isystemstatus
interface ISystemStatus {
    struct Status {
        bool canSuspend;
        bool canResume;
    }

    struct Suspension {
        bool suspended;
        // reason is an integer code,
        // 0 => no reason, 1 => upgrading, 2+ => defined by system usage
        uint248 reason;
    }

    // Views
    function accessControl(bytes32 section, address account) external view returns (bool canSuspend, bool canResume);

    function requireSystemActive() external view;

    function requireIssuanceActive() external view;

    function requireExchangeActive() external view;

    function requireExchangeBetweenPynthsAllowed(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;

    function requirePynthActive(bytes32 currencyKey) external view;

    function requirePynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view;

    function systemSuspension() external view returns (bool suspended, uint248 reason);

    function issuanceSuspension() external view returns (bool suspended, uint248 reason);

    function exchangeSuspension() external view returns (bool suspended, uint248 reason);

    function pynthExchangeSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);

    function pynthSuspension(bytes32 currencyKey) external view returns (bool suspended, uint248 reason);

    function getPynthExchangeSuspensions(bytes32[] calldata pynths)
        external
        view
        returns (bool[] memory exchangeSuspensions, uint256[] memory reasons);

    function getPynthSuspensions(bytes32[] calldata pynths)
        external
        view
        returns (bool[] memory suspensions, uint256[] memory reasons);

    // Restricted functions
    function suspendPynth(bytes32 currencyKey, uint256 reason) external;

    function updateAccessControl(
        bytes32 section,
        address account,
        bool canSuspend,
        bool canResume
    ) external;
}


interface IVirtualPynth {
    // Views
    function balanceOfUnderlying(address account) external view returns (uint);

    function rate() external view returns (uint);

    function readyToSettle() external view returns (bool);

    function secsLeftInWaitingPeriod() external view returns (uint);

    function settled() external view returns (bool);

    function pynth() external view returns (IPynth);

    // Mutative functions
    function settle(address account) external;
}


// https://docs.peri.finance/contracts/source/interfaces/iperiFinance
interface IPeriFinance {
    // Views
    function getRequiredAddress(bytes32 contractName) external view returns (address);

    function anyPynthOrPERIRateIsInvalid() external view returns (bool anyRateInvalid);

    function availableCurrencyKeys() external view returns (bytes32[] memory);

    function availablePynthCount() external view returns (uint);

    function availablePynths(uint index) external view returns (IPynth);

    function collateral(address account) external view returns (uint);

    function collateralisationRatio(address issuer) external view returns (uint);

    function debtBalanceOf(address issuer, bytes32 currencyKey) external view returns (uint);

    function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);

    function maxIssuablePynths(address issuer) external view returns (uint maxIssuable);

    function remainingIssuablePynths(address issuer)
        external
        view
        returns (
            uint maxIssuable,
            uint alreadyIssued,
            uint totalSystemDebt
        );

    function pynths(bytes32 currencyKey) external view returns (IPynth);

    function pynthsByAddress(address pynthAddress) external view returns (bytes32);

    function totalIssuedPynths(bytes32 currencyKey) external view returns (uint);

    function totalIssuedPynthsExcludeEtherCollateral(bytes32 currencyKey) external view returns (uint);

    function transferablePeriFinance(address account) external view returns (uint transferable);

    function amountsToFitClaimable(address account) external view returns (uint burnAmount, uint exTokenAmountToUnstake);

    // Mutative Functions
    function issuePynths(bytes32 _currencyKey, uint _issueAmount) external;

    function issueMaxPynths() external;

    function issuePynthsToMaxQuota(bytes32 _currencyKey) external;

    function burnPynths(bytes32 _currencyKey, uint _burnAmount) external;

    function fitToClaimable() external;

    function exit() external;

    function exchange(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    ) external returns (uint amountReceived);

    function exchangeOnBehalf(
        address exchangeForAddress,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    ) external returns (uint amountReceived);

    function exchangeWithTracking(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address originator,
        bytes32 trackingCode
    ) external returns (uint amountReceived);

    function exchangeOnBehalfWithTracking(
        address exchangeForAddress,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address originator,
        bytes32 trackingCode
    ) external returns (uint amountReceived);

    function exchangeWithVirtual(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        bytes32 trackingCode
    ) external returns (uint amountReceived, IVirtualPynth vPynth);

    function mint(address _user, uint _amount) external returns (bool);

    function inflationalMint(uint _networkDebtShare) external returns (bool);

    function settle(bytes32 currencyKey)
        external
        returns (
            uint reclaimed,
            uint refunded,
            uint numEntries
        );

    // Liquidations
    function liquidateDelinquentAccount(address account, uint pusdAmount) external returns (bool);

    // Restricted Functions

    function mintSecondary(address account, uint amount) external;

    function mintSecondaryRewards(uint amount) external;

    function burnSecondary(address account, uint amount) external;
}


// Inheritance


// Libraries


// Internal references


// https://docs.peri.finance/contracts/source/contracts/feepoolstate
contract FeePoolState is Owned, LimitedSetup {
    using SafeMath for uint;
    using SafeDecimalMath for uint;

    /* ========== STATE VARIABLES ========== */

    uint8 public constant FEE_PERIOD_LENGTH = 6;

    address public feePool;

    // The IssuanceData activity that's happened in a fee period.
    struct IssuanceData {
        uint debtPercentage;
        uint debtEntryIndex;
    }

    // The IssuanceData activity that's happened in a fee period.
    mapping(address => IssuanceData[FEE_PERIOD_LENGTH]) public accountIssuanceLedger;

    constructor(address _owner, IFeePool _feePool) public Owned(_owner) LimitedSetup(6 weeks) {
        feePool = address(_feePool);
    }

    /* ========== SETTERS ========== */

    /**
     * @notice set the FeePool contract as it is the only authority to be able to call
     * appendAccountIssuanceRecord with the onlyFeePool modifer
     * @dev Must be set by owner when FeePool logic is upgraded
     */
    function setFeePool(IFeePool _feePool) external onlyOwner {
        feePool = address(_feePool);
    }

    /* ========== VIEWS ========== */

    /**
     * @notice Get an accounts issuanceData for
     * @param account users account
     * @param index Index in the array to retrieve. Upto FEE_PERIOD_LENGTH
     */
    function getAccountsDebtEntry(address account, uint index)
        public
        view
        returns (uint debtPercentage, uint debtEntryIndex)
    {
        require(index < FEE_PERIOD_LENGTH, "index exceeds the FEE_PERIOD_LENGTH");

        debtPercentage = accountIssuanceLedger[account][index].debtPercentage;
        debtEntryIndex = accountIssuanceLedger[account][index].debtEntryIndex;
    }

    /**
     * @notice Find the oldest debtEntryIndex for the corresponding closingDebtIndex
     * @param account users account
     * @param closingDebtIndex the last periods debt index on close
     */
    function applicableIssuanceData(address account, uint closingDebtIndex) external view returns (uint, uint) {
        IssuanceData[FEE_PERIOD_LENGTH] memory issuanceData = accountIssuanceLedger[account];

        // We want to use the user's debtEntryIndex at when the period closed
        // Find the oldest debtEntryIndex for the corresponding closingDebtIndex
        for (uint i = 0; i < FEE_PERIOD_LENGTH; i++) {
            if (closingDebtIndex >= issuanceData[i].debtEntryIndex) {
                return (issuanceData[i].debtPercentage, issuanceData[i].debtEntryIndex);
            }
        }
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    /**
     * @notice Logs an accounts issuance data in the current fee period which is then stored historically
     * @param account Message.Senders account address
     * @param debtRatio Debt of this account as a percentage of the global debt.
     * @param debtEntryIndex The index in the global debt ledger. periFinance.periFinanceState().issuanceData(account)
     * @param currentPeriodStartDebtIndex The startingDebtIndex of the current fee period
     * @dev onlyFeePool to call me on periFinance.issue() & periFinance.burn() calls to store the locked PERI
     * per fee period so we know to allocate the correct proportions of fees and rewards per period
      accountIssuanceLedger[account][0] has the latest locked amount for the current period. This can be update as many time
      accountIssuanceLedger[account][1-2] has the last locked amount for a previous period they minted or burned
     */
    function appendAccountIssuanceRecord(
        address account,
        uint debtRatio,
        uint debtEntryIndex,
        uint currentPeriodStartDebtIndex
    ) external onlyFeePool {
        // Is the current debtEntryIndex within this fee period
        if (accountIssuanceLedger[account][0].debtEntryIndex < currentPeriodStartDebtIndex) {
            // If its older then shift the previous IssuanceData entries periods down to make room for the new one.
            issuanceDataIndexOrder(account);
        }

        // Always store the latest IssuanceData entry at [0]
        accountIssuanceLedger[account][0].debtPercentage = debtRatio;
        accountIssuanceLedger[account][0].debtEntryIndex = debtEntryIndex;
    }

    /**
     * @notice Pushes down the entire array of debt ratios per fee period
     */
    function issuanceDataIndexOrder(address account) private {
        for (uint i = FEE_PERIOD_LENGTH - 2; i < FEE_PERIOD_LENGTH; i--) {
            uint next = i + 1;
            accountIssuanceLedger[account][next].debtPercentage = accountIssuanceLedger[account][i].debtPercentage;
            accountIssuanceLedger[account][next].debtEntryIndex = accountIssuanceLedger[account][i].debtEntryIndex;
        }
    }

    /**
     * @notice Import issuer data from periFinanceState.issuerData on FeePeriodClose() block #
     * @dev Only callable by the contract owner, and only for 6 weeks after deployment.
     * @param accounts Array of issuing addresses
     * @param ratios Array of debt ratios
     * @param periodToInsert The Fee Period to insert the historical records into
     * @param feePeriodCloseIndex An accounts debtEntryIndex is valid when within the fee peroid,
     * since the input ratio will be an average of the pervious periods it just needs to be
     * > recentFeePeriods[periodToInsert].startingDebtIndex
     * < recentFeePeriods[periodToInsert - 1].startingDebtIndex
     */
    function importIssuerData(
        address[] calldata accounts,
        uint[] calldata ratios,
        uint periodToInsert,
        uint feePeriodCloseIndex
    ) external onlyOwner onlyDuringSetup {
        require(accounts.length == ratios.length, "Length mismatch");

        for (uint i = 0; i < accounts.length; i++) {
            accountIssuanceLedger[accounts[i]][periodToInsert].debtPercentage = ratios[i];
            accountIssuanceLedger[accounts[i]][periodToInsert].debtEntryIndex = feePeriodCloseIndex;
            emit IssuanceDebtRatioEntry(accounts[i], ratios[i], feePeriodCloseIndex);
        }
    }

    /* ========== MODIFIERS ========== */

    modifier onlyFeePool {
        require(msg.sender == address(feePool), "Only the FeePool contract can perform this action");
        _;
    }

    /* ========== Events ========== */
    event IssuanceDebtRatioEntry(address indexed account, uint debtRatio, uint feePeriodCloseIndex);
}


// Inheritance


// https://docs.peri.finance/contracts/source/contracts/state
contract State is Owned {
    // the address of the contract that can modify variables
    // this can only be changed by the owner of this contract
    address public associatedContract;

    constructor(address _associatedContract) internal {
        // This contract is abstract, and thus cannot be instantiated directly
        require(owner != address(0), "Owner must be set");

        associatedContract = _associatedContract;
        emit AssociatedContractUpdated(_associatedContract);
    }

    /* ========== SETTERS ========== */

    // Change the associated contract to a new address
    function setAssociatedContract(address _associatedContract) external onlyOwner {
        associatedContract = _associatedContract;
        emit AssociatedContractUpdated(_associatedContract);
    }

    /* ========== MODIFIERS ========== */

    modifier onlyAssociatedContract {
        require(msg.sender == associatedContract, "Only the associated contract can perform this action");
        _;
    }

    /* ========== EVENTS ========== */

    event AssociatedContractUpdated(address associatedContract);
}


// Inheritance


// https://docs.peri.finance/contracts/source/contracts/eternalstorage
/**
 * @notice  This contract is based on the code available from this blog
 * https://blog.colony.io/writing-upgradeable-contracts-in-solidity-6743f0eecc88/
 * Implements support for storing a keccak256 key and value pairs. It is the more flexible
 * and extensible option. This ensures data schema changes can be implemented without
 * requiring upgrades to the storage contract.
 */
contract EternalStorage is Owned, State {
    constructor(address _owner, address _associatedContract) public Owned(_owner) State(_associatedContract) {}

    /* ========== DATA TYPES ========== */
    mapping(bytes32 => uint) internal UIntStorage;
    mapping(bytes32 => string) internal StringStorage;
    mapping(bytes32 => address) internal AddressStorage;
    mapping(bytes32 => bytes) internal BytesStorage;
    mapping(bytes32 => bytes32) internal Bytes32Storage;
    mapping(bytes32 => bool) internal BooleanStorage;
    mapping(bytes32 => int) internal IntStorage;

    // UIntStorage;
    function getUIntValue(bytes32 record) external view returns (uint) {
        return UIntStorage[record];
    }

    function setUIntValue(bytes32 record, uint value) external onlyAssociatedContract {
        UIntStorage[record] = value;
    }

    function deleteUIntValue(bytes32 record) external onlyAssociatedContract {
        delete UIntStorage[record];
    }

    // StringStorage
    function getStringValue(bytes32 record) external view returns (string memory) {
        return StringStorage[record];
    }

    function setStringValue(bytes32 record, string calldata value) external onlyAssociatedContract {
        StringStorage[record] = value;
    }

    function deleteStringValue(bytes32 record) external onlyAssociatedContract {
        delete StringStorage[record];
    }

    // AddressStorage
    function getAddressValue(bytes32 record) external view returns (address) {
        return AddressStorage[record];
    }

    function setAddressValue(bytes32 record, address value) external onlyAssociatedContract {
        AddressStorage[record] = value;
    }

    function deleteAddressValue(bytes32 record) external onlyAssociatedContract {
        delete AddressStorage[record];
    }

    // BytesStorage
    function getBytesValue(bytes32 record) external view returns (bytes memory) {
        return BytesStorage[record];
    }

    function setBytesValue(bytes32 record, bytes calldata value) external onlyAssociatedContract {
        BytesStorage[record] = value;
    }

    function deleteBytesValue(bytes32 record) external onlyAssociatedContract {
        delete BytesStorage[record];
    }

    // Bytes32Storage
    function getBytes32Value(bytes32 record) external view returns (bytes32) {
        return Bytes32Storage[record];
    }

    function setBytes32Value(bytes32 record, bytes32 value) external onlyAssociatedContract {
        Bytes32Storage[record] = value;
    }

    function deleteBytes32Value(bytes32 record) external onlyAssociatedContract {
        delete Bytes32Storage[record];
    }

    // BooleanStorage
    function getBooleanValue(bytes32 record) external view returns (bool) {
        return BooleanStorage[record];
    }

    function setBooleanValue(bytes32 record, bool value) external onlyAssociatedContract {
        BooleanStorage[record] = value;
    }

    function deleteBooleanValue(bytes32 record) external onlyAssociatedContract {
        delete BooleanStorage[record];
    }

    // IntStorage
    function getIntValue(bytes32 record) external view returns (int) {
        return IntStorage[record];
    }

    function setIntValue(bytes32 record, int value) external onlyAssociatedContract {
        IntStorage[record] = value;
    }

    function deleteIntValue(bytes32 record) external onlyAssociatedContract {
        delete IntStorage[record];
    }
}


// Inheritance


// https://docs.peri.finance/contracts/source/contracts/feepooleternalstorage
contract FeePoolEternalStorage is EternalStorage, LimitedSetup {
    bytes32 internal constant LAST_FEE_WITHDRAWAL = "last_fee_withdrawal";

    constructor(address _owner, address _feePool) public EternalStorage(_owner, _feePool) LimitedSetup(6 weeks) {}

    function importFeeWithdrawalData(address[] calldata accounts, uint[] calldata feePeriodIDs)
        external
        onlyOwner
        onlyDuringSetup
    {
        require(accounts.length == feePeriodIDs.length, "Length mismatch");

        for (uint8 i = 0; i < accounts.length; i++) {
            this.setUIntValue(keccak256(abi.encodePacked(LAST_FEE_WITHDRAWAL, accounts[i])), feePeriodIDs[i]);
        }
    }
}


// https://docs.peri.finance/contracts/source/interfaces/iexchanger
interface IExchanger {
    // Views
    function calculateAmountAfterSettlement(
        address from,
        bytes32 currencyKey,
        uint amount,
        uint refunded
    ) external view returns (uint amountAfterSettlement);

    function isPynthRateInvalid(bytes32 currencyKey) external view returns (bool);

    function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) external view returns (uint);

    function settlementOwing(address account, bytes32 currencyKey)
        external
        view
        returns (
            uint reclaimAmount,
            uint rebateAmount,
            uint numEntries
        );

    function hasWaitingPeriodOrSettlementOwing(address account, bytes32 currencyKey) external view returns (bool);

    function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
        external
        view
        returns (uint exchangeFeeRate);

    function getAmountsForExchange(
        uint sourceAmount,
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    )
        external
        view
        returns (
            uint amountReceived,
            uint fee,
            uint exchangeFeeRate
        );

    function priceDeviationThresholdFactor() external view returns (uint);

    function waitingPeriodSecs() external view returns (uint);

    // Mutative functions
    function exchange(
        address from,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address destinationAddress
    ) external returns (uint amountReceived);

    function exchangeOnBehalf(
        address exchangeForAddress,
        address from,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    ) external returns (uint amountReceived);

    function exchangeWithTracking(
        address from,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address destinationAddress,
        address originator,
        bytes32 trackingCode
    ) external returns (uint amountReceived);

    function exchangeOnBehalfWithTracking(
        address exchangeForAddress,
        address from,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address originator,
        bytes32 trackingCode
    ) external returns (uint amountReceived);

    function exchangeWithVirtual(
        address from,
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        address destinationAddress,
        bytes32 trackingCode
    ) external returns (uint amountReceived, IVirtualPynth vPynth);

    function settle(address from, bytes32 currencyKey)
        external
        returns (
            uint reclaimed,
            uint refunded,
            uint numEntries
        );

    function setLastExchangeRateForPynth(bytes32 currencyKey, uint rate) external;

    function suspendPynthWithInvalidRate(bytes32 currencyKey) external;
}


// https://docs.peri.finance/contracts/source/interfaces/iperiFinancestate
interface IPeriFinanceState {
    // Views
    function debtLedger(uint index) external view returns (uint);

    function issuanceData(address account) external view returns (uint initialDebtOwnership, uint debtEntryIndex);

    function debtLedgerLength() external view returns (uint);

    function hasIssued(address account) external view returns (bool);

    function lastDebtLedgerEntry() external view returns (uint);

    // Mutative functions
    function incrementTotalIssuerCount() external;

    function decrementTotalIssuerCount() external;

    function setCurrentIssuanceData(address account, uint initialDebtOwnership) external;

    function appendDebtLedgerValue(uint value) external;

    function clearIssuanceData(address account) external;
}


pragma experimental ABIEncoderV2;

library VestingEntries {
    struct VestingEntry {
        uint64 endTime;
        uint256 escrowAmount;
    }
    struct VestingEntryWithID {
        uint64 endTime;
        uint256 escrowAmount;
        uint256 entryID;
    }
}

interface IRewardEscrowV2 {
    // Views
    function balanceOf(address account) external view returns (uint);

    function numVestingEntries(address account) external view returns (uint);

    function totalEscrowedAccountBalance(address account) external view returns (uint);

    function totalVestedAccountBalance(address account) external view returns (uint);

    function getVestingQuantity(address account, uint256[] calldata entryIDs) external view returns (uint);

    function getVestingSchedules(
        address account,
        uint256 index,
        uint256 pageSize
    ) external view returns (VestingEntries.VestingEntryWithID[] memory);

    function getAccountVestingEntryIDs(
        address account,
        uint256 index,
        uint256 pageSize
    ) external view returns (uint256[] memory);

    function getVestingEntryClaimable(address account, uint256 entryID) external view returns (uint);

    function getVestingEntry(address account, uint256 entryID) external view returns (uint64, uint256);

    // Mutative functions
    function vest(uint256[] calldata entryIDs) external;

    function createEscrowEntry(
        address beneficiary,
        uint256 deposit,
        uint256 duration
    ) external;

    function appendVestingEntry(
        address account,
        uint256 quantity,
        uint256 duration
    ) external;

    function migrateVestingSchedule(address _addressToMigrate) external;

    function migrateAccountEscrowBalances(
        address[] calldata accounts,
        uint256[] calldata escrowBalances,
        uint256[] calldata vestedBalances
    ) external;

    // Account Merging
    function startMergingWindow() external;

    function mergeAccount(address accountToMerge, uint256[] calldata entryIDs) external;

    function nominateAccountToMerge(address account) external;

    function accountMergingIsOpen() external view returns (bool);

    // L2 Migration
    function importVestingEntries(
        address account,
        uint256 escrowedAmount,
        VestingEntries.VestingEntry[] calldata vestingEntries
    ) external;

    // Return amount of PERI transfered to PeriFinanceBridgeToOptimism deposit contract
    function burnForMigration(address account, uint256[] calldata entryIDs)
        external
        returns (uint256 escrowedAccountBalance, VestingEntries.VestingEntry[] memory vestingEntries);
}


// https://docs.peri.finance/contracts/source/interfaces/idelegateapprovals
interface IDelegateApprovals {
    // Views
    function canBurnFor(address authoriser, address delegate) external view returns (bool);

    function canIssueFor(address authoriser, address delegate) external view returns (bool);

    function canClaimFor(address authoriser, address delegate) external view returns (bool);

    function canExchangeFor(address authoriser, address delegate) external view returns (bool);

    // Mutative
    function approveAllDelegatePowers(address delegate) external;

    function removeAllDelegatePowers(address delegate) external;

    function approveBurnOnBehalf(address delegate) external;

    function removeBurnOnBehalf(address delegate) external;

    function approveIssueOnBehalf(address delegate) external;

    function removeIssueOnBehalf(address delegate) external;

    function approveClaimOnBehalf(address delegate) external;

    function removeClaimOnBehalf(address delegate) external;

    function approveExchangeOnBehalf(address delegate) external;

    function removeExchangeOnBehalf(address delegate) external;
}


// https://docs.peri.finance/contracts/source/interfaces/irewardsdistribution
interface IRewardsDistribution {
    // Structs
    struct DistributionData {
        address destination;
        uint amount;
    }

    // Views
    function authority() external view returns (address);

    function distributions(uint index) external view returns (address destination, uint amount); // DistributionData

    function distributionsLength() external view returns (uint);

    // Mutative Functions
    function distributeRewards(uint amount) external returns (bool);
}


// https://docs.peri.finance/contracts/source/interfaces/iethercollateralsusd
interface IEtherCollateralpUSD {
    // Views
    function totalIssuedPynths() external view returns (uint256);

    function totalLoansCreated() external view returns (uint256);

    function totalOpenLoanCount() external view returns (uint256);

    // Mutative functions
    function openLoan(uint256 _loanAmount) external payable returns (uint256 loanID);

    function closeLoan(uint256 loanID) external;

    function liquidateUnclosedLoan(address _loanCreatorsAddress, uint256 _loanID) external;

    function depositCollateral(address account, uint256 loanID) external payable;

    function withdrawCollateral(uint256 loanID, uint256 withdrawAmount) external;

    function repayLoan(
        address _loanCreatorsAddress,
        uint256 _loanID,
        uint256 _repayAmount
    ) external;
}


interface ICollateralManager {
    // Manager information
    function hasCollateral(address collateral) external view returns (bool);

    function isPynthManaged(bytes32 currencyKey) external view returns (bool);

    // State information
    function long(bytes32 pynth) external view returns (uint amount);

    function short(bytes32 pynth) external view returns (uint amount);

    function totalLong() external view returns (uint pusdValue, bool anyRateIsInvalid);

    function totalShort() external view returns (uint pusdValue, bool anyRateIsInvalid);

    function getBorrowRate() external view returns (uint borrowRate, bool anyRateIsInvalid);

    function getShortRate(bytes32 pynth) external view returns (uint shortRate, bool rateIsInvalid);

    function getRatesAndTime(uint index)
        external
        view
        returns (
            uint entryRate,
            uint lastRate,
            uint lastUpdated,
            uint newIndex
        );

    function getShortRatesAndTime(bytes32 currency, uint index)
        external
        view
        returns (
            uint entryRate,
            uint lastRate,
            uint lastUpdated,
            uint newIndex
        );

    function exceedsDebtLimit(uint amount, bytes32 currency) external view returns (bool canIssue, bool anyRateIsInvalid);

    function arePynthsAndCurrenciesSet(bytes32[] calldata requiredPynthNamesInResolver, bytes32[] calldata pynthKeys)
        external
        view
        returns (bool);

    function areShortablePynthsSet(bytes32[] calldata requiredPynthNamesInResolver, bytes32[] calldata pynthKeys)
        external
        view
        returns (bool);

    // Loans
    function getNewLoanId() external returns (uint id);

    // Manager mutative
    function addCollaterals(address[] calldata collaterals) external;

    function removeCollaterals(address[] calldata collaterals) external;

    function addPynths(bytes32[] calldata pynthNamesInResolver, bytes32[] calldata pynthKeys) external;

    function removePynths(bytes32[] calldata pynths, bytes32[] calldata pynthKeys) external;

    function addShortablePynths(bytes32[2][] calldata requiredPynthAndInverseNamesInResolver, bytes32[] calldata pynthKeys)
        external;

    function removeShortablePynths(bytes32[] calldata pynths) external;

    // State mutative
    function updateBorrowRates(uint rate) external;

    function updateShortRates(bytes32 currency, uint rate) external;

    function incrementLongs(bytes32 pynth, uint amount) external;

    function decrementLongs(bytes32 pynth, uint amount) external;

    function incrementShorts(bytes32 pynth, uint amount) external;

    function decrementShorts(bytes32 pynth, uint amount) external;
}


interface ICrossChainManager {
    // View Functions
    function crossChainState() external view returns (address);

    function debtManager() external view returns (address);

    function userIssuanceDataForTotalNetwork(address) external view returns (uint, uint);

    function getTotalNetworkAdaptedTotalSystemValue(bytes32 currencyKey) external view returns (uint, bool);

    function currentTotalNetworkDebt() external view returns (uint);

    function totalNetworkDebtAtIndex(uint index) external view returns (uint);

    function networkDebtPercentageAtIndex(uint) external view returns (uint);

    function totalNetworkDebtEntryLength() external view returns (uint);

    function currentNetworkDebtPercentage() external view returns (uint);

    // Mutative functions
    function setCrossChainState(address) external;

    function setDebtManager(address) external;

    function appendTotalNetworkDebt(uint) external;

    function addTotalNetworkDebt(uint) external;

    function subtractTotalNetworkDebt(uint) external;

    function setCrossNetworkUserDebt(address, uint) external;

    function clearCrossNetworkUserDebt(address) external;
}


// Inheritance


// Libraries


// Internal references


// https://docs.peri.finance/contracts/source/contracts/feepool
contract FeePool is Owned, Proxyable, LimitedSetup, MixinSystemSettings, IFeePool {
    using SafeMath for uint;
    using SafeDecimalMath for uint;

    // Where fees are pooled in pUSD.
    address public constant FEE_ADDRESS = 0xfeEFEEfeefEeFeefEEFEEfEeFeefEEFeeFEEFEeF;

    // pUSD currencyKey. Fees stored and paid in pUSD
    bytes32 private pUSD = "pUSD";

    // This struct represents the issuance activity that's happened in a fee period.
    struct FeePeriod {
        uint64 feePeriodId;
        uint64 startingDebtIndex;
        uint64 startTime;
        uint feesToDistribute;
        uint feesClaimed;
        uint rewardsToDistribute;
        uint rewardsClaimed;
    }

    // A staker(mintr) can claim from the previous fee period (7 days) only.
    // Fee Periods stored and managed from [0], such that [0] is always
    // the current active fee period which is not claimable until the
    // public function closeCurrentFeePeriod() is called closing the
    // current weeks collected fees. [1] is last weeks feeperiod
    uint8 public constant FEE_PERIOD_LENGTH = 2;

    FeePeriod[FEE_PERIOD_LENGTH] private _recentFeePeriods;
    uint256 private _currentFeePeriod;
    bool private _everDistributedFeeRewards;
    bool private _everAllocatedFeeRewards;
    uint256 public feeRewardsToBeAllocated;

    uint256 public quotaTolerance;

    /* ========== ADDRESS RESOLVER CONFIGURATION ========== */

    bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
    bytes32 private constant CONTRACT_PERIFINANCE = "PeriFinance";
    bytes32 private constant CONTRACT_FEEPOOLSTATE = "FeePoolState";
    bytes32 private constant CONTRACT_FEEPOOLETERNALSTORAGE = "FeePoolEternalStorage";
    bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";
    bytes32 private constant CONTRACT_ISSUER = "Issuer";
    bytes32 private constant CONTRACT_PERIFINANCESTATE = "PeriFinanceState";
    bytes32 private constant CONTRACT_REWARDESCROW_V2 = "RewardEscrowV2";
    bytes32 private constant CONTRACT_DELEGATEAPPROVALS = "DelegateApprovals";
    bytes32 private constant CONTRACT_ETH_COLLATERAL_PUSD = "EtherCollateralpUSD";
    bytes32 private constant CONTRACT_COLLATERALMANAGER = "CollateralManager";
    bytes32 private constant CONTRACT_REWARDSDISTRIBUTION = "RewardsDistribution";
    bytes32 private constant CONTRACT_CROSSCHAINMANAGER = "CrossChainManager";

    /* ========== ETERNAL STORAGE CONSTANTS ========== */

    bytes32 private constant LAST_FEE_WITHDRAWAL = "last_fee_withdrawal";

    constructor(
        address payable _proxy,
        address _owner,
        address _resolver
    ) public Owned(_owner) Proxyable(_proxy) LimitedSetup(3 weeks) MixinSystemSettings(_resolver) {
        // Set our initial fee period
        _recentFeePeriodsStorage(0).feePeriodId = 1;
        _recentFeePeriodsStorage(0).startTime = uint64(now);
    }

    /* ========== VIEWS ========== */
    function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
        bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
        bytes32[] memory newAddresses = new bytes32[](13);
        newAddresses[0] = CONTRACT_SYSTEMSTATUS;
        newAddresses[1] = CONTRACT_PERIFINANCE;
        newAddresses[2] = CONTRACT_FEEPOOLSTATE;
        newAddresses[3] = CONTRACT_FEEPOOLETERNALSTORAGE;
        newAddresses[4] = CONTRACT_EXCHANGER;
        newAddresses[5] = CONTRACT_ISSUER;
        newAddresses[6] = CONTRACT_PERIFINANCESTATE;
        newAddresses[7] = CONTRACT_REWARDESCROW_V2;
        newAddresses[8] = CONTRACT_DELEGATEAPPROVALS;
        newAddresses[9] = CONTRACT_ETH_COLLATERAL_PUSD;
        newAddresses[10] = CONTRACT_REWARDSDISTRIBUTION;
        newAddresses[11] = CONTRACT_COLLATERALMANAGER;
        newAddresses[12] = CONTRACT_CROSSCHAINMANAGER;
        addresses = combineArrays(existingAddresses, newAddresses);
    }

    function systemStatus() internal view returns (ISystemStatus) {
        return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
    }

    function periFinance() internal view returns (IPeriFinance) {
        return IPeriFinance(requireAndGetAddress(CONTRACT_PERIFINANCE));
    }

    function feePoolState() internal view returns (FeePoolState) {
        return FeePoolState(requireAndGetAddress(CONTRACT_FEEPOOLSTATE));
    }

    function feePoolEternalStorage() internal view returns (FeePoolEternalStorage) {
        return FeePoolEternalStorage(requireAndGetAddress(CONTRACT_FEEPOOLETERNALSTORAGE));
    }

    function exchanger() internal view returns (IExchanger) {
        return IExchanger(requireAndGetAddress(CONTRACT_EXCHANGER));
    }

    function etherCollateralpUSD() internal view returns (IEtherCollateralpUSD) {
        return IEtherCollateralpUSD(requireAndGetAddress(CONTRACT_ETH_COLLATERAL_PUSD));
    }

    function collateralManager() internal view returns (ICollateralManager) {
        return ICollateralManager(requireAndGetAddress(CONTRACT_COLLATERALMANAGER));
    }

    function crossChainManager() internal view returns (ICrossChainManager) {
        return ICrossChainManager(requireAndGetAddress(CONTRACT_CROSSCHAINMANAGER));
    }

    function issuer() internal view returns (IIssuer) {
        return IIssuer(requireAndGetAddress(CONTRACT_ISSUER));
    }

    function periFinanceState() internal view returns (IPeriFinanceState) {
        return IPeriFinanceState(requireAndGetAddress(CONTRACT_PERIFINANCESTATE));
    }

    function rewardEscrowV2() internal view returns (IRewardEscrowV2) {
        return IRewardEscrowV2(requireAndGetAddress(CONTRACT_REWARDESCROW_V2));
    }

    function delegateApprovals() internal view returns (IDelegateApprovals) {
        return IDelegateApprovals(requireAndGetAddress(CONTRACT_DELEGATEAPPROVALS));
    }

    function rewardsDistribution() internal view returns (IRewardsDistribution) {
        return IRewardsDistribution(requireAndGetAddress(CONTRACT_REWARDSDISTRIBUTION));
    }

    function issuanceRatio() external view returns (uint) {
        return getIssuanceRatio();
    }

    function feePeriodDuration() external view returns (uint) {
        return getFeePeriodDuration();
    }

    function targetThreshold() external view returns (uint) {
        return getTargetThreshold();
    }

    function recentFeePeriods(uint index)
        external
        view
        returns (
            uint64 feePeriodId,
            uint64 startingDebtIndex,
            uint64 startTime,
            uint feesToDistribute,
            uint feesClaimed,
            uint rewardsToDistribute,
            uint rewardsClaimed
        )
    {
        FeePeriod memory feePeriod = _recentFeePeriodsStorage(index);
        return (
            feePeriod.feePeriodId,
            feePeriod.startingDebtIndex,
            feePeriod.startTime,
            feePeriod.feesToDistribute,
            feePeriod.feesClaimed,
            feePeriod.rewardsToDistribute,
            feePeriod.rewardsClaimed
        );
    }

    function everDistributedFeeRewards() external view returns (bool) {
        return _everDistributedFeeRewards;
    }

    function everAllocatedFeeRewards() external view returns (bool) {
        return _everAllocatedFeeRewards;
    }

    function allocatedOtherNetworkFeeRewards() external view returns (uint) {
        return _allocatedOtherNetworkFeeRewards();
    }

    function _recentFeePeriodsStorage(uint index) internal view returns (FeePeriod storage) {
        return _recentFeePeriods[(_currentFeePeriod + index) % FEE_PERIOD_LENGTH];
    }

    function _allocatedOtherNetworkFeeRewards() internal view returns (uint allocatedFeesForOtherNetworks) {
        uint otherNetworksShare = SafeDecimalMath.preciseUnit().sub(crossChainManager().currentNetworkDebtPercentage());

        allocatedFeesForOtherNetworks = _recentFeePeriodsStorage(0)
            .feesToDistribute
            .decimalToPreciseDecimal()
            .multiplyDecimalRoundPrecise(otherNetworksShare)
            .preciseDecimalToDecimal();
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function distributeFeeRewards() external optionalProxy onlyDebtManager {
        require(getFeePeriodDuration() > 0, "Fee Period Duration not set");
        require(
            _recentFeePeriodsStorage(0).startTime <= (now - getFeePeriodDuration()),
            "distributing fee reward not yet available"
        );

        require(_everDistributedFeeRewards == false, "Distributing fee rewards is possible only once in a period");
        _everDistributedFeeRewards = true;

        uint _allocatedFeeRewards = _allocatedOtherNetworkFeeRewards();

        if (_allocatedFeeRewards <= 0) {
            feeRewardsToBeAllocated = 0;
            return;
        }

        issuer().pynths(pUSD).burn(FEE_ADDRESS, _allocatedFeeRewards);

        feeRewardsToBeAllocated = _allocatedFeeRewards;
        _recentFeePeriodsStorage(0).feesToDistribute = _recentFeePeriodsStorage(0).feesToDistribute.sub(
            _allocatedFeeRewards
        );
    }

    function allocateFeeRewards(uint amount) external optionalProxy onlyDebtManager {
        require(getFeePeriodDuration() > 0, "Fee Period Duration not set");
        require(
            _recentFeePeriodsStorage(0).startTime <= (now - getFeePeriodDuration()),
            "allocating fee reward not yet available"
        );

        require(_everAllocatedFeeRewards == false, "Allocating fee rewards is possible only once in a period");
        _everAllocatedFeeRewards = true;

        if (amount == 0) return;

        uint currentNetworkAllocatedFeeRewards =
            amount
                .decimalToPreciseDecimal()
                .multiplyDecimalRoundPrecise(crossChainManager().currentNetworkDebtPercentage())
                .preciseDecimalToDecimal();

        issuer().pynths(pUSD).issue(FEE_ADDRESS, currentNetworkAllocatedFeeRewards);

        _recentFeePeriodsStorage(0).feesToDistribute = _recentFeePeriodsStorage(0).feesToDistribute.add(
            currentNetworkAllocatedFeeRewards
        );
    }

    /**
     * @notice Logs an accounts issuance data per fee period
     * @param account Message.Senders account address
     * @param debtRatio Debt percentage this account has locked after minting or burning their pynth
     * @param debtEntryIndex The index in the global debt ledger. periFinanceState.issuanceData(account)
     * @dev onlyIssuer to call me on periFinance.issue() & periFinance.burn() calls to store the locked PERI
     * per fee period so we know to allocate the correct proportions of fees and rewards per period
     */
    function appendAccountIssuanceRecord(
        address account,
        uint debtRatio,
        uint debtEntryIndex
    ) external onlyIssuerAndPeriFinanceState {
        feePoolState().appendAccountIssuanceRecord(
            account,
            debtRatio,
            debtEntryIndex,
            _recentFeePeriodsStorage(0).startingDebtIndex
        );

        emitIssuanceDebtRatioEntry(account, debtRatio, debtEntryIndex, _recentFeePeriodsStorage(0).startingDebtIndex);
    }

    /**
     * @notice The Exchanger contract informs us when fees are paid.
     * @param amount susd amount in fees being paid.
     */
    function recordFeePaid(uint amount) external onlyInternalContracts {
        // Keep track off fees in pUSD in the open fee pool period.
        _recentFeePeriodsStorage(0).feesToDistribute = _recentFeePeriodsStorage(0).feesToDistribute.add(amount);
    }

    /**
     * @notice The RewardsDistribution contract informs us how many PERI rewards are sent to RewardEscrow to be claimed.
     */
    function setRewardsToDistribute(uint amount) external {
        address rewardsAuthority = address(rewardsDistribution());
        require(messageSender == rewardsAuthority || msg.sender == rewardsAuthority, "Caller is not rewardsAuthority");
        // Add the amount of PERI rewards to distribute on top of any rolling unclaimed amount
        _recentFeePeriodsStorage(0).rewardsToDistribute = _recentFeePeriodsStorage(0).rewardsToDistribute.add(amount);
    }

    function setQuotaTolerance(uint _val) external onlyOwner {
        require(_val < SafeDecimalMath.unit(), "Tolerance value cannot exceeds 1");

        quotaTolerance = _val;
    }

    /**
     * @notice Close the current fee period and start a new one.
     */
    function closeCurrentFeePeriod() external issuanceActive {
        require(getFeePeriodDuration() > 0, "Fee Period Duration not set");
        require(_recentFeePeriodsStorage(0).startTime <= (now - getFeePeriodDuration()), "Too early to close fee period");
        require(
            _everDistributedFeeRewards || _everAllocatedFeeRewards,
            "fee rewards should be distributed or allocated before closing period"
        );

        // Note:  when FEE_PERIOD_LENGTH = 2, periodClosing is the current period & periodToRollover is the last open claimable period
        FeePeriod storage periodClosing = _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 2);
        FeePeriod storage periodToRollover = _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 1);

        // Any unclaimed fees from the last period in the array roll back one period.
        // Because of the subtraction here, they're effectively proportionally redistributed to those who
        // have already claimed from the old period, available in the new period.
        // The subtraction is important so we don't create a ticking time bomb of an ever growing
        // number of fees that can never decrease and will eventually overflow at the end of the fee pool.
        _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 2).feesToDistribute = periodToRollover
            .feesToDistribute
            .sub(periodToRollover.feesClaimed)
            .add(periodClosing.feesToDistribute);
        _recentFeePeriodsStorage(FEE_PERIOD_LENGTH - 2).rewardsToDistribute = periodToRollover
            .rewardsToDistribute
            .sub(periodToRollover.rewardsClaimed)
            .add(periodClosing.rewardsToDistribute);

        // Shift the previous fee periods across to make room for the new one.
        _currentFeePeriod = _currentFeePeriod.add(FEE_PERIOD_LENGTH).sub(1).mod(FEE_PERIOD_LENGTH);

        // Clear the first element of the array to make sure we don't have any stale values.
        delete _recentFeePeriods[_currentFeePeriod];

        // Open up the new fee period.
        // Increment periodId from the recent closed period feePeriodId
        _recentFeePeriodsStorage(0).feePeriodId = uint64(uint256(_recentFeePeriodsStorage(1).feePeriodId).add(1));
        _recentFeePeriodsStorage(0).startingDebtIndex = uint64(periFinanceState().debtLedgerLength());
        _recentFeePeriodsStorage(0).startTime = uint64(now);

        // allow fee rewards to be distributed when the period is closed
        _everDistributedFeeRewards = false;
        _everAllocatedFeeRewards = false;
        emitFeePeriodClosed(_recentFeePeriodsStorage(1).feePeriodId);
    }

    /**
     * @notice Claim fees for last period when available or not already withdrawn.
     */
    function claimFees() external issuanceActive optionalProxy returns (bool) {
        return _claimFees(messageSender);
    }

    /**
     * @notice Delegated claimFees(). Call from the deletegated address
     * and the fees will be sent to the claimingForAddress.
     * approveClaimOnBehalf() must be called first to approve the deletage address
     * @param claimingForAddress The account you are claiming fees for
     */
    function claimOnBehalf(address claimingForAddress) external issuanceActive optionalProxy returns (bool) {
        require(delegateApprovals().canClaimFor(claimingForAddress, messageSender), "Not approved to claim on behalf");

        return _claimFees(claimingForAddress);
    }

    function _claimFees(address claimingAddress) internal returns (bool) {
        uint rewardsPaid = 0;
        uint feesPaid = 0;
        uint availableFees;
        uint availableRewards;

        // Address won't be able to claim fees if it is too far below the target c-ratio.
        // It will need to burn pynths then try claiming again.
        (bool feesClaimable, bool anyRateIsInvalid) = _isFeesClaimableAndAnyRatesInvalid(claimingAddress);

        require(feesClaimable, "C-Ratio below penalty threshold");

        require(!anyRateIsInvalid, "A pynth or PERI rate is invalid");

        // Get the claimingAddress available fees and rewards
        (availableFees, availableRewards) = feesAvailable(claimingAddress);

        require(
            availableFees > 0 || availableRewards > 0,
            "No fees or rewards available for period, or fees already claimed"
        );

        // Record the address has claimed for this period
        _setLastFeeWithdrawal(claimingAddress, _recentFeePeriodsStorage(1).feePeriodId);

        if (availableFees > 0) {
            // Record the fee payment in our recentFeePeriods
            feesPaid = _recordFeePayment(availableFees);

            // Send them their fees
            _payFees(claimingAddress, feesPaid);
        }

        if (availableRewards > 0) {
            // Record the reward payment in our recentFeePeriods
            rewardsPaid = _recordRewardPayment(availableRewards);

            // Send them their rewards
            _payRewards(claimingAddress, rewardsPaid);
        }

        emitFeesClaimed(claimingAddress, feesPaid, rewardsPaid);

        return true;
    }

    /**
     * @notice Admin function to import the FeePeriod data from the previous contract
     */
    function importFeePeriod(
        uint feePeriodIndex,
        uint feePeriodId,
        uint startingDebtIndex,
        uint startTime,
        uint feesToDistribute,
        uint feesClaimed,
        uint rewardsToDistribute,
        uint rewardsClaimed
    ) public optionalProxy_onlyOwner onlyDuringSetup {
        require(startingDebtIndex <= periFinanceState().debtLedgerLength(), "Cannot import bad data");

        _recentFeePeriods[_currentFeePeriod.add(feePeriodIndex).mod(FEE_PERIOD_LENGTH)] = FeePeriod({
            feePeriodId: uint64(feePeriodId),
            startingDebtIndex: uint64(startingDebtIndex),
            startTime: uint64(startTime),
            feesToDistribute: feesToDistribute,
            feesClaimed: feesClaimed,
            rewardsToDistribute: rewardsToDistribute,
            rewardsClaimed: rewardsClaimed
        });
    }

    /**
     * @notice Record the fee payment in our recentFeePeriods.
     * @param pUSDAmount The amount of fees priced in pUSD.
     */
    function _recordFeePayment(uint pUSDAmount) internal returns (uint) {
        // Don't assign to the parameter
        uint remainingToAllocate = pUSDAmount;

        uint feesPaid;
        // Start at the oldest period and record the amount, moving to newer periods
        // until we've exhausted the amount.
        // The condition checks for overflow because we're going to 0 with an unsigned int.
        for (uint i = FEE_PERIOD_LENGTH - 1; i < FEE_PERIOD_LENGTH; i--) {
            uint feesAlreadyClaimed = _recentFeePeriodsStorage(i).feesClaimed;
            uint delta = _recentFeePeriodsStorage(i).feesToDistribute.sub(feesAlreadyClaimed);

            if (delta > 0) {
                // Take the smaller of the amount left to claim in the period and the amount we need to allocate
                uint amountInPeriod = delta < remainingToAllocate ? delta : remainingToAllocate;

                _recentFeePeriodsStorage(i).feesClaimed = feesAlreadyClaimed.add(amountInPeriod);
                remainingToAllocate = remainingToAllocate.sub(amountInPeriod);
                feesPaid = feesPaid.add(amountInPeriod);

                // No need to continue iterating if we've recorded the whole amount;
                if (remainingToAllocate == 0) return feesPaid;

                // We've exhausted feePeriods to distribute and no fees remain in last period
                // User last to claim would in this scenario have their remainder slashed
                if (i == 0 && remainingToAllocate > 0) {
                    remainingToAllocate = 0;
                }
            }
        }

        return feesPaid;
    }

    /**
     * @notice Record the reward payment in our recentFeePeriods.
     * @param periAmount The amount of PERI tokens.
     */
    function _recordRewardPayment(uint periAmount) internal returns (uint) {
        // Don't assign to the parameter
        uint remainingToAllocate = periAmount;

        uint rewardPaid;

        // Start at the oldest period and record the amount, moving to newer periods
        // until we've exhausted the amount.
        // The condition checks for overflow because we're going to 0 with an unsigned int.
        for (uint i = FEE_PERIOD_LENGTH - 1; i < FEE_PERIOD_LENGTH; i--) {
            uint toDistribute =
                _recentFeePeriodsStorage(i).rewardsToDistribute.sub(_recentFeePeriodsStorage(i).rewardsClaimed);

            if (toDistribute > 0) {
                // Take the smaller of the amount left to claim in the period and the amount we need to allocate
                uint amountInPeriod = toDistribute < remainingToAllocate ? toDistribute : remainingToAllocate;

                _recentFeePeriodsStorage(i).rewardsClaimed = _recentFeePeriodsStorage(i).rewardsClaimed.add(amountInPeriod);
                remainingToAllocate = remainingToAllocate.sub(amountInPeriod);
                rewardPaid = rewardPaid.add(amountInPeriod);

                // No need to continue iterating if we've recorded the whole amount;
                if (remainingToAllocate == 0) return rewardPaid;

                // We've exhausted feePeriods to distribute and no rewards remain in last period
                // User last to claim would in this scenario have their remainder slashed
                // due to rounding up of PreciseDecimal
                if (i == 0 && remainingToAllocate > 0) {
                    remainingToAllocate = 0;
                }
            }
        }
        return rewardPaid;
    }

    /**
     * @notice Send the fees to claiming address.
     * @param account The address to send the fees to.
     * @param pUSDAmount The amount of fees priced in pUSD.
     */
    function _payFees(address account, uint pUSDAmount) internal notFeeAddress(account) {
        // Grab the pUSD Pynth
        IPynth pUSDPynth = issuer().pynths(pUSD);

        // NOTE: we do not control the FEE_ADDRESS so it is not possible to do an
        // ERC20.approve() transaction to allow this feePool to call ERC20.transferFrom
        // to the accounts address

        // Burn the source amount
        pUSDPynth.burn(FEE_ADDRESS, pUSDAmount);

        // Mint their new pynths
        pUSDPynth.issue(account, pUSDAmount);
    }

    /**
     * @notice Send the rewards to claiming address - will be locked in rewardEscrow.
     * @param account The address to send the fees to.
     * @param periAmount The amount of PERI.
     */
    function _payRewards(address account, uint periAmount) internal notFeeAddress(account) {
        /* Escrow the tokens for 1 year. */
        uint escrowDuration = 52 weeks;

        // Record vesting entry for claiming address and amount
        // PERI already minted to rewardEscrow balance
        rewardEscrowV2().appendVestingEntry(account, periAmount, escrowDuration);
    }

    /**
     * @notice The total fees available in the system to be withdrawnn in pUSD
     */
    function totalFeesAvailable() external view returns (uint) {
        uint totalFees = 0;

        // Fees in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalFees = totalFees.add(_recentFeePeriodsStorage(i).feesToDistribute);
            totalFees = totalFees.sub(_recentFeePeriodsStorage(i).feesClaimed);
        }

        return totalFees;
    }

    /**
     * @notice The total PERI rewards available in the system to be withdrawn
     */
    function totalRewardsAvailable() external view returns (uint) {
        uint totalRewards = 0;

        // Rewards in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalRewards = totalRewards.add(_recentFeePeriodsStorage(i).rewardsToDistribute);
            totalRewards = totalRewards.sub(_recentFeePeriodsStorage(i).rewardsClaimed);
        }

        return totalRewards;
    }

    /**
     * @notice The fees available to be withdrawn by a specific account, priced in pUSD
     * @dev Returns two amounts, one for fees and one for PERI rewards
     */
    function feesAvailable(address account) public view returns (uint, uint) {
        // Add up the fees
        uint[2][FEE_PERIOD_LENGTH] memory userFees = feesByPeriod(account);

        uint totalFees = 0;
        uint totalRewards = 0;

        // Fees & Rewards in fee period [0] are not yet available for withdrawal
        for (uint i = 1; i < FEE_PERIOD_LENGTH; i++) {
            totalFees = totalFees.add(userFees[i][0]);
            totalRewards = totalRewards.add(userFees[i][1]);
        }

        // And convert totalFees to pUSD
        // Return totalRewards as is in PERI amount
        return (totalFees, totalRewards);
    }

    function _isFeesClaimableAndAnyRatesInvalid(address account) internal view returns (bool, bool) {
        // External token staked amount should not over the quota limit.
        uint accountExternalTokenQuota = issuer().externalTokenQuota(account, 0, 0, true);
        if (
            accountExternalTokenQuota > getExternalTokenQuota().multiplyDecimal(SafeDecimalMath.unit().add(quotaTolerance))
        ) {
            return (false, false);
        }

        // Threshold is calculated from ratio % above the target ratio (issuanceRatio).
        //  0  <  10%:   Claimable
        // 10% > above:  Unable to claim
        (uint ratio, bool anyRateIsInvalid) = issuer().collateralisationRatioAndAnyRatesInvalid(account);
        uint targetRatio = getIssuanceRatio();

        // Claimable if collateral ratio below target ratio
        if (ratio < targetRatio) {
            return (true, anyRateIsInvalid);
        }

        // Calculate the threshold for collateral ratio before fees can't be claimed.
        uint ratio_threshold = targetRatio.multiplyDecimal(SafeDecimalMath.unit().add(getTargetThreshold()));

        // Not claimable if collateral ratio above threshold
        if (ratio > ratio_threshold) {
            return (false, anyRateIsInvalid);
        }

        return (true, anyRateIsInvalid);
    }

    function isFeesClaimable(address account) external view returns (bool feesClaimable) {
        (feesClaimable, ) = _isFeesClaimableAndAnyRatesInvalid(account);
    }

    /**
     * @notice Calculates fees by period for an account, priced in pUSD
     * @param account The address you want to query the fees for
     */
    function feesByPeriod(address account) public view returns (uint[2][FEE_PERIOD_LENGTH] memory results) {
        // What's the user's debt entry index and the debt they owe to the system at current feePeriod
        uint userOwnershipPercentage;
        uint debtEntryIndex;
        FeePoolState _feePoolState = feePoolState();

        (userOwnershipPercentage, debtEntryIndex) = _feePoolState.getAccountsDebtEntry(account, 0);

        // If they don't have any debt ownership and they never minted, they don't have any fees.
        // User ownership can reduce to 0 if user burns all pynths,
        // however they could have fees applicable for periods they had minted in before so we check debtEntryIndex.
        if (debtEntryIndex == 0 && userOwnershipPercentage == 0) {
            uint[2][FEE_PERIOD_LENGTH] memory nullResults;
            return nullResults;
        }

        // The [0] fee period is not yet ready to claim, but it is a fee period that they can have
        // fees owing for, so we need to report on it anyway.
        uint feesFromPeriod;
        uint rewardsFromPeriod;
        (feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(0, userOwnershipPercentage, debtEntryIndex);

        results[0][0] = feesFromPeriod;
        results[0][1] = rewardsFromPeriod;

        // Retrieve user's last fee claim by periodId
        uint lastFeeWithdrawal = getLastFeeWithdrawal(account);

        // Go through our fee periods from the oldest feePeriod[FEE_PERIOD_LENGTH - 1] and figure out what we owe them.
        // Condition checks for periods > 0
        for (uint i = FEE_PERIOD_LENGTH - 1; i > 0; i--) {
            uint next = i - 1;
            uint nextPeriodStartingDebtIndex = _recentFeePeriodsStorage(next).startingDebtIndex;

            // We can skip the period, as no debt minted during period (next period's startingDebtIndex is still 0)
            if (nextPeriodStartingDebtIndex > 0 && lastFeeWithdrawal < _recentFeePeriodsStorage(i).feePeriodId) {
                // We calculate a feePeriod's closingDebtIndex by looking at the next feePeriod's startingDebtIndex
                // we can use the most recent issuanceData[0] for the current feePeriod
                // else find the applicableIssuanceData for the feePeriod based on the StartingDebtIndex of the period
                uint closingDebtIndex = uint256(nextPeriodStartingDebtIndex).sub(1);

                // Gas optimisation - to reuse debtEntryIndex if found new applicable one
                // if applicable is 0,0 (none found) we keep most recent one from issuanceData[0]
                // return if userOwnershipPercentage = 0)
                (userOwnershipPercentage, debtEntryIndex) = _feePoolState.applicableIssuanceData(account, closingDebtIndex);

                (feesFromPeriod, rewardsFromPeriod) = _feesAndRewardsFromPeriod(i, userOwnershipPercentage, debtEntryIndex);

                results[i][0] = feesFromPeriod;
                results[i][1] = rewardsFromPeriod;
            }
        }
    }

    /**
     * @notice ownershipPercentage is a high precision decimals uint based on
     * wallet's debtPercentage. Gives a precise amount of the feesToDistribute
     * for fees in the period. Precision factor is removed before results are
     * returned.
     * @dev The reported fees owing for the current period [0] are just a
     * running balance until the fee period closes
     */
    function _feesAndRewardsFromPeriod(
        uint period,
        uint ownershipPercentage,
        uint debtEntryIndex
    ) internal view returns (uint, uint) {
        // If it's zero, they haven't issued, and they have no fees OR rewards.
        if (ownershipPercentage == 0) return (0, 0);

        uint debtOwnershipForPeriod = ownershipPercentage;

        // If period has closed we want to calculate debtPercentage for the period
        if (period > 0) {
            uint closingDebtIndex = uint256(_recentFeePeriodsStorage(period - 1).startingDebtIndex).sub(1);
            debtOwnershipForPeriod = _effectiveDebtRatioForPeriod(closingDebtIndex, ownershipPercentage, debtEntryIndex);
        }

        // Calculate their percentage of the fees / rewards in this period
        // This is a high precision integer.
        uint feesFromPeriod = _recentFeePeriodsStorage(period).feesToDistribute.multiplyDecimal(debtOwnershipForPeriod);

        uint rewardsFromPeriod =
            _recentFeePeriodsStorage(period).rewardsToDistribute.multiplyDecimal(debtOwnershipForPeriod);

        return (feesFromPeriod.preciseDecimalToDecimal(), rewardsFromPeriod.preciseDecimalToDecimal());
    }

    function _effectiveDebtRatioForPeriod(
        uint closingDebtIndex,
        uint ownershipPercentage,
        uint debtEntryIndex
    ) internal view returns (uint) {
        // Figure out their global debt percentage delta at end of fee Period.
        // This is a high precision integer.
        IPeriFinanceState _periFinanceState = periFinanceState();
        uint feePeriodDebtOwnership =
            _periFinanceState
                .debtLedger(closingDebtIndex)
                .divideDecimalRoundPrecise(_periFinanceState.debtLedger(debtEntryIndex))
                .multiplyDecimalRoundPrecise(ownershipPercentage);

        return feePeriodDebtOwnership;
    }

    function effectiveDebtRatioForPeriod(address account, uint period) external view returns (uint) {
        require(period != 0, "Current period is not closed yet");
        require(period < FEE_PERIOD_LENGTH, "Exceeds the FEE_PERIOD_LENGTH");

        // If the period being checked is uninitialised then return 0. This is only at the start of the system.
        if (_recentFeePeriodsStorage(period - 1).startingDebtIndex == 0) return 0;

        uint closingDebtIndex = uint256(_recentFeePeriodsStorage(period - 1).startingDebtIndex).sub(1);

        uint ownershipPercentage;
        uint debtEntryIndex;
        (ownershipPercentage, debtEntryIndex) = feePoolState().applicableIssuanceData(account, closingDebtIndex);

        // internal function will check closingDebtIndex has corresponding debtLedger entry
        return _effectiveDebtRatioForPeriod(closingDebtIndex, ownershipPercentage, debtEntryIndex);
    }

    /**
     * @notice Get the feePeriodID of the last claim this account made
     * @param _claimingAddress account to check the last fee period ID claim for
     * @return uint of the feePeriodID this account last claimed
     */
    function getLastFeeWithdrawal(address _claimingAddress) public view returns (uint) {
        return feePoolEternalStorage().getUIntValue(keccak256(abi.encodePacked(LAST_FEE_WITHDRAWAL, _claimingAddress)));
    }

    /**
     * @notice Calculate the collateral ratio before user is blocked from claiming.
     */
    function getPenaltyThresholdRatio() public view returns (uint) {
        return getIssuanceRatio().multiplyDecimal(SafeDecimalMath.unit().add(getTargetThreshold()));
    }

    /**
     * @notice Set the feePeriodID of the last claim this account made
     * @param _claimingAddress account to set the last feePeriodID claim for
     * @param _feePeriodID the feePeriodID this account claimed fees for
     */
    function _setLastFeeWithdrawal(address _claimingAddress, uint _feePeriodID) internal {
        feePoolEternalStorage().setUIntValue(
            keccak256(abi.encodePacked(LAST_FEE_WITHDRAWAL, _claimingAddress)),
            _feePeriodID
        );
    }

    function _debtManager() internal view returns (address) {
        return crossChainManager().debtManager();
    }

    /* ========== Modifiers ========== */
    modifier onlyInternalContracts {
        bool isExchanger = msg.sender == address(exchanger());
        bool isPynth = issuer().pynthsByAddress(msg.sender) != bytes32(0);
        bool isEtherCollateralpUSD = msg.sender == address(etherCollateralpUSD());
        bool isCollateral = collateralManager().hasCollateral(msg.sender);

        require(isExchanger || isPynth || isEtherCollateralpUSD || isCollateral, "Only Internal Contracts");
        _;
    }

    modifier onlyIssuerAndPeriFinanceState {
        bool isIssuer = msg.sender == address(issuer());
        bool isPeriFinanceState = msg.sender == address(periFinanceState());
        require(isIssuer || isPeriFinanceState, "Issuer and PeriFinanceState only");
        _;
    }

    modifier onlyDebtManager {
        bool isDebtManager = messageSender == _debtManager();
        require(isDebtManager, "debt manager only");
        _;
    }

    modifier notFeeAddress(address account) {
        require(account != FEE_ADDRESS, "Fee address not allowed");
        _;
    }

    modifier issuanceActive() {
        systemStatus().requireIssuanceActive();
        _;
    }

    /* ========== Proxy Events ========== */

    event IssuanceDebtRatioEntry(
        address indexed account,
        uint debtRatio,
        uint debtEntryIndex,
        uint feePeriodStartingDebtIndex
    );
    bytes32 private constant ISSUANCEDEBTRATIOENTRY_SIG =
        keccak256("IssuanceDebtRatioEntry(address,uint256,uint256,uint256)");

    function emitIssuanceDebtRatioEntry(
        address account,
        uint debtRatio,
        uint debtEntryIndex,
        uint feePeriodStartingDebtIndex
    ) internal {
        proxy._emit(
            abi.encode(debtRatio, debtEntryIndex, feePeriodStartingDebtIndex),
            2,
            ISSUANCEDEBTRATIOENTRY_SIG,
            bytes32(uint256(uint160(account))),
            0,
            0
        );
    }

    event FeePeriodClosed(uint feePeriodId);
    bytes32 private constant FEEPERIODCLOSED_SIG = keccak256("FeePeriodClosed(uint256)");

    function emitFeePeriodClosed(uint feePeriodId) internal {
        proxy._emit(abi.encode(feePeriodId), 1, FEEPERIODCLOSED_SIG, 0, 0, 0);
    }

    event FeesClaimed(address account, uint pUSDAmount, uint periRewards);
    bytes32 private constant FEESCLAIMED_SIG = keccak256("FeesClaimed(address,uint256,uint256)");

    function emitFeesClaimed(
        address account,
        uint pUSDAmount,
        uint periRewards
    ) internal {
        proxy._emit(abi.encode(account, pUSDAmount, periRewards), 1, FEESCLAIMED_SIG, 0, 0, 0);
    }
}

Contract ABI

[{"inputs":[{"internalType":"address payable","name":"_proxy","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_resolver","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"CacheUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feePeriodId","type":"uint256"}],"name":"FeePeriodClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"pUSDAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"periRewards","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"debtRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"debtEntryIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feePeriodStartingDebtIndex","type":"uint256"}],"name":"IssuanceDebtRatioEntry","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"proxyAddress","type":"address"}],"name":"ProxyUpdated","type":"event"},{"constant":true,"inputs":[],"name":"FEE_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FEE_PERIOD_LENGTH","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"allocateFeeRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"allocatedOtherNetworkFeeRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"debtRatio","type":"uint256"},{"internalType":"uint256","name":"debtEntryIndex","type":"uint256"}],"name":"appendAccountIssuanceRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"claimingForAddress","type":"address"}],"name":"claimOnBehalf","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"closeCurrentFeePeriod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"distributeFeeRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"period","type":"uint256"}],"name":"effectiveDebtRatioForPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"everAllocatedFeeRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"everDistributedFeeRewards","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feePeriodDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeRewardsToBeAllocated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"feesAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"feesByPeriod","outputs":[{"internalType":"uint256[2][2]","name":"results","type":"uint256[2][2]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_claimingAddress","type":"address"}],"name":"getLastFeeWithdrawal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPenaltyThresholdRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"feePeriodIndex","type":"uint256"},{"internalType":"uint256","name":"feePeriodId","type":"uint256"},{"internalType":"uint256","name":"startingDebtIndex","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"feesToDistribute","type":"uint256"},{"internalType":"uint256","name":"feesClaimed","type":"uint256"},{"internalType":"uint256","name":"rewardsToDistribute","type":"uint256"},{"internalType":"uint256","name":"rewardsClaimed","type":"uint256"}],"name":"importFeePeriod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"integrationProxy","outputs":[{"internalType":"contract Proxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isFeesClaimable","outputs":[{"internalType":"bool","name":"feesClaimable","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isResolverCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"issuanceRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"messageSender","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proxy","outputs":[{"internalType":"contract Proxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"quotaTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"rebuildCache","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"recentFeePeriods","outputs":[{"internalType":"uint64","name":"feePeriodId","type":"uint64"},{"internalType":"uint64","name":"startingDebtIndex","type":"uint64"},{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint256","name":"feesToDistribute","type":"uint256"},{"internalType":"uint256","name":"feesClaimed","type":"uint256"},{"internalType":"uint256","name":"rewardsToDistribute","type":"uint256"},{"internalType":"uint256","name":"rewardsClaimed","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recordFeePaid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"resolver","outputs":[{"internalType":"contract AddressResolver","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"resolverAddressesRequired","outputs":[{"internalType":"bytes32[]","name":"addresses","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_integrationProxy","type":"address"}],"name":"setIntegrationProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"setMessageSender","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_proxy","type":"address"}],"name":"setProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_val","type":"uint256"}],"name":"setQuotaTolerance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setRewardsToDistribute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"setupExpiryTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"targetThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalFeesAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalRewardsAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]

6080604052631c1554d160e21b6008553480156200001c57600080fd5b506040516200537c3803806200537c8339810160408190526200003f9162000221565b8080621baf8085856001600160a01b038116620000795760405162461bcd60e51b8152600401620000709062000343565b60405180910390fd5b600080546001600160a01b0319166001600160a01b0383161781556040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91620000c69184906200030b565b60405180910390a1506000546001600160a01b0316620000fa5760405162461bcd60e51b8152600401620000709062000331565b600280546001600160a01b0319166001600160a01b0383161790556040517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e9062000147908390620002fb565b60405180910390a1504201600555600680546001600160a01b0319166001600160a01b0392909216919091179055506001620001846000620001e2565b80546001600160401b0319166001600160401b039290921691909117905542620001af6000620001e2565b80546001600160401b0392909216600160801b02600160801b600160c01b0319909216919091179055506200039e915050565b60006009600260ff16836013540181620001f857fe5b06600281106200020457fe5b6005020192915050565b80516200021b8162000384565b92915050565b6000806000606084860312156200023757600080fd5b60006200024586866200020e565b935050602062000258868287016200020e565b92505060406200026b868287016200020e565b9150509250925092565b620002808162000370565b82525050565b62000280816200035e565b6000620002a060118362000355565b7013dddb995c881b5d5cdd081899481cd95d607a1b815260200192915050565b6000620002cf60198362000355565b7f4f776e657220616464726573732063616e6e6f74206265203000000000000000815260200192915050565b602081016200021b828462000275565b604081016200031b828562000275565b6200032a602083018462000286565b9392505050565b602080825281016200021b8162000291565b602080825281016200021b81620002c0565b90815260200190565b60006001600160a01b0382166200021b565b60006200021b8260006200021b826200035e565b6200038f816200035e565b81146200039b57600080fd5b50565b614fce80620003ae6000396000f3fe608060405234801561001057600080fd5b50600436106102f45760003560e01c80636683051b11610191578063ac834193116100e3578063d294f09311610097578063eb1edd6111610071578063eb1edd6114610578578063ec55688914610580578063fd1f498d14610588576102f4565b8063d294f09314610560578063d67bdd2514610568578063e0e6393d14610570576102f4565b8063b410a034116100c8578063b410a03414610530578063bc67f83214610538578063cff2ddad1461054b576102f4565b8063ac83419314610515578063b10090b81461051d576102f4565b806386645274116101455780638da5cb5b1161011f5780638da5cb5b146104f257806397107d6d146104fa5780639cbdaeb61461050d576102f4565b806386645274146104b75780638844578a146104ca578063899ffef4146104dd576102f4565b80636de813f1116101765780636de813f11461049f57806374185360146104a757806379ba5097146104af576102f4565b80636683051b146104845780636841e9b41461048c576102f4565b80633c41d1731161024a57806353a47bb7116101fe5780635fe47778116101d85780635fe477781461046157806362115496146104695780636466f45e14610471576102f4565b806353a47bb714610431578063569249d01461044657806359a2f19f1461044e576102f4565b80633fcd22401161022f5780633fcd2240146103fb578063446530d11461042157806346ba2d9014610429576102f4565b80633c41d173146103eb5780633ebc457a146103f3576102f4565b80631627540c116102ac578063269652671161028657806326965267146103ae5780632af64bd3146103b657806333140016146103cb576102f4565b80631627540c1461038057806322425fa41461039357806322bf55ef1461039b576102f4565b80630813071c116102dd5780630813071c146103375780630de586151461034a578063131b0ae71461036b576102f4565b806304f3bcec146102f957806307ea50cd14610317575b600080fd5b61030161059b565b60405161030e9190614c06565b60405180910390f35b61032a610325366004613b9d565b6105aa565b60405161030e9190614b23565b61032a610345366004613be1565b61067f565b61035d610358366004613b9d565b6107cb565b60405161030e929190614b3f565b61037e610379366004613b9d565b61084d565b005b61037e61038e366004613b9d565b610877565b61032a6108d5565b61037e6103a9366004613cc2565b6108e5565b61032a610a95565b6103be610a9f565b60405161030e9190614b15565b6103de6103d9366004613b9d565b610bcf565b60405161030e9190614af6565b6103be610e33565b61037e610e41565b61040e610409366004613cc2565b6111cf565b60405161030e9796959493929190614e13565b61032a611278565b61032a61127e565b610439611284565b60405161030e91906149ee565b61032a611293565b6103be61045c366004613b9d565b6112ee565b61037e611300565b61032a61152a565b6103be61047f366004613b9d565b611530565b6103be61165a565b61037e61049a366004613cc2565b611663565b61032a611906565b61037e61195b565b61037e611aad565b61037e6104c5366004613c1b565b611b49565b61037e6104d8366004613cc2565b611c89565b6104e5611d30565b60405161030e9190614b04565b610439612050565b61037e610508366004613b9d565b61205f565b6103016120b2565b61032a6120c1565b61037e61052b366004613d40565b612161565b61032a61235b565b61037e610546366004613b9d565b612365565b61055361238f565b60405161030e9190614e7b565b6103be612394565b61043961240b565b61032a61241a565b610439612424565b61030161243c565b61037e610596366004613cc2565b61244b565b6006546001600160a01b031681565b60006105b46124c8565b6001600160a01b031663bdc963d87f6c6173745f6665655f7769746864726177616c00000000000000000000000000846040516020016105f592919061497c565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016106279190614b23565b60206040518083038186803b15801561063f57600080fd5b505afa158015610653573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106779190810190613c86565b90505b919050565b6000816106a75760405162461bcd60e51b815260040161069e90614dd5565b60405180910390fd5b600282106106c75760405162461bcd60e51b815260040161069e90614d65565b6106d3600183036124f3565b5468010000000000000000900467ffffffffffffffff166106f6575060006107c5565b60006107266001610709600186036124f3565b5468010000000000000000900467ffffffffffffffff169061251d565b9050600080610733612545565b6001600160a01b031663d29c000a87856040518363ffffffff1660e01b8152600401610760929190614a7e565b604080518083038186803b15801561077757600080fd5b505afa15801561078b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107af9190810190613d10565b90925090506107bf838383612570565b93505050505b92915050565b6000806107d6613ab5565b6107df84610bcf565b905060008060015b60028110156108405761080c8482600281106107ff57fe5b60200201515184906126af565b925061083684826002811061081d57fe5b602002015160016020020151839063ffffffff6126af16565b91506001016107e7565b509093509150505b915091565b6108556126d4565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b61087f6126d4565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906108ca9083906149ee565b60405180910390a150565b60006108df612700565b90505b90565b60006108ef6127b8565b6001600160a01b0316331490506000806109076127e3565b6001600160a01b031663c9a2f3ad336040518263ffffffff1660e01b815260040161093291906149fc565b60206040518083038186803b15801561094a57600080fd5b505afa15801561095e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109829190810190613c86565b1415905060006109906127f7565b6001600160a01b0316336001600160a01b031614905060006109b0612822565b6001600160a01b031663b38988f7336040518263ffffffff1660e01b81526004016109db91906149fc565b60206040518083038186803b1580156109f357600080fd5b505afa158015610a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a2b9190810190613c68565b90508380610a365750825b80610a3e5750815b80610a465750805b610a625760405162461bcd60e51b815260040161069e90614c35565b610a8085610a7060006124f3565b600101549063ffffffff6126af16565b610a8a60006124f3565b600101555050505050565b60006108df61284d565b60006060610aab611d30565b905060005b8151811015610bc6576000828281518110610ac757fe5b602090810291909101810151600081815260079092526040918290205460065492517f21f8a7210000000000000000000000000000000000000000000000000000000081529193506001600160a01b039081169216906321f8a72190610b31908590600401614b23565b60206040518083038186803b158015610b4957600080fd5b505afa158015610b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b819190810190613bc3565b6001600160a01b0316141580610bac57506000818152600760205260409020546001600160a01b0316155b15610bbd57600093505050506108e2565b50600101610ab0565b50600191505090565b610bd7613ab5565b6000806000610be4612545565b6040517fb326f84e0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0382169063b326f84e90610c2f908890600090600401614a25565b604080518083038186803b158015610c4657600080fd5b505afa158015610c5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c7e9190810190613d10565b909350915081158015610c8f575082155b15610ca757610c9c613ab5565b935061067a92505050565b600080610cb66000868661296c565b8751829052875160200181905290925090506000610cd3886105aa565b905060015b8015610e275760001981016000610cee826124f3565b5468010000000000000000900467ffffffffffffffff1690508015801590610d285750610d1a836124f3565b5467ffffffffffffffff1684105b15610e1c576000610d4082600163ffffffff61251d16565b6040517fd29c000a0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0389169063d29c000a90610d8a908f908590600401614a7e565b604080518083038186803b158015610da157600080fd5b505afa158015610db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610dd99190810190613d10565b909a509850610de9848b8b61296c565b9097509550868b8560028110610dfb57fe5b602002015152858b8560028110610e0e57fe5b602002015160016020020152505b505060001901610cd8565b50505050505050919050565b601454610100900460ff1690565b610e49612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b158015610e8157600080fd5b505afa158015610e95573d6000803e3d6000fd5b505050506000610ea3612700565b11610ec05760405162461bcd60e51b815260040161069e90614de5565b610ec8612700565b4203610ed460006124f3565b54600160801b900467ffffffffffffffff161115610f045760405162461bcd60e51b815260040161069e90614c65565b60145460ff1680610f1c5750601454610100900460ff165b610f385760405162461bcd60e51b815260040161069e90614da5565b6000610f43816124f3565b90506000610f5160016124f3565b9050610f828260010154610f768360020154846001015461251d90919063ffffffff16565b9063ffffffff6126af16565b610f8c60006124f3565b60010155600380830154600483015491830154610fb392610f76919063ffffffff61251d16565b610fbd60006124f3565b60030155601354610ffa90600290610fee90600190610fe2908463ffffffff6126af16565b9063ffffffff61251d16565b9063ffffffff612a3916565b60138190556009906002811061100c57fe5b6005020180547fffffffffffffffff00000000000000000000000000000000000000000000000016815560006001808301829055600283018290556003830182905560049092015561107890611061816124f3565b5467ffffffffffffffff169063ffffffff6126af16565b61108260006124f3565b805467ffffffffffffffff191667ffffffffffffffff929092169190911790556110aa612a69565b6001600160a01b031663cd92eba96040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e257600080fd5b505afa1580156110f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061111a9190810190613c86565b61112460006124f3565b805467ffffffffffffffff9290921668010000000000000000026fffffffffffffffff0000000000000000199092169190911790554261116460006124f3565b805467ffffffffffffffff92909216600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff9092169190911790556014805461ffff191690556111cb6111bb60016124f3565b5467ffffffffffffffff16612a94565b5050565b60008060008060008060006111e2613ae2565b6111eb896124f3565b6040805160e081018252825467ffffffffffffffff808216808452680100000000000000008304821660208501819052600160801b909304909116938301849052600185015460608401819052600286015460808501819052600387015460a0860181905260049097015460c0909501859052919f929e50939c50929a5091985091965090945092505050565b60165481565b60055481565b6001546001600160a01b031681565b60008060015b60028110156112e8576112bf6112ae826124f3565b60010154839063ffffffff6126af16565b91506112de6112cd826124f3565b60020154839063ffffffff61251d16565b9150600101611299565b50905090565b60006112f982612b4c565b5092915050565b611308612d3b565b6000611312612d91565b6004546001600160a01b039081169116149050806113425760405162461bcd60e51b815260040161069e90614d85565b600061134c612700565b116113695760405162461bcd60e51b815260040161069e90614de5565b611371612700565b420361137d60006124f3565b54600160801b900467ffffffffffffffff1611156113ad5760405162461bcd60e51b815260040161069e90614c55565b60145460ff16156113d05760405162461bcd60e51b815260040161069e90614db5565b6014805460ff1916600117905560006113e761284d565b9050600081116113fc57506000601555611527565b6114046127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b81526004016114319190614b23565b60206040518083038186803b15801561144957600080fd5b505afa15801561145d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114819190810190613ca4565b6001600160a01b0316639dc29fac73feefeefeefeefeefeefeefeefeefeefeefeefeef836040518363ffffffff1660e01b81526004016114c2929190614a7e565b600060405180830381600087803b1580156114dc57600080fd5b505af11580156114f0573d6000803e3d6000fd5b5050506015829055506115178161150760006124f3565b600101549063ffffffff61251d16565b61152160006124f3565b60010155505b50565b60155481565b600061153a612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b15801561157257600080fd5b505afa158015611586573d6000803e3d6000fd5b50505050611592612d3b565b61159a612e0b565b600480546040517f21f4ae570000000000000000000000000000000000000000000000000000000081526001600160a01b03938416936321f4ae57936115e593889392169101614a0a565b60206040518083038186803b1580156115fd57600080fd5b505afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116359190810190613c68565b6116515760405162461bcd60e51b815260040161069e90614cf5565b61067782612e36565b60145460ff1690565b61166b612d3b565b6000611675612d91565b6004546001600160a01b039081169116149050806116a55760405162461bcd60e51b815260040161069e90614d85565b60006116af612700565b116116cc5760405162461bcd60e51b815260040161069e90614de5565b6116d4612700565b42036116e060006124f3565b54600160801b900467ffffffffffffffff1611156117105760405162461bcd60e51b815260040161069e90614c95565b601454610100900460ff16156117385760405162461bcd60e51b815260040161069e90614c85565b6014805461ff00191661010017905581611751576111cb565b60006117eb6117e6611761612f2b565b6001600160a01b03166302760d946040518163ffffffff1660e01b815260040160206040518083038186803b15801561179957600080fd5b505afa1580156117ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117d19190810190613c86565b6117da86612f56565b9063ffffffff612f6c16565b612f85565b90506117f56127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b81526004016118229190614b23565b60206040518083038186803b15801561183a57600080fd5b505afa15801561184e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118729190810190613ca4565b6001600160a01b031663867904b473feefeefeefeefeefeefeefeefeefeefeefeefeef836040518363ffffffff1660e01b81526004016118b3929190614a7e565b600060405180830381600087803b1580156118cd57600080fd5b505af11580156118e1573d6000803e3d6000fd5b505050506118f381610a7060006124f3565b6118fd60006124f3565b60010155505050565b60008060015b60028110156112e857611932611921826124f3565b60030154839063ffffffff6126af16565b9150611951611940826124f3565b60040154839063ffffffff61251d16565b915060010161190c565b6060611965611d30565b905060005b81518110156111cb57600082828151811061198157fe5b602002602001015190506000600660009054906101000a90046001600160a01b03166001600160a01b031663dacb2d0183846040516020016119c391906149d8565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016119ef929190614b4d565b60206040518083038186803b158015611a0757600080fd5b505afa158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190613bc3565b6000838152600760205260409081902080546001600160a01b0319166001600160a01b038416179055519091507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa6890611a9b9084908490614b31565b60405180910390a1505060010161196a565b6001546001600160a01b03163314611ad75760405162461bcd60e51b815260040161069e90614c45565b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92611b1a926001600160a01b0391821692911690614a0a565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000611b536127e3565b6001600160a01b0316336001600160a01b03161490506000611b73612a69565b6001600160a01b0316336001600160a01b03161490508180611b925750805b611bae5760405162461bcd60e51b815260040161069e90614c25565b611bb6612545565b6001600160a01b03166394e1a448868686611bd160006124f3565b5460405160e086901b7fffffffff00000000000000000000000000000000000000000000000000000000168152611c249493929168010000000000000000900467ffffffffffffffff1690600401614ac1565b600060405180830381600087803b158015611c3e57600080fd5b505af1158015611c52573d6000803e3d6000fd5b50505050611c82858585611c6660006124f3565b5468010000000000000000900467ffffffffffffffff16612fa7565b5050505050565b611c916126d4565b73c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611cd557600080fd5b505af4158015611ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0d9190810190613c86565b8110611d2b5760405162461bcd60e51b815260040161069e90614df5565b601655565b606080611d3b613076565b60408051600d8082526101c0820190925291925060609190602082016101a0803883390190505090507f53797374656d537461747573000000000000000000000000000000000000000081600081518110611d9257fe5b6020026020010181815250507f5065726946696e616e636500000000000000000000000000000000000000000081600181518110611dcc57fe5b6020026020010181815250507f466565506f6f6c5374617465000000000000000000000000000000000000000081600281518110611e0657fe5b6020026020010181815250507f466565506f6f6c457465726e616c53746f72616765000000000000000000000081600381518110611e4057fe5b6020026020010181815250507f45786368616e676572000000000000000000000000000000000000000000000081600481518110611e7a57fe5b6020026020010181815250506524b9b9bab2b960d11b81600581518110611e9d57fe5b6020026020010181815250507f5065726946696e616e636553746174650000000000000000000000000000000081600681518110611ed757fe5b6020026020010181815250507f526577617264457363726f77563200000000000000000000000000000000000081600781518110611f1157fe5b6020026020010181815250507f44656c6567617465417070726f76616c7300000000000000000000000000000081600881518110611f4b57fe5b6020026020010181815250507f4574686572436f6c6c61746572616c705553440000000000000000000000000081600981518110611f8557fe5b6020026020010181815250507f52657761726473446973747269627574696f6e0000000000000000000000000081600a81518110611fbf57fe5b6020026020010181815250507f436f6c6c61746572616c4d616e6167657200000000000000000000000000000081600b81518110611ff957fe5b6020026020010181815250507f43726f7373436861696e4d616e6167657200000000000000000000000000000081600c8151811061203357fe5b60200260200101818152505061204982826130d5565b9250505090565b6000546001600160a01b031681565b6120676126d4565b600280546001600160a01b0319166001600160a01b0383161790556040517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e906108ca9083906149fc565b6003546001600160a01b031681565b60006108df61214d6120d161318a565b73c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f769190810190613c86565b6121556131f2565b9063ffffffff61325a16565b612169613284565b600554421061218a5760405162461bcd60e51b815260040161069e90614d75565b612192612a69565b6001600160a01b031663cd92eba96040518163ffffffff1660e01b815260040160206040518083038186803b1580156121ca57600080fd5b505afa1580156121de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122029190810190613c86565b8611156122215760405162461bcd60e51b815260040161069e90614d55565b6040518060e001604052808867ffffffffffffffff1681526020018767ffffffffffffffff1681526020018667ffffffffffffffff168152602001858152602001848152602001838152602001828152506009612291600260ff16610fee8c6013546126af90919063ffffffff16565b6002811061229b57fe5b82516005919091029190910180546020840151604085015167ffffffffffffffff1990921667ffffffffffffffff948516176fffffffffffffffff000000000000000019166801000000000000000091851691909102177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff16600160801b9390911692909202919091178155606082015160018201556080820151600282015560a0820151600382015560c0909101516004909101555050505050505050565b60006108df6131f2565b61236d613309565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b600281565b600061239e612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b1580156123d657600080fd5b505afa1580156123ea573d6000803e3d6000fd5b505050506123f6612d3b565b6004546108df906001600160a01b0316612e36565b6004546001600160a01b031681565b60006108df61318a565b73feefeefeefeefeefeefeefeefeefeefeefeefeef81565b6002546001600160a01b031681565b6000612455613348565b6004549091506001600160a01b038083169116148061247c5750336001600160a01b038216145b6124985760405162461bcd60e51b815260040161069e90614d05565b6124b6826124a660006124f3565b600301549063ffffffff6126af16565b6124c060006124f3565b600301555050565b60006108df7f466565506f6f6c457465726e616c53746f72616765000000000000000000000061336f565b60006009600260ff1683601354018161250857fe5b066002811061251357fe5b6005020192915050565b60008282111561253f5760405162461bcd60e51b815260040161069e90614cb5565b50900390565b60006108df7f466565506f6f6c5374617465000000000000000000000000000000000000000061336f565b60008061257b612a69565b905060006126a3856117da846001600160a01b03166308d95cd5886040518263ffffffff1660e01b81526004016125b29190614b23565b60206040518083038186803b1580156125ca57600080fd5b505afa1580156125de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126029190810190613c86565b6040517f08d95cd50000000000000000000000000000000000000000000000000000000081526001600160a01b038716906308d95cd590612647908d90600401614b23565b60206040518083038186803b15801561265f57600080fd5b505afa158015612673573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126979190810190613c86565b9063ffffffff6133cc16565b925050505b9392505050565b6000828201838110156126a85760405162461bcd60e51b815260040161069e90614ca5565b6000546001600160a01b031633146126fe5760405162461bcd60e51b815260040161069e90614d15565b565b600061270a6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f666565506572696f644475726174696f6e0000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b60206040518083038186803b15801561278057600080fd5b505afa158015612794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108df9190810190613c86565b60006108df7f45786368616e676572000000000000000000000000000000000000000000000061336f565b60006108df6524b9b9bab2b960d11b61336f565b60006108df7f4574686572436f6c6c61746572616c705553440000000000000000000000000061336f565b60006108df7f436f6c6c61746572616c4d616e6167657200000000000000000000000000000061336f565b60008061294761285b612f2b565b6001600160a01b03166302760d946040518163ffffffff1660e01b815260040160206040518083038186803b15801561289357600080fd5b505afa1580156128a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128cb9190810190613c86565b73c59ba4786ca553a4591100e97673c1cff98e5b3763d5e5e6e66040518163ffffffff1660e01b815260040160206040518083038186803b15801561290f57600080fd5b505af4158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fe29190810190613c86565b90506129666117e6826117da61295d60006124f3565b60010154612f56565b91505090565b6000808361297f57506000905080612a06565b8385156129aa576000612999600161070960018a036124f3565b90506129a6818787612570565b9150505b60006129c9826129b9896124f3565b600101549063ffffffff61325a16565b905060006129ea836129da8a6124f3565b600301549063ffffffff61325a16565b90506129f582612f85565b6129fe82612f85565b945094505050505b935093915050565b60006108df7f53797374656d537461747573000000000000000000000000000000000000000061336f565b600081612a585760405162461bcd60e51b815260040161069e90614ce5565b818381612a6157fe5b069392505050565b60006108df7f5065726946696e616e636553746174650000000000000000000000000000000061336f565b6002546040516001600160a01b039091169063907dff9790612aba908490602001614b23565b6040516020818303038152906040526001604051612ad7906149e3565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b168252612b1e93929160009081908190600401614b6d565b600060405180830381600087803b158015612b3857600080fd5b505af1158015611c82573d6000803e3d6000fd5b6000806000612b596127e3565b6001600160a01b0316639288e7048560008060016040518563ffffffff1660e01b8152600401612b8c9493929190614a40565b60206040518083038186803b158015612ba457600080fd5b505afa158015612bb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bdc9190810190613c86565b9050612c33612c2b60165473c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561211557600080fd5b612155613410565b811115612c47576000809250925050610848565b600080612c526127e3565b6001600160a01b031663ae3bbbbb876040518263ffffffff1660e01b8152600401612c7d91906149ee565b604080518083038186803b158015612c9457600080fd5b505afa158015612ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ccc9190810190613ce0565b915091506000612cda6131f2565b905080831015612cf35750600194509250610848915050565b6000612d10612d036120d161318a565b839063ffffffff61325a16565b905080841115612d2b57600083965096505050505050610848565b5060019550909350505050915091565b6002546001600160a01b03163314801590612d6157506003546001600160a01b03163314155b8015612d7857506004546001600160a01b03163314155b156126fe57600480546001600160a01b03191633179055565b6000612d9b612f2b565b6001600160a01b03166360b3a0336040518163ffffffff1660e01b815260040160206040518083038186803b158015612dd357600080fd5b505afa158015612de7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108df9190810190613bc3565b60006108df7f44656c6567617465417070726f76616c7300000000000000000000000000000061336f565b6000808080808080612e4788612b4c565b9150915081612e685760405162461bcd60e51b815260040161069e90614d25565b8015612e865760405162461bcd60e51b815260040161069e90614d35565b612e8f886107cb565b909450925083151580612ea25750600083115b612ebe5760405162461bcd60e51b815260040161069e90614cd5565b612edc88612ecc60016124f3565b5467ffffffffffffffff16613478565b8315612ef757612eeb8461352b565b9450612ef788866135f7565b8215612f1257612f0683613793565b9550612f12888761386d565b612f1d8886886138e7565b506001979650505050505050565b60006108df7f43726f7373436861696e4d616e6167657200000000000000000000000000000061336f565b600061067782633b9aca0063ffffffff6139ac16565b60006126a883836b033b2e3c9fd0803ce80000006139e6565b60006305f5e10082046005600a820610612f9d57600a015b600a900492915050565b6002546040516001600160a01b039091169063907dff9790612fd190869086908690602001614e05565b6040516020818303038152906040526002604051612fee906149ad565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16825261303e9392916001600160a01b038b16906000908190600401614bcc565b600060405180830381600087803b15801561305857600080fd5b505af115801561306c573d6000803e3d6000fd5b5050505050505050565b604080516001808252818301909252606091602080830190803883390190505090507f466c657869626c6553746f726167650000000000000000000000000000000000816000815181106130c657fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015613105578160200160208202803883390190505b50905060005b83518110156131475783818151811061312057fe5b602002602001015182828151811061313457fe5b602090810291909101015260010161310b565b5060005b82518110156112f95782818151811061316057fe5b602002602001015182828651018151811061317757fe5b602090810291909101015260010161314b565b60006131946133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f7461726765745468726573686f6c6400000000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b60006131fc6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f69737375616e6365526174696f000000000000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b6000670de0b6b3a7640000613275848463ffffffff6139ac16565b8161327c57fe5b049392505050565b6002546001600160a01b031633148015906132aa57506003546001600160a01b03163314155b80156132c157506004546001600160a01b03163314155b156132d957600480546001600160a01b031916331790555b6000546004546001600160a01b039081169116146126fe5760405162461bcd60e51b815260040161069e90614c75565b6002546001600160a01b031633148061332c57506003546001600160a01b031633145b6126fe5760405162461bcd60e51b815260040161069e90614dc5565b60006108df7f52657761726473446973747269627574696f6e000000000000000000000000005b60008181526007602090815260408083205490516001600160a01b03909116918215159161339f918691016149b8565b604051602081830303815290604052906112f95760405162461bcd60e51b815260040161069e9190614c14565b60006126a883836b033b2e3c9fd0803ce8000000613a23565b60006108df7f466c657869626c6553746f72616765000000000000000000000000000000000061336f565b600061341a6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f65787465726e616c546f6b656e51756f746100000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b6134806124c8565b6001600160a01b0316633562fd207f6c6173745f6665655f7769746864726177616c00000000000000000000000000846040516020016134c192919061497c565b60405160208183030381529060405280519060200120836040518363ffffffff1660e01b81526004016134f5929190614b3f565b600060405180830381600087803b15801561350f57600080fd5b505af1158015613523573d6000803e3d6000fd5b505050505050565b6000818160015b60028110156135ef576000613546826124f3565b600201549050600061355b82611507856124f3565b905080156135e45760008582106135725785613574565b815b9050613586838263ffffffff6126af16565b61358f856124f3565b600201556135a3868263ffffffff61251d16565b95506135b5858263ffffffff6126af16565b9450856135ca5784965050505050505061067a565b831580156135d85750600086115b156135e257600095505b505b505060001901613532565b509392505050565b816001600160a01b03811673feefeefeefeefeefeefeefeefeefeefeefeefeef14156136355760405162461bcd60e51b815260040161069e90614d95565b600061363f6127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b815260040161366c9190614b23565b60206040518083038186803b15801561368457600080fd5b505afa158015613698573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136bc9190810190613ca4565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081529091506001600160a01b03821690639dc29fac9061371a9073feefeefeefeefeefeefeefeefeefeefeefeefeef908790600401614a7e565b600060405180830381600087803b15801561373457600080fd5b505af1158015613748573d6000803e3d6000fd5b50506040517f867904b40000000000000000000000000000000000000000000000000000000081526001600160a01b038416925063867904b4915061303e9087908790600401614a7e565b6000818160015b60028110156135ef5760006137ce6137b1836124f3565b600401546137be846124f3565b600301549063ffffffff61251d16565b905080156138635760008482106137e557846137e7565b815b9050613806816137f6856124f3565b600401549063ffffffff6126af16565b61380f846124f3565b60040155613823858263ffffffff61251d16565b9450613835848263ffffffff6126af16565b93508461384957839550505050505061067a565b821580156138575750600085115b1561386157600094505b505b506000190161379a565b816001600160a01b03811673feefeefeefeefeefeefeefeefeefeefeefeefeef14156138ab5760405162461bcd60e51b815260040161069e90614d95565b6301dfe2006138b8613a55565b6001600160a01b0316631bb47b448585846040518463ffffffff1660e01b815260040161303e93929190614a99565b6002546040516001600160a01b039091169063907dff979061391190869086908690602001614a99565b604051602081830303815290604052600160405161392e906149a2565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16825261397593929160009081908190600401614b6d565b600060405180830381600087803b15801561398f57600080fd5b505af11580156139a3573d6000803e3d6000fd5b50505050505050565b6000826139bb575060006107c5565b828202828482816139c857fe5b04146126a85760405162461bcd60e51b815260040161069e90614d45565b600080600a83046139fd868663ffffffff6139ac16565b81613a0457fe5b0490506005600a825b0610613a1757600a015b600a9004949350505050565b600080613a4984613a3d87600a870263ffffffff6139ac16565b9063ffffffff613a8016565b90506005600a82613a0d565b60006108df7f526577617264457363726f77563200000000000000000000000000000000000061336f565b6000808211613aa15760405162461bcd60e51b815260040161069e90614cc5565b6000828481613aac57fe5b04949350505050565b60405180604001604052806002905b613acc613b3d565b815260200190600190039081613ac45790505090565b6040518060e00160405280600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600081526020016000815260200160008152602001600081525090565b60405180604001604052806002906020820280388339509192915050565b80356107c581614f5c565b80516107c581614f5c565b80516107c581614f70565b80516107c581614f79565b80516107c581614f82565b80356107c581614f79565b600060208284031215613baf57600080fd5b6000613bbb8484613b5b565b949350505050565b600060208284031215613bd557600080fd5b6000613bbb8484613b66565b60008060408385031215613bf457600080fd5b6000613c008585613b5b565b9250506020613c1185828601613b92565b9150509250929050565b600080600060608486031215613c3057600080fd5b6000613c3c8686613b5b565b9350506020613c4d86828701613b92565b9250506040613c5e86828701613b92565b9150509250925092565b600060208284031215613c7a57600080fd5b6000613bbb8484613b71565b600060208284031215613c9857600080fd5b6000613bbb8484613b7c565b600060208284031215613cb657600080fd5b6000613bbb8484613b87565b600060208284031215613cd457600080fd5b6000613bbb8484613b92565b60008060408385031215613cf357600080fd5b6000613cff8585613b7c565b9250506020613c1185828601613b71565b60008060408385031215613d2357600080fd5b6000613d2f8585613b7c565b9250506020613c1185828601613b7c565b600080600080600080600080610100898b031215613d5d57600080fd5b6000613d698b8b613b92565b9850506020613d7a8b828c01613b92565b9750506040613d8b8b828c01613b92565b9650506060613d9c8b828c01613b92565b9550506080613dad8b828c01613b92565b94505060a0613dbe8b828c01613b92565b93505060c0613dcf8b828c01613b92565b92505060e0613de08b828c01613b92565b9150509295985092959890939650565b6000613dfc8383613ee6565b505060400190565b6000613e108383613f3b565b505060200190565b613e2181614edc565b82525050565b613e2181614ea2565b613e21613e3c82614ea2565b614f3b565b613e4a81614e8f565b613e54818461067a565b9250613e5f826108e2565b8060005b83811015613523578151613e778782613df0565b9650613e8283614e89565b925050600101613e63565b6000613e9882614e95565b613ea28185614e99565b9350613ead83614e89565b8060005b83811015613edb578151613ec58882613e04565b9750613ed083614e89565b925050600101613eb1565b509495945050505050565b613eef81614e8f565b613ef9818461067a565b9250613f04826108e2565b8060005b83811015613523578151613f1c8782613e04565b9650613f2783614e89565b925050600101613f08565b613e2181614ead565b613e21816108e2565b613e21613f50826108e2565b6108e2565b6000613f6082614e95565b613f6a8185614e99565b9350613f7a818560208601614f0b565b613f8381614f4c565b9093019392505050565b613e2181614eb2565b613e2181614ee7565b613e2181614ef5565b6000613fb5602083614e99565b7f49737375657220616e64205065726946696e616e63655374617465206f6e6c79815260200192915050565b6000613fee601783614e99565b7f4f6e6c7920496e7465726e616c20436f6e747261637473000000000000000000815260200192915050565b6000614027603583614e99565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527f2063616e20616363657074206f776e6572736869700000000000000000000000602082015260400192915050565b6000614086602983614e99565b7f646973747269627574696e672066656520726577617264206e6f74207965742081527f617661696c61626c650000000000000000000000000000000000000000000000602082015260400192915050565b60006140e5601d83614e99565b7f546f6f206561726c7920746f20636c6f73652066656520706572696f64000000815260200192915050565b600061411e601383614e99565b7f4f776e6572206f6e6c792066756e6374696f6e00000000000000000000000000815260200192915050565b600061415760248361067a565b7f46656573436c61696d656428616464726573732c75696e743235362c75696e7481527f3235362900000000000000000000000000000000000000000000000000000000602082015260240192915050565b60006141b6603883614e99565b7f416c6c6f636174696e6720666565207265776172647320697320706f7373696281527f6c65206f6e6c79206f6e636520696e206120706572696f640000000000000000602082015260400192915050565b6000614215602783614e99565b7f616c6c6f636174696e672066656520726577617264206e6f742079657420617681527f61696c61626c6500000000000000000000000000000000000000000000000000602082015260400192915050565b600061427460378361067a565b7f49737375616e636544656274526174696f456e74727928616464726573732c7581527f696e743235362c75696e743235362c75696e7432353629000000000000000000602082015260370192915050565b60006142d3601b83614e99565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b600061430c601e83614e99565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b6000614345601a83614e99565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b600061437e604083614e99565b7f4e6f2066656573206f72207265776172647320617661696c61626c6520666f7281527f20706572696f642c206f72206665657320616c726561647920636c61696d6564602082015260400192915050565b60006143dd60118361067a565b7f4d697373696e6720616464726573733a20000000000000000000000000000000815260110192915050565b6000614416601883614e99565b7f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815260200192915050565b600061444f601f83614e99565b7f4e6f7420617070726f76656420746f20636c61696d206f6e20626568616c6600815260200192915050565b6000614488601e83614e99565b7f43616c6c6572206973206e6f742072657761726473417574686f726974790000815260200192915050565b60006144c1602f83614e99565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681527f6f726d207468697320616374696f6e0000000000000000000000000000000000602082015260400192915050565b6000614520601f83614e99565b7f432d526174696f2062656c6f772070656e616c7479207468726573686f6c6400815260200192915050565b6000614559601f83614e99565b7f412070796e7468206f722050455249207261746520697320696e76616c696400815260200192915050565b6000614592602183614e99565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006145f1601683614e99565b7f43616e6e6f7420696d706f727420626164206461746100000000000000000000815260200192915050565b600061462a601d83614e99565b7f4578636565647320746865204645455f504552494f445f4c454e475448000000815260200192915050565b6000614663602983614e99565b7f43616e206f6e6c7920706572666f726d207468697320616374696f6e2064757281527f696e672073657475700000000000000000000000000000000000000000000000602082015260400192915050565b60006146c260198361067a565b7f5265736f6c766572206d697373696e67207461726765743a2000000000000000815260190192915050565b60006146fb601183614e99565b7f64656274206d616e61676572206f6e6c79000000000000000000000000000000815260200192915050565b6000614734601783614e99565b7f4665652061646472657373206e6f7420616c6c6f776564000000000000000000815260200192915050565b600061476d604483614e99565b7f66656520726577617264732073686f756c64206265206469737472696275746581527f64206f7220616c6c6f6361746564206265666f726520636c6f73696e6720706560208201527f72696f6400000000000000000000000000000000000000000000000000000000604082015260600192915050565b60006147f2603a83614e99565b7f446973747269627574696e6720666565207265776172647320697320706f737381527f69626c65206f6e6c79206f6e636520696e206120706572696f64000000000000602082015260400192915050565b600061485160188361067a565b7f466565506572696f64436c6f7365642875696e74323536290000000000000000815260180192915050565b600061488a601783614e99565b7f4f6e6c79207468652070726f78792063616e2063616c6c000000000000000000815260200192915050565b60006148c3602083614e99565b7f43757272656e7420706572696f64206973206e6f7420636c6f73656420796574815260200192915050565b60006148fc601b83614e99565b7f46656520506572696f64204475726174696f6e206e6f74207365740000000000815260200192915050565b6000614935602083614e99565b7f546f6c6572616e63652076616c75652063616e6e6f7420657863656564732031815260200192915050565b613e2181614f00565b613e2181614ec9565b613e2181614ed6565b60006149888285613f44565b6020820191506149988284613e30565b5060140192915050565b60006107c58261414a565b60006107c582614267565b60006149c3826143d0565b91506149cf8284613f44565b50602001919050565b60006149c3826146b5565b60006107c582614844565b602081016107c58284613e27565b602081016107c58284613e18565b60408101614a188285613e27565b6126a86020830184613e27565b60408101614a338285613e27565b6126a86020830184613f9f565b60808101614a4e8287613e27565b614a5b6020830186613f9f565b614a686040830185613f9f565b614a756060830184613f32565b95945050505050565b60408101614a8c8285613e27565b6126a86020830184613f3b565b60608101614aa78286613e27565b614ab46020830185613f3b565b613bbb6040830184613f3b565b60808101614acf8287613e27565b614adc6020830186613f3b565b614ae96040830185613f3b565b614a756060830184614961565b608081016107c58284613e41565b602080825281016126a88184613e8d565b602081016107c58284613f32565b602081016107c58284613f3b565b60408101614a188285613f3b565b60408101614a8c8285613f3b565b60408101614b5b8285613f3b565b8181036020830152613bbb8184613f55565b60c08082528101614b7e8189613f55565b9050614b8d6020830188613f9f565b614b9a6040830187613f3b565b614ba76060830186613f96565b614bb46080830185613f96565b614bc160a0830184613f96565b979650505050505050565b60c08082528101614bdd8189613f55565b9050614bec6020830188613f9f565b614bf96040830187613f3b565b614ba76060830186613f3b565b602081016107c58284613f8d565b602080825281016126a88184613f55565b6020808252810161067781613fa8565b6020808252810161067781613fe1565b602080825281016106778161401a565b6020808252810161067781614079565b60208082528101610677816140d8565b6020808252810161067781614111565b60208082528101610677816141a9565b6020808252810161067781614208565b60208082528101610677816142c6565b60208082528101610677816142ff565b6020808252810161067781614338565b6020808252810161067781614371565b6020808252810161067781614409565b6020808252810161067781614442565b602080825281016106778161447b565b60208082528101610677816144b4565b6020808252810161067781614513565b602080825281016106778161454c565b6020808252810161067781614585565b60208082528101610677816145e4565b602080825281016106778161461d565b6020808252810161067781614656565b60208082528101610677816146ee565b6020808252810161067781614727565b6020808252810161067781614760565b60208082528101610677816147e5565b602080825281016106778161487d565b60208082528101610677816148b6565b60208082528101610677816148ef565b6020808252810161067781614928565b60608101614aa78286613f3b565b60e08101614e21828a61496a565b614e2e602083018961496a565b614e3b604083018861496a565b614e486060830187613f3b565b614e556080830186613f3b565b614e6260a0830185613f3b565b614e6f60c0830184613f3b565b98975050505050505050565b602081016107c58284614973565b60200190565b50600290565b5190565b90815260200190565b600061067782614ebd565b151590565b600061067782614ea2565b6001600160a01b031690565b67ffffffffffffffff1690565b60ff1690565b600061067782614eb2565b6000610677613f50836108e2565b6000610677826108e2565b600061067782614ec9565b60005b83811015614f26578181015183820152602001614f0e565b83811115614f35576000848401525b50505050565b600061067782600061067782614f56565b601f01601f191690565b60601b90565b614f6581614ea2565b811461152757600080fd5b614f6581614ead565b614f65816108e2565b614f6581614eb256fea365627a7a72315820882079c2d3031f6a74125aae31f9cd51dec8225767dd29b17b1d172a05b79e396c6578706572696d656e74616cf564736f6c634300051000400000000000000000000000006e2eea6d56368e60f74a3f89f30b15b46bf3f7df000000000000000000000000166220468aff631290b7e1ddff7f45b052de832400000000000000000000000022f5d16097cf2396d6c893a77ea3b97234c0c114

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102f45760003560e01c80636683051b11610191578063ac834193116100e3578063d294f09311610097578063eb1edd6111610071578063eb1edd6114610578578063ec55688914610580578063fd1f498d14610588576102f4565b8063d294f09314610560578063d67bdd2514610568578063e0e6393d14610570576102f4565b8063b410a034116100c8578063b410a03414610530578063bc67f83214610538578063cff2ddad1461054b576102f4565b8063ac83419314610515578063b10090b81461051d576102f4565b806386645274116101455780638da5cb5b1161011f5780638da5cb5b146104f257806397107d6d146104fa5780639cbdaeb61461050d576102f4565b806386645274146104b75780638844578a146104ca578063899ffef4146104dd576102f4565b80636de813f1116101765780636de813f11461049f57806374185360146104a757806379ba5097146104af576102f4565b80636683051b146104845780636841e9b41461048c576102f4565b80633c41d1731161024a57806353a47bb7116101fe5780635fe47778116101d85780635fe477781461046157806362115496146104695780636466f45e14610471576102f4565b806353a47bb714610431578063569249d01461044657806359a2f19f1461044e576102f4565b80633fcd22401161022f5780633fcd2240146103fb578063446530d11461042157806346ba2d9014610429576102f4565b80633c41d173146103eb5780633ebc457a146103f3576102f4565b80631627540c116102ac578063269652671161028657806326965267146103ae5780632af64bd3146103b657806333140016146103cb576102f4565b80631627540c1461038057806322425fa41461039357806322bf55ef1461039b576102f4565b80630813071c116102dd5780630813071c146103375780630de586151461034a578063131b0ae71461036b576102f4565b806304f3bcec146102f957806307ea50cd14610317575b600080fd5b61030161059b565b60405161030e9190614c06565b60405180910390f35b61032a610325366004613b9d565b6105aa565b60405161030e9190614b23565b61032a610345366004613be1565b61067f565b61035d610358366004613b9d565b6107cb565b60405161030e929190614b3f565b61037e610379366004613b9d565b61084d565b005b61037e61038e366004613b9d565b610877565b61032a6108d5565b61037e6103a9366004613cc2565b6108e5565b61032a610a95565b6103be610a9f565b60405161030e9190614b15565b6103de6103d9366004613b9d565b610bcf565b60405161030e9190614af6565b6103be610e33565b61037e610e41565b61040e610409366004613cc2565b6111cf565b60405161030e9796959493929190614e13565b61032a611278565b61032a61127e565b610439611284565b60405161030e91906149ee565b61032a611293565b6103be61045c366004613b9d565b6112ee565b61037e611300565b61032a61152a565b6103be61047f366004613b9d565b611530565b6103be61165a565b61037e61049a366004613cc2565b611663565b61032a611906565b61037e61195b565b61037e611aad565b61037e6104c5366004613c1b565b611b49565b61037e6104d8366004613cc2565b611c89565b6104e5611d30565b60405161030e9190614b04565b610439612050565b61037e610508366004613b9d565b61205f565b6103016120b2565b61032a6120c1565b61037e61052b366004613d40565b612161565b61032a61235b565b61037e610546366004613b9d565b612365565b61055361238f565b60405161030e9190614e7b565b6103be612394565b61043961240b565b61032a61241a565b610439612424565b61030161243c565b61037e610596366004613cc2565b61244b565b6006546001600160a01b031681565b60006105b46124c8565b6001600160a01b031663bdc963d87f6c6173745f6665655f7769746864726177616c00000000000000000000000000846040516020016105f592919061497c565b604051602081830303815290604052805190602001206040518263ffffffff1660e01b81526004016106279190614b23565b60206040518083038186803b15801561063f57600080fd5b505afa158015610653573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106779190810190613c86565b90505b919050565b6000816106a75760405162461bcd60e51b815260040161069e90614dd5565b60405180910390fd5b600282106106c75760405162461bcd60e51b815260040161069e90614d65565b6106d3600183036124f3565b5468010000000000000000900467ffffffffffffffff166106f6575060006107c5565b60006107266001610709600186036124f3565b5468010000000000000000900467ffffffffffffffff169061251d565b9050600080610733612545565b6001600160a01b031663d29c000a87856040518363ffffffff1660e01b8152600401610760929190614a7e565b604080518083038186803b15801561077757600080fd5b505afa15801561078b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107af9190810190613d10565b90925090506107bf838383612570565b93505050505b92915050565b6000806107d6613ab5565b6107df84610bcf565b905060008060015b60028110156108405761080c8482600281106107ff57fe5b60200201515184906126af565b925061083684826002811061081d57fe5b602002015160016020020151839063ffffffff6126af16565b91506001016107e7565b509093509150505b915091565b6108556126d4565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b61087f6126d4565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906108ca9083906149ee565b60405180910390a150565b60006108df612700565b90505b90565b60006108ef6127b8565b6001600160a01b0316331490506000806109076127e3565b6001600160a01b031663c9a2f3ad336040518263ffffffff1660e01b815260040161093291906149fc565b60206040518083038186803b15801561094a57600080fd5b505afa15801561095e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109829190810190613c86565b1415905060006109906127f7565b6001600160a01b0316336001600160a01b031614905060006109b0612822565b6001600160a01b031663b38988f7336040518263ffffffff1660e01b81526004016109db91906149fc565b60206040518083038186803b1580156109f357600080fd5b505afa158015610a07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610a2b9190810190613c68565b90508380610a365750825b80610a3e5750815b80610a465750805b610a625760405162461bcd60e51b815260040161069e90614c35565b610a8085610a7060006124f3565b600101549063ffffffff6126af16565b610a8a60006124f3565b600101555050505050565b60006108df61284d565b60006060610aab611d30565b905060005b8151811015610bc6576000828281518110610ac757fe5b602090810291909101810151600081815260079092526040918290205460065492517f21f8a7210000000000000000000000000000000000000000000000000000000081529193506001600160a01b039081169216906321f8a72190610b31908590600401614b23565b60206040518083038186803b158015610b4957600080fd5b505afa158015610b5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b819190810190613bc3565b6001600160a01b0316141580610bac57506000818152600760205260409020546001600160a01b0316155b15610bbd57600093505050506108e2565b50600101610ab0565b50600191505090565b610bd7613ab5565b6000806000610be4612545565b6040517fb326f84e0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0382169063b326f84e90610c2f908890600090600401614a25565b604080518083038186803b158015610c4657600080fd5b505afa158015610c5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c7e9190810190613d10565b909350915081158015610c8f575082155b15610ca757610c9c613ab5565b935061067a92505050565b600080610cb66000868661296c565b8751829052875160200181905290925090506000610cd3886105aa565b905060015b8015610e275760001981016000610cee826124f3565b5468010000000000000000900467ffffffffffffffff1690508015801590610d285750610d1a836124f3565b5467ffffffffffffffff1684105b15610e1c576000610d4082600163ffffffff61251d16565b6040517fd29c000a0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0389169063d29c000a90610d8a908f908590600401614a7e565b604080518083038186803b158015610da157600080fd5b505afa158015610db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610dd99190810190613d10565b909a509850610de9848b8b61296c565b9097509550868b8560028110610dfb57fe5b602002015152858b8560028110610e0e57fe5b602002015160016020020152505b505060001901610cd8565b50505050505050919050565b601454610100900460ff1690565b610e49612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b158015610e8157600080fd5b505afa158015610e95573d6000803e3d6000fd5b505050506000610ea3612700565b11610ec05760405162461bcd60e51b815260040161069e90614de5565b610ec8612700565b4203610ed460006124f3565b54600160801b900467ffffffffffffffff161115610f045760405162461bcd60e51b815260040161069e90614c65565b60145460ff1680610f1c5750601454610100900460ff165b610f385760405162461bcd60e51b815260040161069e90614da5565b6000610f43816124f3565b90506000610f5160016124f3565b9050610f828260010154610f768360020154846001015461251d90919063ffffffff16565b9063ffffffff6126af16565b610f8c60006124f3565b60010155600380830154600483015491830154610fb392610f76919063ffffffff61251d16565b610fbd60006124f3565b60030155601354610ffa90600290610fee90600190610fe2908463ffffffff6126af16565b9063ffffffff61251d16565b9063ffffffff612a3916565b60138190556009906002811061100c57fe5b6005020180547fffffffffffffffff00000000000000000000000000000000000000000000000016815560006001808301829055600283018290556003830182905560049092015561107890611061816124f3565b5467ffffffffffffffff169063ffffffff6126af16565b61108260006124f3565b805467ffffffffffffffff191667ffffffffffffffff929092169190911790556110aa612a69565b6001600160a01b031663cd92eba96040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e257600080fd5b505afa1580156110f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061111a9190810190613c86565b61112460006124f3565b805467ffffffffffffffff9290921668010000000000000000026fffffffffffffffff0000000000000000199092169190911790554261116460006124f3565b805467ffffffffffffffff92909216600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff9092169190911790556014805461ffff191690556111cb6111bb60016124f3565b5467ffffffffffffffff16612a94565b5050565b60008060008060008060006111e2613ae2565b6111eb896124f3565b6040805160e081018252825467ffffffffffffffff808216808452680100000000000000008304821660208501819052600160801b909304909116938301849052600185015460608401819052600286015460808501819052600387015460a0860181905260049097015460c0909501859052919f929e50939c50929a5091985091965090945092505050565b60165481565b60055481565b6001546001600160a01b031681565b60008060015b60028110156112e8576112bf6112ae826124f3565b60010154839063ffffffff6126af16565b91506112de6112cd826124f3565b60020154839063ffffffff61251d16565b9150600101611299565b50905090565b60006112f982612b4c565b5092915050565b611308612d3b565b6000611312612d91565b6004546001600160a01b039081169116149050806113425760405162461bcd60e51b815260040161069e90614d85565b600061134c612700565b116113695760405162461bcd60e51b815260040161069e90614de5565b611371612700565b420361137d60006124f3565b54600160801b900467ffffffffffffffff1611156113ad5760405162461bcd60e51b815260040161069e90614c55565b60145460ff16156113d05760405162461bcd60e51b815260040161069e90614db5565b6014805460ff1916600117905560006113e761284d565b9050600081116113fc57506000601555611527565b6114046127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b81526004016114319190614b23565b60206040518083038186803b15801561144957600080fd5b505afa15801561145d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114819190810190613ca4565b6001600160a01b0316639dc29fac73feefeefeefeefeefeefeefeefeefeefeefeefeef836040518363ffffffff1660e01b81526004016114c2929190614a7e565b600060405180830381600087803b1580156114dc57600080fd5b505af11580156114f0573d6000803e3d6000fd5b5050506015829055506115178161150760006124f3565b600101549063ffffffff61251d16565b61152160006124f3565b60010155505b50565b60155481565b600061153a612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b15801561157257600080fd5b505afa158015611586573d6000803e3d6000fd5b50505050611592612d3b565b61159a612e0b565b600480546040517f21f4ae570000000000000000000000000000000000000000000000000000000081526001600160a01b03938416936321f4ae57936115e593889392169101614a0a565b60206040518083038186803b1580156115fd57600080fd5b505afa158015611611573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116359190810190613c68565b6116515760405162461bcd60e51b815260040161069e90614cf5565b61067782612e36565b60145460ff1690565b61166b612d3b565b6000611675612d91565b6004546001600160a01b039081169116149050806116a55760405162461bcd60e51b815260040161069e90614d85565b60006116af612700565b116116cc5760405162461bcd60e51b815260040161069e90614de5565b6116d4612700565b42036116e060006124f3565b54600160801b900467ffffffffffffffff1611156117105760405162461bcd60e51b815260040161069e90614c95565b601454610100900460ff16156117385760405162461bcd60e51b815260040161069e90614c85565b6014805461ff00191661010017905581611751576111cb565b60006117eb6117e6611761612f2b565b6001600160a01b03166302760d946040518163ffffffff1660e01b815260040160206040518083038186803b15801561179957600080fd5b505afa1580156117ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117d19190810190613c86565b6117da86612f56565b9063ffffffff612f6c16565b612f85565b90506117f56127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b81526004016118229190614b23565b60206040518083038186803b15801561183a57600080fd5b505afa15801561184e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118729190810190613ca4565b6001600160a01b031663867904b473feefeefeefeefeefeefeefeefeefeefeefeefeef836040518363ffffffff1660e01b81526004016118b3929190614a7e565b600060405180830381600087803b1580156118cd57600080fd5b505af11580156118e1573d6000803e3d6000fd5b505050506118f381610a7060006124f3565b6118fd60006124f3565b60010155505050565b60008060015b60028110156112e857611932611921826124f3565b60030154839063ffffffff6126af16565b9150611951611940826124f3565b60040154839063ffffffff61251d16565b915060010161190c565b6060611965611d30565b905060005b81518110156111cb57600082828151811061198157fe5b602002602001015190506000600660009054906101000a90046001600160a01b03166001600160a01b031663dacb2d0183846040516020016119c391906149d8565b6040516020818303038152906040526040518363ffffffff1660e01b81526004016119ef929190614b4d565b60206040518083038186803b158015611a0757600080fd5b505afa158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190613bc3565b6000838152600760205260409081902080546001600160a01b0319166001600160a01b038416179055519091507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa6890611a9b9084908490614b31565b60405180910390a1505060010161196a565b6001546001600160a01b03163314611ad75760405162461bcd60e51b815260040161069e90614c45565b6000546001546040517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c92611b1a926001600160a01b0391821692911690614a0a565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b6000611b536127e3565b6001600160a01b0316336001600160a01b03161490506000611b73612a69565b6001600160a01b0316336001600160a01b03161490508180611b925750805b611bae5760405162461bcd60e51b815260040161069e90614c25565b611bb6612545565b6001600160a01b03166394e1a448868686611bd160006124f3565b5460405160e086901b7fffffffff00000000000000000000000000000000000000000000000000000000168152611c249493929168010000000000000000900467ffffffffffffffff1690600401614ac1565b600060405180830381600087803b158015611c3e57600080fd5b505af1158015611c52573d6000803e3d6000fd5b50505050611c82858585611c6660006124f3565b5468010000000000000000900467ffffffffffffffff16612fa7565b5050505050565b611c916126d4565b73c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611cd557600080fd5b505af4158015611ce9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0d9190810190613c86565b8110611d2b5760405162461bcd60e51b815260040161069e90614df5565b601655565b606080611d3b613076565b60408051600d8082526101c0820190925291925060609190602082016101a0803883390190505090507f53797374656d537461747573000000000000000000000000000000000000000081600081518110611d9257fe5b6020026020010181815250507f5065726946696e616e636500000000000000000000000000000000000000000081600181518110611dcc57fe5b6020026020010181815250507f466565506f6f6c5374617465000000000000000000000000000000000000000081600281518110611e0657fe5b6020026020010181815250507f466565506f6f6c457465726e616c53746f72616765000000000000000000000081600381518110611e4057fe5b6020026020010181815250507f45786368616e676572000000000000000000000000000000000000000000000081600481518110611e7a57fe5b6020026020010181815250506524b9b9bab2b960d11b81600581518110611e9d57fe5b6020026020010181815250507f5065726946696e616e636553746174650000000000000000000000000000000081600681518110611ed757fe5b6020026020010181815250507f526577617264457363726f77563200000000000000000000000000000000000081600781518110611f1157fe5b6020026020010181815250507f44656c6567617465417070726f76616c7300000000000000000000000000000081600881518110611f4b57fe5b6020026020010181815250507f4574686572436f6c6c61746572616c705553440000000000000000000000000081600981518110611f8557fe5b6020026020010181815250507f52657761726473446973747269627574696f6e0000000000000000000000000081600a81518110611fbf57fe5b6020026020010181815250507f436f6c6c61746572616c4d616e6167657200000000000000000000000000000081600b81518110611ff957fe5b6020026020010181815250507f43726f7373436861696e4d616e6167657200000000000000000000000000000081600c8151811061203357fe5b60200260200101818152505061204982826130d5565b9250505090565b6000546001600160a01b031681565b6120676126d4565b600280546001600160a01b0319166001600160a01b0383161790556040517ffc80377ca9c49cc11ae6982f390a42db976d5530af7c43889264b13fbbd7c57e906108ca9083906149fc565b6003546001600160a01b031681565b60006108df61214d6120d161318a565b73c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f769190810190613c86565b6121556131f2565b9063ffffffff61325a16565b612169613284565b600554421061218a5760405162461bcd60e51b815260040161069e90614d75565b612192612a69565b6001600160a01b031663cd92eba96040518163ffffffff1660e01b815260040160206040518083038186803b1580156121ca57600080fd5b505afa1580156121de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122029190810190613c86565b8611156122215760405162461bcd60e51b815260040161069e90614d55565b6040518060e001604052808867ffffffffffffffff1681526020018767ffffffffffffffff1681526020018667ffffffffffffffff168152602001858152602001848152602001838152602001828152506009612291600260ff16610fee8c6013546126af90919063ffffffff16565b6002811061229b57fe5b82516005919091029190910180546020840151604085015167ffffffffffffffff1990921667ffffffffffffffff948516176fffffffffffffffff000000000000000019166801000000000000000091851691909102177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff16600160801b9390911692909202919091178155606082015160018201556080820151600282015560a0820151600382015560c0909101516004909101555050505050505050565b60006108df6131f2565b61236d613309565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b600281565b600061239e612a0e565b6001600160a01b0316637c3125416040518163ffffffff1660e01b815260040160006040518083038186803b1580156123d657600080fd5b505afa1580156123ea573d6000803e3d6000fd5b505050506123f6612d3b565b6004546108df906001600160a01b0316612e36565b6004546001600160a01b031681565b60006108df61318a565b73feefeefeefeefeefeefeefeefeefeefeefeefeef81565b6002546001600160a01b031681565b6000612455613348565b6004549091506001600160a01b038083169116148061247c5750336001600160a01b038216145b6124985760405162461bcd60e51b815260040161069e90614d05565b6124b6826124a660006124f3565b600301549063ffffffff6126af16565b6124c060006124f3565b600301555050565b60006108df7f466565506f6f6c457465726e616c53746f72616765000000000000000000000061336f565b60006009600260ff1683601354018161250857fe5b066002811061251357fe5b6005020192915050565b60008282111561253f5760405162461bcd60e51b815260040161069e90614cb5565b50900390565b60006108df7f466565506f6f6c5374617465000000000000000000000000000000000000000061336f565b60008061257b612a69565b905060006126a3856117da846001600160a01b03166308d95cd5886040518263ffffffff1660e01b81526004016125b29190614b23565b60206040518083038186803b1580156125ca57600080fd5b505afa1580156125de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126029190810190613c86565b6040517f08d95cd50000000000000000000000000000000000000000000000000000000081526001600160a01b038716906308d95cd590612647908d90600401614b23565b60206040518083038186803b15801561265f57600080fd5b505afa158015612673573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126979190810190613c86565b9063ffffffff6133cc16565b925050505b9392505050565b6000828201838110156126a85760405162461bcd60e51b815260040161069e90614ca5565b6000546001600160a01b031633146126fe5760405162461bcd60e51b815260040161069e90614d15565b565b600061270a6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f666565506572696f644475726174696f6e0000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b60206040518083038186803b15801561278057600080fd5b505afa158015612794573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108df9190810190613c86565b60006108df7f45786368616e676572000000000000000000000000000000000000000000000061336f565b60006108df6524b9b9bab2b960d11b61336f565b60006108df7f4574686572436f6c6c61746572616c705553440000000000000000000000000061336f565b60006108df7f436f6c6c61746572616c4d616e6167657200000000000000000000000000000061336f565b60008061294761285b612f2b565b6001600160a01b03166302760d946040518163ffffffff1660e01b815260040160206040518083038186803b15801561289357600080fd5b505afa1580156128a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128cb9190810190613c86565b73c59ba4786ca553a4591100e97673c1cff98e5b3763d5e5e6e66040518163ffffffff1660e01b815260040160206040518083038186803b15801561290f57600080fd5b505af4158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fe29190810190613c86565b90506129666117e6826117da61295d60006124f3565b60010154612f56565b91505090565b6000808361297f57506000905080612a06565b8385156129aa576000612999600161070960018a036124f3565b90506129a6818787612570565b9150505b60006129c9826129b9896124f3565b600101549063ffffffff61325a16565b905060006129ea836129da8a6124f3565b600301549063ffffffff61325a16565b90506129f582612f85565b6129fe82612f85565b945094505050505b935093915050565b60006108df7f53797374656d537461747573000000000000000000000000000000000000000061336f565b600081612a585760405162461bcd60e51b815260040161069e90614ce5565b818381612a6157fe5b069392505050565b60006108df7f5065726946696e616e636553746174650000000000000000000000000000000061336f565b6002546040516001600160a01b039091169063907dff9790612aba908490602001614b23565b6040516020818303038152906040526001604051612ad7906149e3565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b168252612b1e93929160009081908190600401614b6d565b600060405180830381600087803b158015612b3857600080fd5b505af1158015611c82573d6000803e3d6000fd5b6000806000612b596127e3565b6001600160a01b0316639288e7048560008060016040518563ffffffff1660e01b8152600401612b8c9493929190614a40565b60206040518083038186803b158015612ba457600080fd5b505afa158015612bb8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bdc9190810190613c86565b9050612c33612c2b60165473c59ba4786ca553a4591100e97673c1cff98e5b3763907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561211557600080fd5b612155613410565b811115612c47576000809250925050610848565b600080612c526127e3565b6001600160a01b031663ae3bbbbb876040518263ffffffff1660e01b8152600401612c7d91906149ee565b604080518083038186803b158015612c9457600080fd5b505afa158015612ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ccc9190810190613ce0565b915091506000612cda6131f2565b905080831015612cf35750600194509250610848915050565b6000612d10612d036120d161318a565b839063ffffffff61325a16565b905080841115612d2b57600083965096505050505050610848565b5060019550909350505050915091565b6002546001600160a01b03163314801590612d6157506003546001600160a01b03163314155b8015612d7857506004546001600160a01b03163314155b156126fe57600480546001600160a01b03191633179055565b6000612d9b612f2b565b6001600160a01b03166360b3a0336040518163ffffffff1660e01b815260040160206040518083038186803b158015612dd357600080fd5b505afa158015612de7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108df9190810190613bc3565b60006108df7f44656c6567617465417070726f76616c7300000000000000000000000000000061336f565b6000808080808080612e4788612b4c565b9150915081612e685760405162461bcd60e51b815260040161069e90614d25565b8015612e865760405162461bcd60e51b815260040161069e90614d35565b612e8f886107cb565b909450925083151580612ea25750600083115b612ebe5760405162461bcd60e51b815260040161069e90614cd5565b612edc88612ecc60016124f3565b5467ffffffffffffffff16613478565b8315612ef757612eeb8461352b565b9450612ef788866135f7565b8215612f1257612f0683613793565b9550612f12888761386d565b612f1d8886886138e7565b506001979650505050505050565b60006108df7f43726f7373436861696e4d616e6167657200000000000000000000000000000061336f565b600061067782633b9aca0063ffffffff6139ac16565b60006126a883836b033b2e3c9fd0803ce80000006139e6565b60006305f5e10082046005600a820610612f9d57600a015b600a900492915050565b6002546040516001600160a01b039091169063907dff9790612fd190869086908690602001614e05565b6040516020818303038152906040526002604051612fee906149ad565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16825261303e9392916001600160a01b038b16906000908190600401614bcc565b600060405180830381600087803b15801561305857600080fd5b505af115801561306c573d6000803e3d6000fd5b5050505050505050565b604080516001808252818301909252606091602080830190803883390190505090507f466c657869626c6553746f726167650000000000000000000000000000000000816000815181106130c657fe5b60200260200101818152505090565b60608151835101604051908082528060200260200182016040528015613105578160200160208202803883390190505b50905060005b83518110156131475783818151811061312057fe5b602002602001015182828151811061313457fe5b602090810291909101015260010161310b565b5060005b82518110156112f95782818151811061316057fe5b602002602001015182828651018151811061317757fe5b602090810291909101015260010161314b565b60006131946133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f7461726765745468726573686f6c6400000000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b60006131fc6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f69737375616e6365526174696f000000000000000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b6000670de0b6b3a7640000613275848463ffffffff6139ac16565b8161327c57fe5b049392505050565b6002546001600160a01b031633148015906132aa57506003546001600160a01b03163314155b80156132c157506004546001600160a01b03163314155b156132d957600480546001600160a01b031916331790555b6000546004546001600160a01b039081169116146126fe5760405162461bcd60e51b815260040161069e90614c75565b6002546001600160a01b031633148061332c57506003546001600160a01b031633145b6126fe5760405162461bcd60e51b815260040161069e90614dc5565b60006108df7f52657761726473446973747269627574696f6e000000000000000000000000005b60008181526007602090815260408083205490516001600160a01b03909116918215159161339f918691016149b8565b604051602081830303815290604052906112f95760405162461bcd60e51b815260040161069e9190614c14565b60006126a883836b033b2e3c9fd0803ce8000000613a23565b60006108df7f466c657869626c6553746f72616765000000000000000000000000000000000061336f565b600061341a6133e5565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7f65787465726e616c546f6b656e51756f746100000000000000000000000000006040518363ffffffff1660e01b8152600401612768929190614b3f565b6134806124c8565b6001600160a01b0316633562fd207f6c6173745f6665655f7769746864726177616c00000000000000000000000000846040516020016134c192919061497c565b60405160208183030381529060405280519060200120836040518363ffffffff1660e01b81526004016134f5929190614b3f565b600060405180830381600087803b15801561350f57600080fd5b505af1158015613523573d6000803e3d6000fd5b505050505050565b6000818160015b60028110156135ef576000613546826124f3565b600201549050600061355b82611507856124f3565b905080156135e45760008582106135725785613574565b815b9050613586838263ffffffff6126af16565b61358f856124f3565b600201556135a3868263ffffffff61251d16565b95506135b5858263ffffffff6126af16565b9450856135ca5784965050505050505061067a565b831580156135d85750600086115b156135e257600095505b505b505060001901613532565b509392505050565b816001600160a01b03811673feefeefeefeefeefeefeefeefeefeefeefeefeef14156136355760405162461bcd60e51b815260040161069e90614d95565b600061363f6127e3565b6001600160a01b03166357ad46636008546040518263ffffffff1660e01b815260040161366c9190614b23565b60206040518083038186803b15801561368457600080fd5b505afa158015613698573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136bc9190810190613ca4565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081529091506001600160a01b03821690639dc29fac9061371a9073feefeefeefeefeefeefeefeefeefeefeefeefeef908790600401614a7e565b600060405180830381600087803b15801561373457600080fd5b505af1158015613748573d6000803e3d6000fd5b50506040517f867904b40000000000000000000000000000000000000000000000000000000081526001600160a01b038416925063867904b4915061303e9087908790600401614a7e565b6000818160015b60028110156135ef5760006137ce6137b1836124f3565b600401546137be846124f3565b600301549063ffffffff61251d16565b905080156138635760008482106137e557846137e7565b815b9050613806816137f6856124f3565b600401549063ffffffff6126af16565b61380f846124f3565b60040155613823858263ffffffff61251d16565b9450613835848263ffffffff6126af16565b93508461384957839550505050505061067a565b821580156138575750600085115b1561386157600094505b505b506000190161379a565b816001600160a01b03811673feefeefeefeefeefeefeefeefeefeefeefeefeef14156138ab5760405162461bcd60e51b815260040161069e90614d95565b6301dfe2006138b8613a55565b6001600160a01b0316631bb47b448585846040518463ffffffff1660e01b815260040161303e93929190614a99565b6002546040516001600160a01b039091169063907dff979061391190869086908690602001614a99565b604051602081830303815290604052600160405161392e906149a2565b6040519081900381207fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16825261397593929160009081908190600401614b6d565b600060405180830381600087803b15801561398f57600080fd5b505af11580156139a3573d6000803e3d6000fd5b50505050505050565b6000826139bb575060006107c5565b828202828482816139c857fe5b04146126a85760405162461bcd60e51b815260040161069e90614d45565b600080600a83046139fd868663ffffffff6139ac16565b81613a0457fe5b0490506005600a825b0610613a1757600a015b600a9004949350505050565b600080613a4984613a3d87600a870263ffffffff6139ac16565b9063ffffffff613a8016565b90506005600a82613a0d565b60006108df7f526577617264457363726f77563200000000000000000000000000000000000061336f565b6000808211613aa15760405162461bcd60e51b815260040161069e90614cc5565b6000828481613aac57fe5b04949350505050565b60405180604001604052806002905b613acc613b3d565b815260200190600190039081613ac45790505090565b6040518060e00160405280600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600067ffffffffffffffff168152602001600081526020016000815260200160008152602001600081525090565b60405180604001604052806002906020820280388339509192915050565b80356107c581614f5c565b80516107c581614f5c565b80516107c581614f70565b80516107c581614f79565b80516107c581614f82565b80356107c581614f79565b600060208284031215613baf57600080fd5b6000613bbb8484613b5b565b949350505050565b600060208284031215613bd557600080fd5b6000613bbb8484613b66565b60008060408385031215613bf457600080fd5b6000613c008585613b5b565b9250506020613c1185828601613b92565b9150509250929050565b600080600060608486031215613c3057600080fd5b6000613c3c8686613b5b565b9350506020613c4d86828701613b92565b9250506040613c5e86828701613b92565b9150509250925092565b600060208284031215613c7a57600080fd5b6000613bbb8484613b71565b600060208284031215613c9857600080fd5b6000613bbb8484613b7c565b600060208284031215613cb657600080fd5b6000613bbb8484613b87565b600060208284031215613cd457600080fd5b6000613bbb8484613b92565b60008060408385031215613cf357600080fd5b6000613cff8585613b7c565b9250506020613c1185828601613b71565b60008060408385031215613d2357600080fd5b6000613d2f8585613b7c565b9250506020613c1185828601613b7c565b600080600080600080600080610100898b031215613d5d57600080fd5b6000613d698b8b613b92565b9850506020613d7a8b828c01613b92565b9750506040613d8b8b828c01613b92565b9650506060613d9c8b828c01613b92565b9550506080613dad8b828c01613b92565b94505060a0613dbe8b828c01613b92565b93505060c0613dcf8b828c01613b92565b92505060e0613de08b828c01613b92565b9150509295985092959890939650565b6000613dfc8383613ee6565b505060400190565b6000613e108383613f3b565b505060200190565b613e2181614edc565b82525050565b613e2181614ea2565b613e21613e3c82614ea2565b614f3b565b613e4a81614e8f565b613e54818461067a565b9250613e5f826108e2565b8060005b83811015613523578151613e778782613df0565b9650613e8283614e89565b925050600101613e63565b6000613e9882614e95565b613ea28185614e99565b9350613ead83614e89565b8060005b83811015613edb578151613ec58882613e04565b9750613ed083614e89565b925050600101613eb1565b509495945050505050565b613eef81614e8f565b613ef9818461067a565b9250613f04826108e2565b8060005b83811015613523578151613f1c8782613e04565b9650613f2783614e89565b925050600101613f08565b613e2181614ead565b613e21816108e2565b613e21613f50826108e2565b6108e2565b6000613f6082614e95565b613f6a8185614e99565b9350613f7a818560208601614f0b565b613f8381614f4c565b9093019392505050565b613e2181614eb2565b613e2181614ee7565b613e2181614ef5565b6000613fb5602083614e99565b7f49737375657220616e64205065726946696e616e63655374617465206f6e6c79815260200192915050565b6000613fee601783614e99565b7f4f6e6c7920496e7465726e616c20436f6e747261637473000000000000000000815260200192915050565b6000614027603583614e99565b7f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7581527f2063616e20616363657074206f776e6572736869700000000000000000000000602082015260400192915050565b6000614086602983614e99565b7f646973747269627574696e672066656520726577617264206e6f74207965742081527f617661696c61626c650000000000000000000000000000000000000000000000602082015260400192915050565b60006140e5601d83614e99565b7f546f6f206561726c7920746f20636c6f73652066656520706572696f64000000815260200192915050565b600061411e601383614e99565b7f4f776e6572206f6e6c792066756e6374696f6e00000000000000000000000000815260200192915050565b600061415760248361067a565b7f46656573436c61696d656428616464726573732c75696e743235362c75696e7481527f3235362900000000000000000000000000000000000000000000000000000000602082015260240192915050565b60006141b6603883614e99565b7f416c6c6f636174696e6720666565207265776172647320697320706f7373696281527f6c65206f6e6c79206f6e636520696e206120706572696f640000000000000000602082015260400192915050565b6000614215602783614e99565b7f616c6c6f636174696e672066656520726577617264206e6f742079657420617681527f61696c61626c6500000000000000000000000000000000000000000000000000602082015260400192915050565b600061427460378361067a565b7f49737375616e636544656274526174696f456e74727928616464726573732c7581527f696e743235362c75696e743235362c75696e7432353629000000000000000000602082015260370192915050565b60006142d3601b83614e99565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b600061430c601e83614e99565b7f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815260200192915050565b6000614345601a83614e99565b7f536166654d6174683a206469766973696f6e206279207a65726f000000000000815260200192915050565b600061437e604083614e99565b7f4e6f2066656573206f72207265776172647320617661696c61626c6520666f7281527f20706572696f642c206f72206665657320616c726561647920636c61696d6564602082015260400192915050565b60006143dd60118361067a565b7f4d697373696e6720616464726573733a20000000000000000000000000000000815260110192915050565b6000614416601883614e99565b7f536166654d6174683a206d6f64756c6f206279207a65726f0000000000000000815260200192915050565b600061444f601f83614e99565b7f4e6f7420617070726f76656420746f20636c61696d206f6e20626568616c6600815260200192915050565b6000614488601e83614e99565b7f43616c6c6572206973206e6f742072657761726473417574686f726974790000815260200192915050565b60006144c1602f83614e99565b7f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726681527f6f726d207468697320616374696f6e0000000000000000000000000000000000602082015260400192915050565b6000614520601f83614e99565b7f432d526174696f2062656c6f772070656e616c7479207468726573686f6c6400815260200192915050565b6000614559601f83614e99565b7f412070796e7468206f722050455249207261746520697320696e76616c696400815260200192915050565b6000614592602183614e99565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f81527f7700000000000000000000000000000000000000000000000000000000000000602082015260400192915050565b60006145f1601683614e99565b7f43616e6e6f7420696d706f727420626164206461746100000000000000000000815260200192915050565b600061462a601d83614e99565b7f4578636565647320746865204645455f504552494f445f4c454e475448000000815260200192915050565b6000614663602983614e99565b7f43616e206f6e6c7920706572666f726d207468697320616374696f6e2064757281527f696e672073657475700000000000000000000000000000000000000000000000602082015260400192915050565b60006146c260198361067a565b7f5265736f6c766572206d697373696e67207461726765743a2000000000000000815260190192915050565b60006146fb601183614e99565b7f64656274206d616e61676572206f6e6c79000000000000000000000000000000815260200192915050565b6000614734601783614e99565b7f4665652061646472657373206e6f7420616c6c6f776564000000000000000000815260200192915050565b600061476d604483614e99565b7f66656520726577617264732073686f756c64206265206469737472696275746581527f64206f7220616c6c6f6361746564206265666f726520636c6f73696e6720706560208201527f72696f6400000000000000000000000000000000000000000000000000000000604082015260600192915050565b60006147f2603a83614e99565b7f446973747269627574696e6720666565207265776172647320697320706f737381527f69626c65206f6e6c79206f6e636520696e206120706572696f64000000000000602082015260400192915050565b600061485160188361067a565b7f466565506572696f64436c6f7365642875696e74323536290000000000000000815260180192915050565b600061488a601783614e99565b7f4f6e6c79207468652070726f78792063616e2063616c6c000000000000000000815260200192915050565b60006148c3602083614e99565b7f43757272656e7420706572696f64206973206e6f7420636c6f73656420796574815260200192915050565b60006148fc601b83614e99565b7f46656520506572696f64204475726174696f6e206e6f74207365740000000000815260200192915050565b6000614935602083614e99565b7f546f6c6572616e63652076616c75652063616e6e6f7420657863656564732031815260200192915050565b613e2181614f00565b613e2181614ec9565b613e2181614ed6565b60006149888285613f44565b6020820191506149988284613e30565b5060140192915050565b60006107c58261414a565b60006107c582614267565b60006149c3826143d0565b91506149cf8284613f44565b50602001919050565b60006149c3826146b5565b60006107c582614844565b602081016107c58284613e27565b602081016107c58284613e18565b60408101614a188285613e27565b6126a86020830184613e27565b60408101614a338285613e27565b6126a86020830184613f9f565b60808101614a4e8287613e27565b614a5b6020830186613f9f565b614a686040830185613f9f565b614a756060830184613f32565b95945050505050565b60408101614a8c8285613e27565b6126a86020830184613f3b565b60608101614aa78286613e27565b614ab46020830185613f3b565b613bbb6040830184613f3b565b60808101614acf8287613e27565b614adc6020830186613f3b565b614ae96040830185613f3b565b614a756060830184614961565b608081016107c58284613e41565b602080825281016126a88184613e8d565b602081016107c58284613f32565b602081016107c58284613f3b565b60408101614a188285613f3b565b60408101614a8c8285613f3b565b60408101614b5b8285613f3b565b8181036020830152613bbb8184613f55565b60c08082528101614b7e8189613f55565b9050614b8d6020830188613f9f565b614b9a6040830187613f3b565b614ba76060830186613f96565b614bb46080830185613f96565b614bc160a0830184613f96565b979650505050505050565b60c08082528101614bdd8189613f55565b9050614bec6020830188613f9f565b614bf96040830187613f3b565b614ba76060830186613f3b565b602081016107c58284613f8d565b602080825281016126a88184613f55565b6020808252810161067781613fa8565b6020808252810161067781613fe1565b602080825281016106778161401a565b6020808252810161067781614079565b60208082528101610677816140d8565b6020808252810161067781614111565b60208082528101610677816141a9565b6020808252810161067781614208565b60208082528101610677816142c6565b60208082528101610677816142ff565b6020808252810161067781614338565b6020808252810161067781614371565b6020808252810161067781614409565b6020808252810161067781614442565b602080825281016106778161447b565b60208082528101610677816144b4565b6020808252810161067781614513565b602080825281016106778161454c565b6020808252810161067781614585565b60208082528101610677816145e4565b602080825281016106778161461d565b6020808252810161067781614656565b60208082528101610677816146ee565b6020808252810161067781614727565b6020808252810161067781614760565b60208082528101610677816147e5565b602080825281016106778161487d565b60208082528101610677816148b6565b60208082528101610677816148ef565b6020808252810161067781614928565b60608101614aa78286613f3b565b60e08101614e21828a61496a565b614e2e602083018961496a565b614e3b604083018861496a565b614e486060830187613f3b565b614e556080830186613f3b565b614e6260a0830185613f3b565b614e6f60c0830184613f3b565b98975050505050505050565b602081016107c58284614973565b60200190565b50600290565b5190565b90815260200190565b600061067782614ebd565b151590565b600061067782614ea2565b6001600160a01b031690565b67ffffffffffffffff1690565b60ff1690565b600061067782614eb2565b6000610677613f50836108e2565b6000610677826108e2565b600061067782614ec9565b60005b83811015614f26578181015183820152602001614f0e565b83811115614f35576000848401525b50505050565b600061067782600061067782614f56565b601f01601f191690565b60601b90565b614f6581614ea2565b811461152757600080fd5b614f6581614ead565b614f65816108e2565b614f6581614eb256fea365627a7a72315820882079c2d3031f6a74125aae31f9cd51dec8225767dd29b17b1d172a05b79e396c6578706572696d656e74616cf564736f6c63430005100040

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000006e2eea6d56368e60f74a3f89f30b15b46bf3f7df000000000000000000000000166220468aff631290b7e1ddff7f45b052de832400000000000000000000000022f5d16097cf2396d6c893a77ea3b97234c0c114

-----Decoded View---------------
Arg [0] : _proxy (address): 0x6e2EEa6D56368E60f74a3F89F30b15B46BF3f7DF
Arg [1] : _owner (address): 0x166220468aff631290b7E1Ddff7F45b052De8324
Arg [2] : _resolver (address): 0x22F5D16097CF2396D6C893A77Ea3b97234c0C114

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000006e2eea6d56368e60f74a3f89f30b15b46bf3f7df
Arg [1] : 000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324
Arg [2] : 00000000000000000000000022f5d16097cf2396d6c893a77ea3b97234c0c114


Library Used

SafeDecimalMath : 0xc59ba4786ca553a4591100e97673c1cff98e5b37Unverified

Deployed Bytecode Sourcemap

75335:38115:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;75335:38115:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15737:31;;;:::i;:::-;;;;;;;;;;;;;;;;109601:213;;;;;;;;;:::i;:::-;;;;;;;;108421:934;;;;;;;;;:::i;100631:658::-;;;;;;;;;:::i;:::-;;;;;;;;;6633:145;;;;;;;;;:::i;:::-;;2268:141;;;;;;;;;:::i;81581:106::-;;;:::i;86882:258::-;;;;;;;;;:::i;82788:132::-;;;:::i;17432:537::-;;;:::i;:::-;;;;;;;;102992:3094;;;;;;;;;:::i;:::-;;;;;;;;82666:114;;;:::i;88039:2683::-;;;:::i;81805:727::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;76696:29;;;:::i;8019:27::-;;;:::i;2037:29::-;;;:::i;:::-;;;;;;;;99443:433;;;:::i;102660:167::-;;;;;;;;;:::i;83656:970::-;;;:::i;76649:38::-;;;:::i;91273:283::-;;;;;;;;;:::i;82540:118::-;;;:::i;84634:1043::-;;;;;;;;;:::i;99981:463::-;;;:::i;16726:657::-;;;:::i;2417:271::-;;;:::i;86239:493::-;;;;;;;;;:::i;87763:184::-;;;;;;;;;:::i;78326:1006::-;;;:::i;:::-;;;;;;;;2010:20;;;:::i;6488:137::-;;;;;;;;;:::i;5911:29::-;;;:::i;109925:173::-;;;:::i;93375:884::-;;;;;;;;;:::i;81475:98::-;;;:::i;6786:102::-;;;;;;;;;:::i;76406:43::-;;;:::i;:::-;;;;;;;;90832:125;;;:::i;6180:28::-;;;:::i;81695:102::-;;;:::i;75532:80::-;;;:::i;5886:18::-;;;:::i;87288:467::-;;;;;;;;;:::i;15737:31::-;;;-1:-1:-1;;;;;15737:31:0;;:::o;109601:213::-;109678:4;109702:23;:21;:23::i;:::-;-1:-1:-1;;;;;109702:36:0;;109766:19;109787:16;109749:55;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;109749:55:0;;;109739:66;;;;;;109702:104;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;109702:104:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;109702:104:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;109702:104:0;;;;;;;;;109695:111;;109601:213;;;;:::o;108421:934::-;108511:4;108536:11;108528:56;;;;-1:-1:-1;;;108528:56:0;;;;;;;;;;;;;;;;;76448:1;108603:26;;108595:68;;;;-1:-1:-1;;;108595:68:0;;;;;;;;;108793:36;108827:1;108818:6;:10;108793:24;:36::i;:::-;:54;;;;;;108789:73;;-1:-1:-1;108861:1:0;108854:8;;108789:73;108875:21;108899:70;108967:1;108907:36;108941:1;108932:6;:10;108907:24;:36::i;:::-;:54;;;;;;;108899:67;:70::i;:::-;108875:94;;108982:24;109017:19;109087:14;:12;:14::i;:::-;-1:-1:-1;;;;;109087:37:0;;109125:7;109134:16;109087:64;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;109087:64:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;109087:64:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;109087:64:0;;;;;;;;;109047:104;;-1:-1:-1;109047:104:0;-1:-1:-1;109264:83:0;109293:16;109047:104;;109264:28;:83::i;:::-;109257:90;;;;;108421:934;;;;;:::o;100631:658::-;100692:4;100698;100743:42;;:::i;:::-;100788:21;100801:7;100788:12;:21::i;:::-;100743:66;-1:-1:-1;100822:14:0;;100981:1;100967:175;76448:1;100984:21;;100967:175;;;101039:29;101053:8;101062:1;101053:11;;;;;;;;;;;:14;101039:9;;:13;:29::i;:::-;101027:41;;101098:32;101115:8;101124:1;101115:11;;;;;;;;;;;101127:1;101115:14;;;;101098:12;;:32;:16;:32;:::i;:::-;101083:47;-1:-1:-1;101007:3:0;;100967:175;;;-1:-1:-1;101257:9:0;;-1:-1:-1;101268:12:0;-1:-1:-1;;100631:658:0;;;;:::o;6633:145::-;2726:12;:10;:12::i;:::-;6727:16;:43;;-1:-1:-1;;;;;;6727:43:0;-1:-1:-1;;;;;6727:43:0;;;;;;;;;;6633:145::o;2268:141::-;2726:12;:10;:12::i;:::-;2340:14;:23;;-1:-1:-1;;;;;;2340:23:0;-1:-1:-1;;;;;2340:23:0;;;;;2379:22;;;;;;2340:23;;2379:22;;;;;;;;;;2268:141;:::o;81581:106::-;81633:4;81657:22;:20;:22::i;:::-;81650:29;;81581:106;;:::o;86882:258::-;110823:16;110864:11;:9;:11::i;:::-;-1:-1:-1;;;;;110842:34:0;:10;:34;;-1:-1:-1;110887:12:0;;110902:8;:6;:8::i;:::-;-1:-1:-1;;;;;110902:24:0;;110927:10;110902:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110902:36:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;110902:36:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;110902:36:0;;;;;;;;;:50;;110887:65;;110963:26;111014:21;:19;:21::i;:::-;-1:-1:-1;;;;;110992:44:0;:10;-1:-1:-1;;;;;110992:44:0;;110963:73;;111047:17;111067:19;:17;:19::i;:::-;-1:-1:-1;;;;;111067:33:0;;111101:10;111067:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;111067:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;111067:45:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;111067:45:0;;;;;;;;;111047:65;;111133:11;:22;;;;111148:7;111133:22;:47;;;;111159:21;111133:47;:63;;;;111184:12;111133:63;111125:99;;;;-1:-1:-1;;;111125:99:0;;;;;;;;;87076:56;87125:6;87076:27;87101:1;87076:24;:27::i;:::-;:44;;;;:56;:48;:56;:::i;:::-;87029:27;87054:1;87029:24;:27::i;:::-;:44;;:103;-1:-1:-1;;;;;86882:258:0:o;82788:132::-;82854:4;82878:34;:32;:34::i;17432:537::-;17483:4;17500:34;17537:27;:25;:27::i;:::-;17500:64;-1:-1:-1;17580:6:0;17575:363;17596:17;:24;17592:1;:28;17575:363;;;17642:12;17657:17;17675:1;17657:20;;;;;;;;;;;;;;;;;;;17824:18;;;;:12;:18;;;;;;;;;17795:8;;:25;;;;;17657:20;;-1:-1:-1;;;;;;17824:18:0;;;;17795:8;;:19;;:25;;17657:20;;17795:25;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17795:25:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;17795:25:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;17795:25:0;;;;;;;;;-1:-1:-1;;;;;17795:47:0;;;:83;;;-1:-1:-1;17876:1:0;17846:18;;;:12;:18;;;;;;-1:-1:-1;;;;;17846:18:0;:32;17795:83;17791:136;;;17906:5;17899:12;;;;;;;17791:136;-1:-1:-1;17622:3:0;;17575:363;;;;17957:4;17950:11;;;17432:537;:::o;102992:3094::-;103052:41;;:::i;:::-;103210:28;103249:19;103279:26;103308:14;:12;:14::i;:::-;103379:46;;;;;103279:43;;-1:-1:-1;;;;;;103379:34:0;;;;;:46;;103414:7;;103423:1;;103379:46;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;103379:46:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;103379:46:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;103379:46:0;;;;;;;;;103335:90;;-1:-1:-1;103335:90:0;-1:-1:-1;103728:19:0;;:51;;;;-1:-1:-1;103751:28:0;;103728:51;103724:162;;;103796:45;;:::i;:::-;103863:11;-1:-1:-1;103856:18:0;;-1:-1:-1;;;103856:18:0;103724:162;104061:19;104091:22;104162:69;104188:1;104191:23;104216:14;104162:25;:69::i;:::-;104244:10;;:30;;;104285:10;;104244;104285:13;:33;;;104124:107;;-1:-1:-1;104124:107:0;-1:-1:-1;104252:1:0;104411:29;104432:7;104411:20;:29::i;:::-;104386:54;-1:-1:-1;104653:1:0;104619:1460;104656:5;;104619:1460;;-1:-1:-1;;104695:5:0;;104683:9;104750:30;104695:5;104750:24;:30::i;:::-;:48;;;;;;;-1:-1:-1;104936:31:0;;;;;:94;;;104991:27;105016:1;104991:24;:27::i;:::-;:39;;;104971:59;;104936:94;104932:1136;;;105377:21;105401:43;105409:27;105442:1;105401:43;:40;:43;:::i;:::-;105758:63;;;;;105377:67;;-1:-1:-1;;;;;;105758:36:0;;;;;:63;;105795:7;;105377:67;;105758:63;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;105758:63:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;105758:63:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;105758:63:0;;;;;;;;;105714:107;;-1:-1:-1;105714:107:0;-1:-1:-1;105880:69:0;105906:1;105714:107;;105880:25;:69::i;:::-;105842:107;;-1:-1:-1;105842:107:0;-1:-1:-1;105842:107:0;105970:7;105978:1;105970:10;;;;;;;;;;;:30;106035:17;106019:7;106027:1;106019:10;;;;;;;;;;;106030:1;106019:13;;;:33;-1:-1:-1;104932:1136:0;-1:-1:-1;;;;104663:3:0;104619:1460;;;;102992:3094;;;;;;;;;:::o;82666:114::-;82748:24;;;;;;;;82666:114::o;88039:2683::-;111885:14;:12;:14::i;:::-;-1:-1:-1;;;;;111885:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;111885:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;111885:38:0;;;;88140:1;88115:22;:20;:22::i;:::-;:26;88107:66;;;;-1:-1:-1;;;88107:66:0;;;;;;;;;88240:22;:20;:22::i;:::-;88234:3;:28;88192:27;88217:1;88192:24;:27::i;:::-;:37;-1:-1:-1;;;88192:37:0;;;;:71;;88184:113;;;;-1:-1:-1;;;88184:113:0;;;;;;;;;88330:26;;;;;:54;;-1:-1:-1;88360:24:0;;;;;;;88330:54;88308:172;;;;-1:-1:-1;;;88308:172:0;;;;;;;;;88629:31;88663:47;88629:31;88663:24;:47::i;:::-;88629:81;-1:-1:-1;88721:34:0;88758:47;88803:1;88758:24;:47::i;:::-;88721:84;;89369:145;89483:13;:30;;;89369:95;89435:16;:28;;;89369:16;:47;;;:65;;:95;;;;:::i;:::-;:113;:145;:113;:145;:::i;:::-;89302:47;89327:21;89302:24;:47::i;:::-;:64;;:212;89715:33;;;;;89664:31;;;;89595:50;;;;:154;;:101;;:50;:101;:68;:101;:::i;:154::-;89525:47;89550:21;89525:24;:47::i;:::-;:67;;:224;89862:17;;:70;;76448:1;;89862:47;;89907:1;;89862:40;;76448:1;89862:40;:21;:40;:::i;:::-;:44;:47;:44;:47;:::i;:::-;:51;:70;:51;:70;:::i;:::-;89842:17;:90;;;90046:17;;:36;;;;;;;;;;90039:43;;;;;;90046:36;90039:43;;;;;;;;;;;;;;;;;;;;;;;;90257:55;;90265:27;90039:43;90265:24;:27::i;:::-;:39;;;;90257:55;:52;:55;:::i;:::-;90208:27;90233:1;90208:24;:27::i;:::-;:105;;-1:-1:-1;;90208:105:0;;;;;;;;;;;;90379:18;:16;:18::i;:::-;-1:-1:-1;;;;;90379:35:0;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;90379:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;90379:37:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;90379:37:0;;;;;;;;;90324:27;90349:1;90324:24;:27::i;:::-;:93;;;;;;;;;-1:-1:-1;;90324:93:0;;;;;;;;;90475:3;90428:27;90324:45;90428:24;:27::i;:::-;:51;;;;;;;-1:-1:-1;;;90428:51:0;;;;;;;;;;;90566:26;:34;;-1:-1:-1;;90611:32:0;;;90654:60;90674:27;-1:-1:-1;90674:24:0;:27::i;:::-;:39;;;90654:19;:60::i;:::-;111934:1;;88039:2683::o;81805:727::-;81907:18;81940:24;81979:16;82010:21;82046:16;82077:24;82116:19;82163:26;;:::i;:::-;82192:31;82217:5;82192:24;:31::i;:::-;82163:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;82163:60:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;82163:60:0;;-1:-1:-1;82163:60:0;;-1:-1:-1;82163:60:0;;-1:-1:-1;82163:60:0;;-1:-1:-1;82163:60:0;;-1:-1:-1;81805:727:0;-1:-1:-1;;;81805:727:0:o;76696:29::-;;;;:::o;8019:27::-;;;;:::o;2037:29::-;;;-1:-1:-1;;;;;2037:29:0;;:::o;99443:433::-;99496:4;;99630:1;99616:224;76448:1;99633:21;;99616:224;;;99688:59;99702:27;99727:1;99702:24;:27::i;:::-;:44;;;99688:9;;:59;:13;:59;:::i;:::-;99676:71;;99774:54;99788:27;99813:1;99788:24;:27::i;:::-;:39;;;99774:9;;:54;:13;:54;:::i;:::-;99762:66;-1:-1:-1;99656:3:0;;99616:224;;;-1:-1:-1;99859:9:0;-1:-1:-1;99443:433:0;:::o;102660:167::-;102725:18;102776:43;102811:7;102776:34;:43::i;:::-;-1:-1:-1;102756:63:0;102660:167;-1:-1:-1;;102660:167:0:o;83656:970::-;7165:16;:14;:16::i;:::-;111577:18;111615:14;:12;:14::i;:::-;111598:13;;-1:-1:-1;;;;;111598:13:0;;;:31;;;;-1:-1:-1;111598:31:0;111640:43;;;;-1:-1:-1;;;111640:43:0;;;;;;;;;83771:1;83746:22;:20;:22::i;:::-;:26;83738:66;;;;-1:-1:-1;;;83738:66:0;;;;;;;;;83885:22;:20;:22::i;:::-;83879:3;:28;83837:27;83862:1;83837:24;:27::i;:::-;:37;-1:-1:-1;;;83837:37:0;;;;:71;;83815:162;;;;-1:-1:-1;;;83815:162:0;;;;;;;;;83998:26;;;;:35;83990:106;;;;-1:-1:-1;;;83990:106:0;;;;;;;;;84107:26;:33;;-1:-1:-1;;84107:33:0;84136:4;84107:33;;;:26;84181:34;:32;:34::i;:::-;84153:62;;84256:1;84232:20;:25;84228:106;;-1:-1:-1;84300:1:0;84274:23;:27;84316:7;;84228:106;84346:8;:6;:8::i;:::-;-1:-1:-1;;;;;84346:15:0;;84362:4;;84346:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;84346:21:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;84346:21:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;84346:21:0;;;;;;;;;-1:-1:-1;;;;;84346:26:0;;75570:42;84386:20;84346:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;84346:61:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;84420:23:0;:46;;;-1:-1:-1;84524:94:0;84446:20;84524:27;84549:1;84524:24;:27::i;:::-;:44;;;;:94;:48;:94;:::i;:::-;84477:27;84502:1;84477:24;:27::i;:::-;:44;;:141;-1:-1:-1;111694:1:0;7192;83656:970::o;76649:38::-;;;;:::o;91273:283::-;91371:4;111885:14;:12;:14::i;:::-;-1:-1:-1;;;;;111885:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;111885:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;111885:38:0;;;;7165:16;:14;:16::i;:::-;91396:19;:17;:19::i;:::-;91448:13;;;91396:66;;;;;-1:-1:-1;;;;;91396:31:0;;;;;;:66;;91428:18;;91448:13;;;91396:66;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;91396:66:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;91396:66:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;91396:66:0;;;;;;;;;91388:110;;;;-1:-1:-1;;;91388:110:0;;;;;;;;;91518:30;91529:18;91518:10;:30::i;82540:118::-;82624:26;;;;82540:118;:::o;84634:1043::-;7165:16;:14;:16::i;:::-;111577:18;111615:14;:12;:14::i;:::-;111598:13;;-1:-1:-1;;;;;111598:13:0;;;:31;;;;-1:-1:-1;111598:31:0;111640:43;;;;-1:-1:-1;;;111640:43:0;;;;;;;;;84758:1;84733:22;:20;:22::i;:::-;:26;84725:66;;;;-1:-1:-1;;;84725:66:0;;;;;;;;;84872:22;:20;:22::i;:::-;84866:3;:28;84824:27;84849:1;84824:24;:27::i;:::-;:37;-1:-1:-1;;;84824:37:0;;;;:71;;84802:160;;;;-1:-1:-1;;;84802:160:0;;;;;;;;;84983:24;;;;;;;:33;84975:102;;;;-1:-1:-1;;;84975:102:0;;;;;;;;;85088:24;:31;;-1:-1:-1;;85088:31:0;;;;;85136:11;85132:24;;85149:7;;85132:24;85168:38;85222:192;:148;85319:19;:17;:19::i;:::-;-1:-1:-1;;;;;85319:48:0;;:50;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;85319:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;85319:50:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;85319:50:0;;;;;;;;;85222;:6;:48;:50::i;:::-;:96;:148;:96;:148;:::i;:::-;:190;:192::i;:::-;85168:246;;85427:8;:6;:8::i;:::-;-1:-1:-1;;;;;85427:15:0;;85443:4;;85427:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;85427:21:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;85427:21:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;85427:21:0;;;;;;;;;-1:-1:-1;;;;;85427:27:0;;75570:42;85468:33;85427:75;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;85427:75:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;85427:75:0;;;;85562:107;85625:33;85562:27;85587:1;85562:24;:27::i;:107::-;85515:27;85540:1;85515:24;:27::i;:::-;:44;;:154;-1:-1:-1;7192:1:0;84634:1043;:::o;99981:463::-;100037:4;;100177:1;100163:242;76448:1;100180:21;;100163:242;;;100238:65;100255:27;100280:1;100255:24;:27::i;:::-;:47;;;100238:12;;:65;:16;:65;:::i;:::-;100223:80;;100333:60;100350:27;100375:1;100350:24;:27::i;:::-;:42;;;100333:12;;:60;:16;:60;:::i;:::-;100318:75;-1:-1:-1;100203:3:0;;100163:242;;16726:657;16768:34;16805:27;:25;:27::i;:::-;16768:64;-1:-1:-1;16926:6:0;16921:455;16942:17;:24;16938:1;:28;16921:455;;;16988:12;17003:17;17021:1;17003:20;;;;;;;;;;;;;;16988:35;;17131:19;17170:8;;;;;;;;;-1:-1:-1;;;;;17170:8:0;-1:-1:-1;;;;;17170:29:0;;17200:4;17259;17213:51;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;17213:51:0;;;17170:96;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;17170:96:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;17170:96:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;17170:96:0;;;;;;;;;17281:18;;;;:12;:18;;;;;;;:32;;-1:-1:-1;;;;;;17281:32:0;-1:-1:-1;;;;;17281:32:0;;;;;17333:31;17281:32;;-1:-1:-1;17333:31:0;;;;17281:18;;:32;;17333:31;;;;;;;;;;-1:-1:-1;;16968:3:0;;16921:455;;2417:271;2486:14;;-1:-1:-1;;;;;2486:14:0;2472:10;:28;2464:94;;;;-1:-1:-1;;;2464:94:0;;;;;;;;;2587:5;;;2594:14;2574:35;;;;;;-1:-1:-1;;;;;2587:5:0;;;;2594:14;;;2574:35;;;;;;;;;;2628:14;;;;2620:22;;-1:-1:-1;;;;;;2620:22:0;;;-1:-1:-1;;;;;2628:14:0;;2620:22;;;;2653:27;;;2417:271::o;86239:493::-;111302:13;111340:8;:6;:8::i;:::-;-1:-1:-1;;;;;111318:31:0;:10;-1:-1:-1;;;;;111318:31:0;;111302:47;;111360:23;111408:18;:16;:18::i;:::-;-1:-1:-1;;;;;111386:41:0;:10;-1:-1:-1;;;;;111386:41:0;;111360:67;;111446:8;:30;;;;111458:18;111446:30;111438:75;;;;-1:-1:-1;;;111438:75:0;;;;;;;;;86414:14;:12;:14::i;:::-;-1:-1:-1;;;;;86414:42:0;;86471:7;86493:9;86517:14;86546:27;86571:1;86546:24;:27::i;:::-;:45;86414:188;;;;;;;;;;;;;;;86546:45;;;;;;86414:188;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;86414:188:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;86414:188:0;;;;86615:109;86642:7;86651:9;86662:14;86678:27;86703:1;86678:24;:27::i;:::-;:45;;;;;;86615:26;:109::i;:::-;86239:493;;;;;:::o;87763:184::-;2726:12;:10;:12::i;:::-;87846:15;:20;:22;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;87846:22:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;87846:22:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;87846:22:0;;;;;;;;;87839:4;:29;87831:74;;;;-1:-1:-1;;;87831:74:0;;;;;;;;;87918:14;:21;87763:184::o;78326:1006::-;78384:26;78423:34;78460:47;:45;:47::i;:::-;78550:17;;;78564:2;78550:17;;;;;;;;;78423:84;;-1:-1:-1;78518:29:0;;78550:17;;;;17:15:-1;;105:10;78550:17:0;88:34:-1;136:17;;-1:-1;78550:17:0;78518:49;;78596:21;78578:12;78591:1;78578:15;;;;;;;;;;;;;:39;;;;;78646:20;78628:12;78641:1;78628:15;;;;;;;;;;;;;:38;;;;;78695:21;78677:12;78690:1;78677:15;;;;;;;;;;;;;:39;;;;;78745:30;78727:12;78740:1;78727:15;;;;;;;;;;;;;:48;;;;;78804:18;78786:12;78799:1;78786:15;;;;;;;;;;;;;:36;;;;;-1:-1:-1;;;78833:12:0;78846:1;78833:15;;;;;;;;;;;;;:33;;;;;78895:25;78877:12;78890:1;78877:15;;;;;;;;;;;;;:43;;;;;78949:24;78931:12;78944:1;78931:15;;;;;;;;;;;;;:42;;;;;79002:26;78984:12;78997:1;78984:15;;;;;;;;;;;;;:44;;;;;79057:28;79039:12;79052:1;79039:15;;;;;;;;;;;;;:46;;;;;79115:28;79096:12;79109:2;79096:16;;;;;;;;;;;;;:47;;;;;79173:26;79154:12;79167:2;79154:16;;;;;;;;;;;;;:45;;;;;79229:26;79210:12;79223:2;79210:16;;;;;;;;;;;;;:45;;;;;79278:46;79292:17;79311:12;79278:13;:46::i;:::-;79266:58;;78326:1006;;;:::o;2010:20::-;;;-1:-1:-1;;;;;2010:20:0;;:::o;6488:137::-;2726:12;:10;:12::i;:::-;6560:5;:21;;-1:-1:-1;;;;;;6560:21:0;-1:-1:-1;;;;;6560:21:0;;;;;6597:20;;;;;;6560:21;;6597:20;;5911:29;;;-1:-1:-1;;;;;5911:29:0;;:::o;109925:173::-;109982:4;110006:84;110041:48;110068:20;:18;:20::i;:::-;110041:15;:20;:22;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110041:22:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;110041:22:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;110041:22:0;;;;;;;;:48;110006:18;:16;:18::i;:::-;:34;:84;:34;:84;:::i;93375:884::-;7470:26;:24;:26::i;:::-;8337:15;;8331:3;:21;8323:75;;;;-1:-1:-1;;;8323:75:0;;;;;;;;;93733:18;:16;:18::i;:::-;-1:-1:-1;;;;;93733:35:0;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;93733:37:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;93733:37:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;93733:37:0;;;;;;;;;93712:17;:58;;93704:93;;;;-1:-1:-1;;;93704:93:0;;;;;;;;;93892:359;;;;;;;;93937:11;93892:359;;;;;;93990:17;93892:359;;;;;;94041:9;93892:359;;;;;;94084:16;93892:359;;;;94128:11;93892:359;;;;94175:19;93892:359;;;;94225:14;93892:359;;;93810:17;93828:60;76448:1;93828:60;;:37;93850:14;93828:17;;:21;;:37;;;;:::i;:60::-;93810:79;;;;;;;:441;;:79;;;;;;;;;:441;;;;;;;;;;-1:-1:-1;;93810:441:0;;;;;;;;-1:-1:-1;;93810:441:0;;;;;;;;;;;;-1:-1:-1;;;93810:441:0;;;;;;;;;;;;;;;;;;-1:-1:-1;93810:441:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;93375:884:0:o;81475:98::-;81523:4;81547:18;:16;:18::i;6786:102::-;6926:12;:10;:12::i;:::-;6858:13;:22;;-1:-1:-1;;;;;;6858:22:0;-1:-1:-1;;;;;6858:22:0;;;;;;;;;;6786:102::o;76406:43::-;76448:1;76406:43;:::o;90832:125::-;90900:4;111885:14;:12;:14::i;:::-;-1:-1:-1;;;;;111885:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;111885:38:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;111885:38:0;;;;7165:16;:14;:16::i;:::-;90935:13;;90924:25;;-1:-1:-1;;;;;90935:13:0;90924:10;:25::i;6180:28::-;;;-1:-1:-1;;;;;6180:28:0;;:::o;81695:102::-;81745:4;81769:20;:18;:20::i;75532:80::-;75570:42;75532:80;:::o;5886:18::-;;;-1:-1:-1;;;;;5886:18:0;;:::o;87288:467::-;87353:24;87388:21;:19;:21::i;:::-;87429:13;;87353:57;;-1:-1:-1;;;;;;87429:33:0;;;:13;;:33;;:67;;-1:-1:-1;87466:10:0;-1:-1:-1;;;;;87466:30:0;;;87429:67;87421:110;;;;-1:-1:-1;;;87421:110:0;;;;;;;;;87688:59;87740:6;87688:27;87713:1;87688:24;:27::i;:::-;:47;;;;:59;:51;:59;:::i;:::-;87638:27;87663:1;87638:24;:27::i;:::-;:47;;:109;-1:-1:-1;;87288:467:0:o;79796:180::-;79852:21;79915:52;79936:30;79915:20;:52::i;82928:180::-;82997:17;83034;76448:1;83052:47;;83073:5;83053:17;;:25;83052:47;;;;;;83034:66;;;;;;;;;;;82928:180;-1:-1:-1;;82928:180:0:o;31262:184::-;31320:7;31353:1;31348;:6;;31340:49;;;;-1:-1:-1;;;31340:49:0;;;;;;;;;-1:-1:-1;31412:5:0;;;31262:184::o;79644:144::-;79691:12;79736:43;79757:21;79736:20;:43::i;107724:689::-;107889:4;108032:35;108070:18;:16;:18::i;:::-;108032:56;;108099:27;108142:221;108343:19;108142:154;108251:17;-1:-1:-1;;;;;108251:28:0;;108280:14;108251:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108251:44:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;108251:44:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;108251:44:0;;;;;;;;;108142:64;;;;;-1:-1:-1;;;;;108142:46:0;;;;;:64;;108189:16;;108142:64;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;108142:64:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;108142:64:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;108142:64:0;;;;;;;;;:108;:154;:108;:154;:::i;:221::-;108099:264;-1:-1:-1;;;107724:689:0;;;;;;:::o;30806:181::-;30864:7;30896:5;;;30920:6;;;;30912:46;;;;-1:-1:-1;;;30912:46:0;;;;;;;;2766:133;2834:5;;-1:-1:-1;;;;;2834:5:0;2820:10;:19;2812:79;;;;-1:-1:-1;;;2812:79:0;;;;;;;;;2766:133::o;25899:266::-;25954:4;26075:17;:15;:17::i;:::-;-1:-1:-1;;;;;26075:30:0;;-1:-1:-1;;;26129:27:0;26075:82;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;26075:82:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;26075:82:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;26075:82:0;;;;;;;;79984:134;80028:10;80069:40;80090:18;80069:20;:40::i;80656:122::-;80697:7;80732:37;-1:-1:-1;;;80732:20:0;:37::i;80126:174::-;80180:20;80241:50;80262:28;80241:20;:50::i;80308:166::-;80360:18;80417:48;80438:26;80417:20;:48::i;83116:478::-;83183:34;83230:23;83256:85;83290:19;:17;:19::i;:::-;-1:-1:-1;;;;;83290:48:0;;:50;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83290:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;83290:50:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;83290:50:0;;;;;;;;;83256:15;:27;:29;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;83256:29:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;83256:29:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;83256:29:0;;;;;;;;:85;83230:111;;83386:200;:160;83527:18;83386:98;:27;83411:1;83386:24;:27::i;:::-;:58;;;:96;:98::i;:200::-;83354:232;;83116:478;;:::o;106495:1221::-;106647:4;;106755:24;106751:43;;-1:-1:-1;106789:1:0;;-1:-1:-1;106789:1:0;106781:13;;106751:43;106837:19;106957:10;;106953:260;;106984:21;107008:70;107076:1;107016:36;107050:1;107041:6;:10;107016:24;:36::i;107008:70::-;106984:94;;107118:83;107147:16;107165:19;107186:14;107118:28;:83::i;:::-;107093:108;;106953:260;;107347:19;107369:89;107435:22;107369:32;107394:6;107369:24;:32::i;:::-;:49;;;;:89;:65;:89;:::i;:::-;107347:111;;107471:22;107509:92;107578:22;107509:32;107534:6;107509:24;:32::i;:::-;:52;;;;:92;:68;:92;:::i;:::-;107471:130;;107622:40;:14;:38;:40::i;:::-;107664:43;:17;:41;:43::i;:::-;107614:94;;;;;;;106495:1221;;;;;;;:::o;79340:146::-;79387:13;79434:43;79455:21;79434:20;:43::i;33425:152::-;33483:7;33511:6;33503:43;;;;-1:-1:-1;;;33503:43:0;;;;;;;;;33568:1;33564;:5;;;;;;;33425:152;-1:-1:-1;;;33425:152:0:o;80786:162::-;80837:17;80892:47;80913:25;80892:20;:47::i;112893:144::-;112960:5;;112972:23;;-1:-1:-1;;;;;112960:5:0;;;;:11;;112972:23;;112983:11;;112972:23;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;112972:23:0;;;112997:1;112847:37;;;;;;;;;;;;;;;112960:69;;;;;;;;;;;;113021:1;;;;;;112960:69;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;112960:69:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;101297:1355:0;101381:4;101387;101478:30;101511:8;:6;:8::i;:::-;-1:-1:-1;;;;;101511:27:0;;101539:7;101548:1;101551;101554:4;101511:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;101511:48:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;101511:48:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;101511:48:0;;;;;;;;;101478:81;;101616:83;101656:42;101683:14;;101656:15;:20;:22;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;101656:42:0;101616:23;:21;:23::i;:83::-;101588:25;:111;101570:189;;;101734:5;101741;101726:21;;;;;;;101570:189;101939:10;101951:21;101976:8;:6;:8::i;:::-;-1:-1:-1;;;;;101976:49:0;;102026:7;101976:58;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;101976:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;101976:58:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;101976:58:0;;;;;;;;;101938:96;;;;102045:16;102064:18;:16;:18::i;:::-;102045:37;;102168:11;102160:5;:19;102156:83;;;-1:-1:-1;102204:4:0;;-1:-1:-1;102210:16:0;-1:-1:-1;102196:31:0;;-1:-1:-1;;102196:31:0;102156:83;102338:20;102361:77;102389:48;102416:20;:18;:20::i;102389:48::-;102361:11;;:77;:27;:77;:::i;:::-;102338:100;;102525:15;102517:5;:23;102513:88;;;102565:5;102572:16;102557:32;;;;;;;;;;;102513:88;-1:-1:-1;102621:4:0;;-1:-1:-1;102627:16:0;;-1:-1:-1;;;;101297:1355:0;;;:::o;7209:209::-;7279:5;;-1:-1:-1;;;;;7279:5:0;7264:10;7258:26;;;;:67;;-1:-1:-1;7309:16:0;;-1:-1:-1;;;;;7309:16:0;7294:10;7288:37;;7258:67;:98;;;;-1:-1:-1;7329:13:0;;-1:-1:-1;;;;;7329:13:0;7346:10;7329:27;;7258:98;7254:157;;;7373:13;:26;;-1:-1:-1;;;;;;7373:26:0;7389:10;7373:26;;;7209:209::o;110615:115::-;110662:7;110689:19;:17;:19::i;:::-;-1:-1:-1;;;;;110689:31:0;;:33;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110689:33:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;110689:33:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;110689:33:0;;;;;;;;81119:166;81171:18;81228:48;81249:26;81228:20;:48::i;91564:1698::-;91627:4;;;;;;;91968:51;92003:15;91968:34;:51::i;:::-;91922:97;;;;92040:13;92032:57;;;;-1:-1:-1;;;92032:57:0;;;;;;;;;92111:16;92110:17;92102:61;;;;-1:-1:-1;;;92102:61:0;;;;;;;;;92275:30;92289:15;92275:13;:30::i;:::-;92239:66;;-1:-1:-1;92239:66:0;-1:-1:-1;92340:17:0;;;;:41;;;92380:1;92361:16;:20;92340:41;92318:155;;;;-1:-1:-1;;;92318:155:0;;;;;;;;;92545:79;92567:15;92584:27;92609:1;92584:24;:27::i;:::-;:39;;;92545:21;:79::i;:::-;92641:17;;92637:245;;92749:32;92767:13;92749:17;:32::i;:::-;92738:43;;92835:35;92844:15;92861:8;92835;:35::i;:::-;92898:20;;92894:269;;93015:38;93036:16;93015:20;:38::i;:::-;93001:52;;93110:41;93122:15;93139:11;93110;:41::i;:::-;93175:55;93191:15;93208:8;93218:11;93175:15;:55::i;:::-;-1:-1:-1;93250:4:0;;91564:1698;-1:-1:-1;;;;;;;91564:1698:0:o;80482:166::-;80534:18;80591:48;80612:26;80591:20;:48::i;40232:143::-;40296:4;40320:47;:1;34201:42;40320:47;:5;:47;:::i;36815:149::-;36891:4;36915:41;36937:1;36940;34098:31;36915:21;:41::i;40485:298::-;40549:4;40595:45;40590:51;;40683:1;40677:2;40658:21;;:26;40654:81;;40721:2;40701:22;40654:81;40773:2;40754:21;;;;-1:-1:-1;;40485:298:0:o;112312:434::-;112498:5;;112524:65;;-1:-1:-1;;;;;112498:5:0;;;;:11;;112524:65;;112535:9;;112546:14;;112562:26;;112524:65;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;112524:65:0;;;112604:1;112235:68;;;;;;;;;;;;;;;112498:240;;;;;;;;;;;;-1:-1:-1;;;;;112669:25:0;;;112661:34;;;;112498:240;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;112498:240:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;112498:240:0;;;;112312:434;;;;:::o;23751:183::-;23860:16;;;23874:1;23860:16;;;;;;;;;23809:26;;23860:16;;;;;;105:10:-1;23860:16:0;88:34:-1;136:17;;-1:-1;23860:16:0;23848:28;;23902:24;23887:9;23897:1;23887:12;;;;;;;;;;;;;:39;;;;;23751:183;:::o;15992:458::-;16114:28;16203:6;:13;16188:5;:12;:28;16174:43;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;16174:43:0;-1:-1:-1;16160:57:0;-1:-1:-1;16235:6:0;16230:92;16251:5;:12;16247:1;:16;16230:92;;;16302:5;16308:1;16302:8;;;;;;;;;;;;;;16285:11;16297:1;16285:14;;;;;;;;;;;;;;;;;:25;16265:3;;16230:92;;;-1:-1:-1;16339:6:0;16334:109;16355:6;:13;16351:1;:17;16334:109;;;16422:6;16429:1;16422:9;;;;;;;;;;;;;;16390:11;16417:1;16402:5;:12;:16;16390:29;;;;;;;;;;;;;;;;;:41;16370:3;;16334:109;;26173:261;26226:4;26347:17;:15;:17::i;:::-;-1:-1:-1;;;;;26347:30:0;;-1:-1:-1;;;26401:24:0;26347:79;;;;;;;;;;;;;;;;;25634:257;25685:4;25806:17;:15;:17::i;:::-;-1:-1:-1;;;;;25806:30:0;;-1:-1:-1;;;25860:22:0;25806:77;;;;;;;;;;;;;;;;;34991:195;35055:4;33967:18;35163:8;:1;35169;35163:8;:5;:8;:::i;:::-;:15;;;;;;;34991:195;-1:-1:-1;;;34991:195:0:o;7578:284::-;7658:5;;-1:-1:-1;;;;;7658:5:0;7643:10;7637:26;;;;:67;;-1:-1:-1;7688:16:0;;-1:-1:-1;;;;;7688:16:0;7673:10;7667:37;;7637:67;:98;;;;-1:-1:-1;7708:13:0;;-1:-1:-1;;;;;7708:13:0;7725:10;7708:27;;7637:98;7633:157;;;7752:13;:26;;-1:-1:-1;;;;;;7752:26:0;7768:10;7752:26;;;7633:157;7825:5;;7808:13;;-1:-1:-1;;;;;7808:13:0;;;7825:5;;7808:22;7800:54;;;;-1:-1:-1;;;7800:54:0;;;;;;;;6966:157;7041:5;;-1:-1:-1;;;;;7041:5:0;7026:10;7020:26;;:67;;-1:-1:-1;7071:16:0;;-1:-1:-1;;;;;7071:16:0;7056:10;7050:37;7020:67;7012:103;;;;-1:-1:-1;;;7012:103:0;;;;;;;;81293:174;81347:20;81408:50;81429:28;18031:268;18098:7;18142:18;;;:12;:18;;;;;;;;;18215:43;;-1:-1:-1;;;;;18142:18:0;;;;18179:27;;;;18215:43;;18155:4;;18215:43;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;18215:43:0;;;18171:89;;;;;-1:-1:-1;;;18171:89:0;;;;;;;;;39981:145;40055:4;40079:39;40099:1;40102;34098:31;40079:19;:39::i;23942:158::-;23992:16;24045:46;24066:24;24045:20;:46::i;27991:171::-;28047:4;28071:17;:15;:17::i;:::-;-1:-1:-1;;;;;28071:30:0;;-1:-1:-1;;;28125:28:0;28071:83;;;;;;;;;;;;;;;;;110348:259;110444:23;:21;:23::i;:::-;-1:-1:-1;;;;;110444:36:0;;110522:19;110543:16;110505:55;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;110505:55:0;;;110495:66;;;;;;110576:12;110444:155;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;110444:155:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;110444:155:0;;;;110348:259;;:::o;94410:1669::-;94472:4;94558:10;94472:4;94864:1;94830:1214;76448:1;94867:21;;94830:1214;;;94910:23;94936:27;94961:1;94936:24;:27::i;:::-;:39;;;94910:65;;94990:10;95003:68;95052:18;95003:27;95028:1;95003:24;:27::i;:68::-;94990:81;-1:-1:-1;95092:9:0;;95088:945;;95236:19;95266;95258:5;:27;:57;;95296:19;95258:57;;;95288:5;95258:57;95236:79;-1:-1:-1;95378:38:0;:18;95236:79;95378:38;:22;:38;:::i;:::-;95336:27;95361:1;95336:24;:27::i;:::-;:39;;:80;95457:39;:19;95481:14;95457:39;:23;:39;:::i;:::-;95435:61;-1:-1:-1;95526:28:0;:8;95539:14;95526:28;:12;:28;:::i;:::-;95515:39;-1:-1:-1;95665:24:0;95661:45;;95698:8;95691:15;;;;;;;;;;95661:45;95917:6;;:33;;;;;95949:1;95927:19;:23;95917:33;95913:105;;;95997:1;95975:23;;95913:105;95088:945;;-1:-1:-1;;;;94890:3:0;94830:1214;;;-1:-1:-1;96063:8:0;94410:1669;-1:-1:-1;;;94410:1669:0:o;98180:555::-;98255:7;-1:-1:-1;;;;;111770:22:0;;75570:42;111770:22;;111762:58;;;;-1:-1:-1;;;111762:58:0;;;;;;;;;98307:16;98326:8;:6;:8::i;:::-;-1:-1:-1;;;;;98326:15:0;;98342:4;;98326:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;98326:21:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;98326:21:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;98326:21:0;;;;;;;;;98605:39;;;;;98307:40;;-1:-1:-1;;;;;;98605:14:0;;;;;:39;;75570:42;;98633:10;;98605:39;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;98605:39:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;98691:36:0;;;;;-1:-1:-1;;;;;98691:15:0;;;-1:-1:-1;98691:15:0;;-1:-1:-1;98691:36:0;;98707:7;;98716:10;;98691:36;;;;96225:1761;96290:4;96376:10;96290:4;96686:1;96652:1299;76448:1;96689:21;;96652:1299;;;96732:17;96769:95;96821:27;96846:1;96821:24;:27::i;:::-;:42;;;96769:27;96794:1;96769:24;:27::i;:::-;:47;;;;:95;:51;:95;:::i;:::-;96732:132;-1:-1:-1;96885:16:0;;96881:1059;;97036:19;97073;97058:12;:34;:71;;97110:19;97058:71;;;97095:12;97058:71;97036:93;;97195:62;97242:14;97195:27;97220:1;97195:24;:27::i;:::-;:42;;;;:62;:46;:62;:::i;:::-;97150:27;97175:1;97150:24;:27::i;:::-;:42;;:107;97298:39;:19;97322:14;97298:39;:23;:39;:::i;:::-;97276:61;-1:-1:-1;97369:30:0;:10;97384:14;97369:30;:14;:30;:::i;:::-;97356:43;-1:-1:-1;97510:24:0;97506:47;;97543:10;97536:17;;;;;;;;;97506:47;97824:6;;:33;;;;;97856:1;97834:19;:23;97824:33;97820:105;;;97904:1;97882:23;;97820:105;96881:1059;;-1:-1:-1;;;96712:3:0;96652:1299;;98950:387;99028:7;-1:-1:-1;;;;;111770:22:0;;75570:42;111770:22;;111762:58;;;;-1:-1:-1;;;111762:58:0;;;;;;;;;99115:8;99257:16;:14;:16::i;:::-;-1:-1:-1;;;;;99257:35:0;;99293:7;99302:10;99314:14;99257:72;;;;;;;;;;;;;;;;;;113222:225;113353:5;;113365:44;;-1:-1:-1;;;;;113353:5:0;;;;:11;;113365:44;;113376:7;;113385:10;;113397:11;;113365:44;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;113365:44:0;;;113411:1;113164:49;;;;;;;;;;;;;;;113353:86;;;;;;;;;;;;113431:1;;;;;;113353:86;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;113353:86:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;113353:86:0;;;;113222:225;;;:::o;31697:470::-;31755:7;31999:6;31995:47;;-1:-1:-1;32029:1:0;32022:8;;31995:47;32066:5;;;32070:1;32066;:5;:1;32090:5;;;;;:10;32082:56;;;;-1:-1:-1;;;32082:56:0;;;;;;;;35805:421;35928:4;;36081:2;36065:13;:18;36053:8;:1;36059;36053:8;:5;:8;:::i;:::-;:31;;;;;;;-1:-1:-1;36126:1:0;36120:2;36053:31;36101:21;;:26;36097:81;;36164:2;36144:22;36097:81;36216:2;36197:21;;;;-1:-1:-1;;;;35805:421:0:o;38745:328::-;38866:4;;38905:32;38935:1;38905:25;:1;38927:2;38911:18;;38905:25;:5;:25;:::i;:::-;:29;:32;:29;:32;:::i;:::-;38883:54;-1:-1:-1;38977:1:0;38971:2;38883:54;38954:19;;80956:155;81005:15;81056:46;81077:24;81056:20;:46::i;32635:333::-;32693:7;32792:1;32788;:5;32780:44;;;;-1:-1:-1;;;32780:44:0;;;;;;;;;32835:9;32851:1;32847;:5;;;;;;;32635:333;-1:-1:-1;;;;32635:333:0:o;75335:38115::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;-1:-1;75335:38115:0;;;-1:-1:-1;;75335:38115:0:o;5:130:-1:-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;436:128;511:13;;529:30;511:13;529:30;;571:134;649:13;;667:33;649:13;667:33;;712:162;804:13;;822:47;804:13;822:47;;881:130;948:20;;973:33;948:20;973:33;;1159:241;;1263:2;1251:9;1242:7;1238:23;1234:32;1231:2;;;1279:1;1276;1269:12;1231:2;1314:1;1331:53;1376:7;1356:9;1331:53;;;1321:63;1225:175;-1:-1;;;;1225:175;1407:263;;1522:2;1510:9;1501:7;1497:23;1493:32;1490:2;;;1538:1;1535;1528:12;1490:2;1573:1;1590:64;1646:7;1626:9;1590:64;;1941:366;;;2062:2;2050:9;2041:7;2037:23;2033:32;2030:2;;;2078:1;2075;2068:12;2030:2;2113:1;2130:53;2175:7;2155:9;2130:53;;;2120:63;;2092:97;2220:2;2238:53;2283:7;2274:6;2263:9;2259:22;2238:53;;;2228:63;;2199:98;2024:283;;;;;;2314:491;;;;2452:2;2440:9;2431:7;2427:23;2423:32;2420:2;;;2468:1;2465;2458:12;2420:2;2503:1;2520:53;2565:7;2545:9;2520:53;;;2510:63;;2482:97;2610:2;2628:53;2673:7;2664:6;2653:9;2649:22;2628:53;;;2618:63;;2589:98;2718:2;2736:53;2781:7;2772:6;2761:9;2757:22;2736:53;;;2726:63;;2697:98;2414:391;;;;;;2812:257;;2924:2;2912:9;2903:7;2899:23;2895:32;2892:2;;;2940:1;2937;2930:12;2892:2;2975:1;2992:61;3045:7;3025:9;2992:61;;3076:263;;3191:2;3179:9;3170:7;3166:23;3162:32;3159:2;;;3207:1;3204;3197:12;3159:2;3242:1;3259:64;3315:7;3295:9;3259:64;;3346:291;;3475:2;3463:9;3454:7;3450:23;3446:32;3443:2;;;3491:1;3488;3481:12;3443:2;3526:1;3543:78;3613:7;3593:9;3543:78;;3644:241;;3748:2;3736:9;3727:7;3723:23;3719:32;3716:2;;;3764:1;3761;3754:12;3716:2;3799:1;3816:53;3861:7;3841:9;3816:53;;4162:393;;;4291:2;4279:9;4270:7;4266:23;4262:32;4259:2;;;4307:1;4304;4297:12;4259:2;4342:1;4359:64;4415:7;4395:9;4359:64;;;4349:74;;4321:108;4460:2;4478:61;4531:7;4522:6;4511:9;4507:22;4478:61;;4562:399;;;4694:2;4682:9;4673:7;4669:23;4665:32;4662:2;;;4710:1;4707;4700:12;4662:2;4745:1;4762:64;4818:7;4798:9;4762:64;;;4752:74;;4724:108;4863:2;4881:64;4937:7;4928:6;4917:9;4913:22;4881:64;;4968:1121;;;;;;;;;5191:3;5179:9;5170:7;5166:23;5162:33;5159:2;;;5208:1;5205;5198:12;5159:2;5243:1;5260:53;5305:7;5285:9;5260:53;;;5250:63;;5222:97;5350:2;5368:53;5413:7;5404:6;5393:9;5389:22;5368:53;;;5358:63;;5329:98;5458:2;5476:53;5521:7;5512:6;5501:9;5497:22;5476:53;;;5466:63;;5437:98;5566:2;5584:53;5629:7;5620:6;5609:9;5605:22;5584:53;;;5574:63;;5545:98;5674:3;5693:53;5738:7;5729:6;5718:9;5714:22;5693:53;;;5683:63;;5653:99;5783:3;5802:53;5847:7;5838:6;5827:9;5823:22;5802:53;;;5792:63;;5762:99;5892:3;5911:53;5956:7;5947:6;5936:9;5932:22;5911:53;;;5901:63;;5871:99;6001:3;6020:53;6065:7;6056:6;6045:9;6041:22;6020:53;;;6010:63;;5980:99;5153:936;;;;;;;;;;;;6097:253;;6222:88;6306:3;6298:6;6222:88;;;-1:-1;;6339:4;6330:14;;6215:135;6359:173;;6446:46;6488:3;6480:6;6446:46;;;-1:-1;;6521:4;6512:14;;6439:93;6722:142;6813:45;6852:5;6813:45;;;6808:3;6801:58;6795:69;;;6871:113;6954:24;6972:5;6954:24;;6991:152;7092:45;7112:24;7130:5;7112:24;;;7092:45;;7189:812;7360:71;7425:5;7360:71;;;7444:103;7540:6;7535:3;7444:103;;;7437:110;;7568:73;7635:5;7568:73;;;7661:7;7689:1;7674:315;7699:6;7696:1;7693:13;7674:315;;;7766:6;7760:13;7787:101;7884:3;7869:13;7787:101;;;7780:108;;7905:77;7975:6;7905:77;;;7895:87;-1:-1;;7721:1;7714:9;7674:315;;8040:690;;8185:54;8233:5;8185:54;;;8252:86;8331:6;8326:3;8252:86;;;8245:93;;8359:56;8409:5;8359:56;;;8435:7;8463:1;8448:260;8473:6;8470:1;8467:13;8448:260;;;8540:6;8534:13;8561:63;8620:3;8605:13;8561:63;;;8554:70;;8641:60;8694:6;8641:60;;;8631:70;-1:-1;;8495:1;8488:9;8448:260;;;-1:-1;8721:3;;8164:566;-1:-1;;;;;8164:566;8771:624;8890:48;8932:5;8890:48;;;8951:74;9018:6;9013:3;8951:74;;;8944:81;;9046:50;9090:5;9046:50;;;9116:7;9144:1;9129:254;9154:6;9151:1;9148:13;9129:254;;;9221:6;9215:13;9242:63;9301:3;9286:13;9242:63;;;9235:70;;9322:54;9369:6;9322:54;;;9312:64;-1:-1;;9176:1;9169:9;9129:254;;9403:104;9480:21;9495:5;9480:21;;9514:103;9587:24;9605:5;9587:24;;9744:152;9845:45;9865:24;9883:5;9865:24;;;9845:45;;9903:343;;10013:38;10045:5;10013:38;;;10063:70;10126:6;10121:3;10063:70;;;10056:77;;10138:52;10183:6;10178:3;10171:4;10164:5;10160:16;10138:52;;;10211:29;10233:6;10211:29;;;10202:39;;;;9993:253;-1:-1;;;9993:253;10253:172;10359:60;10413:5;10359:60;;10607:142;10698:45;10737:5;10698:45;;10756:142;10847:45;10886:5;10847:45;;11558:332;;11718:67;11782:2;11777:3;11718:67;;;11818:34;11798:55;;11881:2;11872:12;;11704:186;-1:-1;;11704:186;11899:323;;12059:67;12123:2;12118:3;12059:67;;;12159:25;12139:46;;12213:2;12204:12;;12045:177;-1:-1;;12045:177;12231:390;;12391:67;12455:2;12450:3;12391:67;;;12491:34;12471:55;;12560:23;12555:2;12546:12;;12539:45;12612:2;12603:12;;12377:244;-1:-1;;12377:244;12630:378;;12790:67;12854:2;12849:3;12790:67;;;12890:34;12870:55;;12959:11;12954:2;12945:12;;12938:33;12999:2;12990:12;;12776:232;-1:-1;;12776:232;13017:329;;13177:67;13241:2;13236:3;13177:67;;;13277:31;13257:52;;13337:2;13328:12;;13163:183;-1:-1;;13163:183;13355:319;;13515:67;13579:2;13574:3;13515:67;;;13615:21;13595:42;;13665:2;13656:12;;13501:173;-1:-1;;13501:173;13683:409;;13861:85;13943:2;13938:3;13861:85;;;13979:34;13959:55;;14048:6;14043:2;14034:12;;14027:28;14083:2;14074:12;;13847:245;-1:-1;;13847:245;14101:393;;14261:67;14325:2;14320:3;14261:67;;;14361:34;14341:55;;14430:26;14425:2;14416:12;;14409:48;14485:2;14476:12;;14247:247;-1:-1;;14247:247;14503:376;;14663:67;14727:2;14722:3;14663:67;;;14763:34;14743:55;;14832:9;14827:2;14818:12;;14811:31;14870:2;14861:12;;14649:230;-1:-1;;14649:230;14888:428;;15066:85;15148:2;15143:3;15066:85;;;15184:34;15164:55;;15253:25;15248:2;15239:12;;15232:47;15307:2;15298:12;;15052:264;-1:-1;;15052:264;15325:327;;15485:67;15549:2;15544:3;15485:67;;;15585:29;15565:50;;15643:2;15634:12;;15471:181;-1:-1;;15471:181;15661:330;;15821:67;15885:2;15880:3;15821:67;;;15921:32;15901:53;;15982:2;15973:12;;15807:184;-1:-1;;15807:184;16000:326;;16160:67;16224:2;16219:3;16160:67;;;16260:28;16240:49;;16317:2;16308:12;;16146:180;-1:-1;;16146:180;16335:401;;16495:67;16559:2;16554:3;16495:67;;;16595:34;16575:55;;16664:34;16659:2;16650:12;;16643:56;16727:2;16718:12;;16481:255;-1:-1;;16481:255;16745:353;;16923:85;17005:2;17000:3;16923:85;;;17041:19;17021:40;;17089:2;17080:12;;16909:189;-1:-1;;16909:189;17107:324;;17267:67;17331:2;17326:3;17267:67;;;17367:26;17347:47;;17422:2;17413:12;;17253:178;-1:-1;;17253:178;17440:331;;17600:67;17664:2;17659:3;17600:67;;;17700:33;17680:54;;17762:2;17753:12;;17586:185;-1:-1;;17586:185;17780:330;;17940:67;18004:2;17999:3;17940:67;;;18040:32;18020:53;;18101:2;18092:12;;17926:184;-1:-1;;17926:184;18119:384;;18279:67;18343:2;18338:3;18279:67;;;18379:34;18359:55;;18448:17;18443:2;18434:12;;18427:39;18494:2;18485:12;;18265:238;-1:-1;;18265:238;18512:331;;18672:67;18736:2;18731:3;18672:67;;;18772:33;18752:54;;18834:2;18825:12;;18658:185;-1:-1;;18658:185;18852:331;;19012:67;19076:2;19071:3;19012:67;;;19112:33;19092:54;;19174:2;19165:12;;18998:185;-1:-1;;18998:185;19192:370;;19352:67;19416:2;19411:3;19352:67;;;19452:34;19432:55;;19521:3;19516:2;19507:12;;19500:25;19553:2;19544:12;;19338:224;-1:-1;;19338:224;19571:322;;19731:67;19795:2;19790:3;19731:67;;;19831:24;19811:45;;19884:2;19875:12;;19717:176;-1:-1;;19717:176;19902:329;;20062:67;20126:2;20121:3;20062:67;;;20162:31;20142:52;;20222:2;20213:12;;20048:183;-1:-1;;20048:183;20240:378;;20400:67;20464:2;20459:3;20400:67;;;20500:34;20480:55;;20569:11;20564:2;20555:12;;20548:33;20609:2;20600:12;;20386:232;-1:-1;;20386:232;20627:361;;20805:85;20887:2;20882:3;20805:85;;;20923:27;20903:48;;20979:2;20970:12;;20791:197;-1:-1;;20791:197;20997:317;;21157:67;21221:2;21216:3;21157:67;;;21257:19;21237:40;;21305:2;21296:12;;21143:171;-1:-1;;21143:171;21323:323;;21483:67;21547:2;21542:3;21483:67;;;21583:25;21563:46;;21637:2;21628:12;;21469:177;-1:-1;;21469:177;21655:442;;21815:67;21879:2;21874:3;21815:67;;;21915:34;21895:55;;21984:34;21979:2;21970:12;;21963:56;22053:6;22048:2;22039:12;;22032:28;22088:2;22079:12;;21801:296;-1:-1;;21801:296;22106:395;;22266:67;22330:2;22325:3;22266:67;;;22366:34;22346:55;;22435:28;22430:2;22421:12;;22414:50;22492:2;22483:12;;22252:249;-1:-1;;22252:249;22510:360;;22688:85;22770:2;22765:3;22688:85;;;22806:26;22786:47;;22861:2;22852:12;;22674:196;-1:-1;;22674:196;22879:323;;23039:67;23103:2;23098:3;23039:67;;;23139:25;23119:46;;23193:2;23184:12;;23025:177;-1:-1;;23025:177;23211:332;;23371:67;23435:2;23430:3;23371:67;;;23471:34;23451:55;;23534:2;23525:12;;23357:186;-1:-1;;23357:186;23552:327;;23712:67;23776:2;23771:3;23712:67;;;23812:29;23792:50;;23870:2;23861:12;;23698:181;-1:-1;;23698:181;23888:332;;24048:67;24112:2;24107:3;24048:67;;;24148:34;24128:55;;24211:2;24202:12;;24034:186;-1:-1;;24034:186;24458:124;24540:36;24570:5;24540:36;;24589:110;24670:23;24687:5;24670:23;;24706:107;24785:22;24801:5;24785:22;;24820:383;;24967:75;25038:3;25029:6;24967:75;;;25064:2;25059:3;25055:12;25048:19;;25078:75;25149:3;25140:6;25078:75;;;-1:-1;25175:2;25166:12;;24955:248;-1:-1;;24955:248;25210:372;;25409:148;25553:3;25409:148;;25589:372;;25788:148;25932:3;25788:148;;25968:511;;26195:148;26339:3;26195:148;;;26188:155;;26354:75;26425:3;26416:6;26354:75;;;-1:-1;26451:2;26442:12;;26176:303;-1:-1;26176:303;26486:511;;26713:148;26857:3;26713:148;;27004:372;;27203:148;27347:3;27203:148;;27383:213;27501:2;27486:18;;27515:71;27490:9;27559:6;27515:71;;27603:229;27729:2;27714:18;;27743:79;27718:9;27795:6;27743:79;;27839:324;27985:2;27970:18;;27999:71;27974:9;28043:6;27999:71;;;28081:72;28149:2;28138:9;28134:18;28125:6;28081:72;;28170:340;28324:2;28309:18;;28338:71;28313:9;28382:6;28338:71;;;28420:80;28496:2;28485:9;28481:18;28472:6;28420:80;;28517:567;28729:3;28714:19;;28744:71;28718:9;28788:6;28744:71;;;28826:80;28902:2;28891:9;28887:18;28878:6;28826:80;;;28917;28993:2;28982:9;28978:18;28969:6;28917:80;;;29008:66;29070:2;29059:9;29055:18;29046:6;29008:66;;;28700:384;;;;;;;;29091:324;29237:2;29222:18;;29251:71;29226:9;29295:6;29251:71;;;29333:72;29401:2;29390:9;29386:18;29377:6;29333:72;;29422:435;29596:2;29581:18;;29610:71;29585:9;29654:6;29610:71;;;29692:72;29760:2;29749:9;29745:18;29736:6;29692:72;;;29775;29843:2;29832:9;29828:18;29819:6;29775:72;;29864:545;30065:3;30050:19;;30080:71;30054:9;30124:6;30080:71;;;30162:72;30230:2;30219:9;30215:18;30206:6;30162:72;;;30245;30313:2;30302:9;30298:18;30289:6;30245:72;;;30328:71;30395:2;30384:9;30380:18;30371:6;30328:71;;30416:382;30618:3;30603:19;;30633:155;30607:9;30761:6;30633:155;;30805:361;30973:2;30987:47;;;30958:18;;31048:108;30958:18;31142:6;31048:108;;31173:201;31285:2;31270:18;;31299:65;31274:9;31337:6;31299:65;;31381:213;31499:2;31484:18;;31513:71;31488:9;31557:6;31513:71;;31601:324;31747:2;31732:18;;31761:71;31736:9;31805:6;31761:71;;31932:324;32078:2;32063:18;;32092:71;32067:9;32136:6;32092:71;;32263:412;32429:2;32414:18;;32443:71;32418:9;32487:6;32443:71;;;32562:9;32556:4;32552:20;32547:2;32536:9;32532:18;32525:48;32587:78;32660:4;32651:6;32587:78;;33013:919;33321:3;33336:47;;;33306:19;;33397:76;33306:19;33459:6;33397:76;;;33389:84;;33484:80;33560:2;33549:9;33545:18;33536:6;33484:80;;;33575:72;33643:2;33632:9;33628:18;33619:6;33575:72;;;33658:80;33734:2;33723:9;33719:18;33710:6;33658:80;;;33749:81;33825:3;33814:9;33810:19;33801:6;33749:81;;;33841;33917:3;33906:9;33902:19;33893:6;33841:81;;;33292:640;;;;;;;;;;33939:903;34239:3;34254:47;;;34224:19;;34315:76;34224:19;34377:6;34315:76;;;34307:84;;34402:80;34478:2;34467:9;34463:18;34454:6;34402:80;;;34493:72;34561:2;34550:9;34546:18;34537:6;34493:72;;;34576;34644:2;34633:9;34629:18;34620:6;34576:72;;34849:259;34990:2;34975:18;;35004:94;34979:9;35071:6;35004:94;;35377:301;35515:2;35529:47;;;35500:18;;35590:78;35500:18;35654:6;35590:78;;35685:407;35876:2;35890:47;;;35861:18;;35951:131;35861:18;35951:131;;36099:407;36290:2;36304:47;;;36275:18;;36365:131;36275:18;36365:131;;36513:407;36704:2;36718:47;;;36689:18;;36779:131;36689:18;36779:131;;36927:407;37118:2;37132:47;;;37103:18;;37193:131;37103:18;37193:131;;37341:407;37532:2;37546:47;;;37517:18;;37607:131;37517:18;37607:131;;37755:407;37946:2;37960:47;;;37931:18;;38021:131;37931:18;38021:131;;38169:407;38360:2;38374:47;;;38345:18;;38435:131;38345:18;38435:131;;38583:407;38774:2;38788:47;;;38759:18;;38849:131;38759:18;38849:131;;38997:407;39188:2;39202:47;;;39173:18;;39263:131;39173:18;39263:131;;39411:407;39602:2;39616:47;;;39587:18;;39677:131;39587:18;39677:131;;39825:407;40016:2;40030:47;;;40001:18;;40091:131;40001:18;40091:131;;40239:407;40430:2;40444:47;;;40415:18;;40505:131;40415:18;40505:131;;40653:407;40844:2;40858:47;;;40829:18;;40919:131;40829:18;40919:131;;41067:407;41258:2;41272:47;;;41243:18;;41333:131;41243:18;41333:131;;41481:407;41672:2;41686:47;;;41657:18;;41747:131;41657:18;41747:131;;41895:407;42086:2;42100:47;;;42071:18;;42161:131;42071:18;42161:131;;42309:407;42500:2;42514:47;;;42485:18;;42575:131;42485:18;42575:131;;42723:407;42914:2;42928:47;;;42899:18;;42989:131;42899:18;42989:131;;43137:407;43328:2;43342:47;;;43313:18;;43403:131;43313:18;43403:131;;43551:407;43742:2;43756:47;;;43727:18;;43817:131;43727:18;43817:131;;43965:407;44156:2;44170:47;;;44141:18;;44231:131;44141:18;44231:131;;44379:407;44570:2;44584:47;;;44555:18;;44645:131;44555:18;44645:131;;44793:407;44984:2;44998:47;;;44969:18;;45059:131;44969:18;45059:131;;45207:407;45398:2;45412:47;;;45383:18;;45473:131;45383:18;45473:131;;45621:407;45812:2;45826:47;;;45797:18;;45887:131;45797:18;45887:131;;46035:407;46226:2;46240:47;;;46211:18;;46301:131;46211:18;46301:131;;46449:407;46640:2;46654:47;;;46625:18;;46715:131;46625:18;46715:131;;46863:407;47054:2;47068:47;;;47039:18;;47129:131;47039:18;47129:131;;47277:407;47468:2;47482:47;;;47453:18;;47543:131;47453:18;47543:131;;47691:407;47882:2;47896:47;;;47867:18;;47957:131;47867:18;47957:131;;48656:435;48830:2;48815:18;;48844:71;48819:9;48888:6;48844:71;;49098:871;49378:3;49363:19;;49393:69;49367:9;49435:6;49393:69;;;49473:70;49539:2;49528:9;49524:18;49515:6;49473:70;;;49554;49620:2;49609:9;49605:18;49596:6;49554:70;;;49635:72;49703:2;49692:9;49688:18;49679:6;49635:72;;;49718:73;49786:3;49775:9;49771:19;49762:6;49718:73;;;49802;49870:3;49859:9;49855:19;49846:6;49802:73;;;49886;49954:3;49943:9;49939:19;49930:6;49886:73;;;49349:620;;;;;;;;;;;49976:205;50090:2;50075:18;;50104:67;50079:9;50144:6;50104:67;;50311:151;50435:4;50426:14;;50383:79;50569:127;-1:-1;50682:4;;50660:36;50703:137;50806:12;;50777:63;51740:178;51858:19;;;51907:4;51898:14;;51851:67;52562:91;;52624:24;52642:5;52624:24;;52766:85;52832:13;52825:21;;52808:43;52937:105;;53013:24;53031:5;53013:24;;53136:121;-1:-1;;;;;53198:54;;53181:76;53343:96;53415:18;53404:30;;53387:52;53446:81;53517:4;53506:16;;53489:38;53534:129;;53621:37;53652:5;53621:37;;54272:138;;54359:46;54372:32;54398:5;54372:32;;54417:116;;54504:24;54522:5;54504:24;;55029:106;;55107:23;55124:5;55107:23;;55143:268;55208:1;55215:101;55229:6;55226:1;55223:13;55215:101;;;55296:11;;;55290:18;55277:11;;;55270:39;55251:2;55244:10;55215:101;;;55331:6;55328:1;55325:13;55322:2;;;55396:1;55387:6;55382:3;55378:16;55371:27;55322:2;55192:219;;;;;55419:95;;55483:26;55503:5;55602:89;55666:20;55680:5;55666:20;;55698:97;55786:2;55766:14;-1:-1;;55762:28;;55746:49;55903:94;55977:2;55973:14;;55945:52;56005:117;56074:24;56092:5;56074:24;;;56067:5;56064:35;56054:2;;56113:1;56110;56103:12;56269:111;56335:21;56350:5;56335:21;;56387:117;56456:24;56474:5;56456:24;;56511:145;56594:38;56626:5;56594:38;

Swarm Source

bzzr://882079c2d3031f6a74125aae31f9cd51dec8225767dd29b17b1d172a05b79e39

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.