Contract 0x3b08c19106632da4fb21e26646cb542cf5df546a

Contract Overview

Balance:
0 MATIC
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x9096d815076515f17e65d9dd924c448abbebddb138bc069cdde4431ec490786eUpdate Rates264287942022-05-23 7:50:063 hrs 20 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xfdb0a750009e3ddabce75dabc9f4946587044e59f7cc7a3c99ba4ee5db5bbed0Update Rates264275302022-05-23 5:43:075 hrs 27 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x454714fde0a96dfe37bf0f596f1089097234fe34ae38e04a447ebd2300a8d9f1Update Rates264275182022-05-23 5:42:065 hrs 28 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x957f5d77dfe1114c5af89bde0761641cbc98d4f22578dc9d34247d5410746236Update Rates264233262022-05-22 21:29:1113 hrs 41 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xcf6f6f9a59dc4ee110fdcc5fa9a3d65cb7477afd8db96841aa7a91d5da222fcdUpdate Rates264229722022-05-22 20:46:0414 hrs 24 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xe20b6d82551c31ace8990424803eadb00159cc7af80292b73d409c146857e829Update Rates264229662022-05-22 20:45:0414 hrs 25 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x6dcef4ce6c84f9c590b576aac297bac7245489cedb8626b18fc63e59de027830Update Rates264229242022-05-22 20:38:0414 hrs 32 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x29ab88da6ce79d90522a6aa6c843c8a76251e0630754320e7d0165ff669e9eb9Update Rates264229002022-05-22 20:34:0314 hrs 36 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xe4a99f9089ce104c095659a722095ca03d152df315d6a1341b201c6825a90905Update Rates264226272022-05-22 19:59:0915 hrs 11 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x66372568713880e6938a83eae9de46a33be954ef3df5bc70bd07461b8ffb94feUpdate Rates264226152022-05-22 19:57:0915 hrs 13 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.0001444562
0x27be5dac38698f283ddc958168a26c68bfcf4104923de4691c395106a04337f7Update Rates264225952022-05-22 19:53:4915 hrs 16 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x1571d9ca98bab67be3965d087ab87bb6aabe42610116858ba682e919afd622d3Update Rates264222382022-05-22 19:10:1216 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x587353c6903ecd37fc32e5942bb49e403da83e7f92d28489d951d43944481842Update Rates264197832022-05-22 13:51:0321 hrs 19 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xb600f26b57f86b15f883fed3939307aba57074a210697c67ac10b5632df1ee19Update Rates264197782022-05-22 13:50:1321 hrs 20 mins ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x612e63541e24759dd1c6d4dae0933b70d1db2926ab080f1194a004d9d469a15dUpdate Rates264149092022-05-22 3:56:041 day 7 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xe218d3c50d612aaeb23ace6f65e457650287b70d299e1b256affb1ca42679b5dUpdate Rates264148972022-05-22 3:55:041 day 7 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x12771fec7b619bbe875f94a952b3ca4cb31c90960fa8d71649d84394244bbd29Update Rates264138002022-05-22 1:44:071 day 9 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x68c6643e084754c9637d41e3eedbcab175188032729628a5d245959bf3f87577Update Rates264123202022-05-21 22:54:081 day 12 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xcbb760d9697a234087345eafc2e627a639a34641ebd095e091dcaa603e6c9220Update Rates264087692022-05-21 16:37:071 day 18 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x07ebca5774e11eeac0549733d1c912c409035d4aa8ffc4bfb16d5cde25bd532cUpdate Rates264087622022-05-21 16:36:061 day 18 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x6028c479575662662c888a974bd6f4a7f46ccb78da115ead843463b0e7913da8Update Rates264062852022-05-21 12:21:081 day 22 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0xb7973d15da6ad9e949ffe49b63996a7fbce1d82b190fbd94f4c723cf523f9497Update Rates264062732022-05-21 12:20:081 day 22 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x9915a406ff2c90685cc7d01cb93c8ada2addfed9942dcabb26fe13a513ec9177Update Rates264041532022-05-21 9:06:102 days 2 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x29ea4eecb7692bbb8d3ced060882241202b003a373ba9c57276aa5ba0dc22552Update Rates264041352022-05-21 9:03:102 days 2 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
0x5f885bea1677bd583a8296020d38e9a067d07cb3f163049ffba03cb2937f20eaUpdate Rates264040662022-05-21 8:54:042 days 2 hrs ago0x055ca0b950e129ff387de1dbf53cabcb434a64be IN  0x3b08c19106632da4fb21e26646cb542cf5df546a0 MATIC0.000144482
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ExchangeRates

Compiler Version
v0.5.16+commit.9c3226ce

Optimization Enabled:
Yes with 20000 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity)

/**
 *Submitted for verification at polygonscan.com on 2021-12-27
*/

/*
    ___            _       ___  _                          
    | .\ ___  _ _ <_> ___ | __><_>._ _  ___ ._ _  ___  ___ 
    |  _// ._>| '_>| ||___|| _> | || ' |<_> || ' |/ | '/ ._>
    |_|  \___.|_|  |_|     |_|  |_||_|_|<___||_|_|\_|_.\___.
    
* PeriFinance: ExchangeRates.sol
*
* Latest source (may be newer): https://github.com/perifinance/peri-finance/blob/master/contracts/ExchangeRates.sol
* Docs: Will be added in the future. 
* https://docs.peri.finance/contracts/source/contracts/ExchangeRates
*
* Contract Dependencies: 
*	- IAddressResolver
*	- IExchangeRates
*	- MixinResolver
*	- MixinSystemSettings
*	- Owned
* Libraries: 
*	- SafeDecimalMath
*	- SafeMath
*
* 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);
}


// 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/iexchangerates
interface IExchangeRates {
    // Structs
    struct RateAndUpdatedTime {
        uint216 rate;
        uint40 time;
    }

    struct InversePricing {
        uint entryPoint;
        uint upperLimit;
        uint lowerLimit;
        bool frozenAtUpperLimit;
        bool frozenAtLowerLimit;
    }

    // Views
    function aggregators(bytes32 currencyKey) external view returns (address);

    function aggregatorWarningFlags() external view returns (address);

    function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool);

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

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

    function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory);

    function effectiveValue(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    ) external view returns (uint value);

    function effectiveValueAndRates(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    )
        external
        view
        returns (
            uint value,
            uint sourceRate,
            uint destinationRate
        );

    function effectiveValueAtRound(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        uint roundIdForSrc,
        uint roundIdForDest
    ) external view returns (uint value);

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

    function getLastRoundIdBeforeElapsedSecs(
        bytes32 currencyKey,
        uint startingRoundId,
        uint startingTimestamp,
        uint timediff
    ) external view returns (uint);

    function inversePricing(bytes32 currencyKey)
        external
        view
        returns (
            uint entryPoint,
            uint upperLimit,
            uint lowerLimit,
            bool frozenAtUpperLimit,
            bool frozenAtLowerLimit
        );

    function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256);

    function oracle() external view returns (address);

    function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time);

    function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);

    function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid);

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

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

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

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

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

    function rateStalePeriod() external view returns (uint);

    function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds)
        external
        view
        returns (uint[] memory rates, uint[] memory times);

    function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys)
        external
        view
        returns (uint[] memory rates, bool anyRateInvalid);

    function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory);

    // Mutative functions
    function freezeRate(bytes32 currencyKey) 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);
    }
}


interface AggregatorInterface {
  function latestAnswer() external view returns (int256);
  function latestTimestamp() external view returns (uint256);
  function latestRound() external view returns (uint256);
  function getAnswer(uint256 roundId) external view returns (int256);
  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}


interface AggregatorV3Interface {

  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}


/**
 * @title The V2 & V3 Aggregator Interface
 * @notice Solidity V0.5 does not allow interfaces to inherit from other
 * interfaces so this contract is a combination of v0.5 AggregatorInterface.sol
 * and v0.5 AggregatorV3Interface.sol.
 */
interface AggregatorV2V3Interface {
  //
  // V2 Interface:
  //
  function latestAnswer() external view returns (int256);
  function latestTimestamp() external view returns (uint256);
  function latestRound() external view returns (uint256);
  function getAnswer(uint256 roundId) external view returns (int256);
  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);

  //
  // V3 Interface:
  //
  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

}


interface FlagsInterface {
  function getFlag(address) external view returns (bool);
  function getFlags(address[] calldata) external view returns (bool[] memory);
  function raiseFlag(address) external;
  function raiseFlags(address[] calldata) external;
  function lowerFlags(address[] calldata) external;
  function setRaisingAccessController(address) 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/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;
}


// Inheritance


// Libraries


// Internal references
// AggregatorInterface from Chainlink represents a decentralized pricing network for a single currency key

// FlagsInterface from Chainlink addresses SIP-76


interface IExternalRateAggregator {
    function getRateAndUpdatedTime(bytes32 _currencyKey) external view returns (uint, uint);
}

// https://docs.peri.finance/contracts/source/contracts/exchangerates
contract ExchangeRates is Owned, MixinSystemSettings, IExchangeRates {
    using SafeMath for uint;
    using SafeDecimalMath for uint;

    // Exchange rates and update times stored by currency code, e.g. 'PERI', or 'pUSD'
    mapping(bytes32 => mapping(uint => RateAndUpdatedTime)) private _rates;

    // The address of the oracle which pushes rate updates to this contract
    address public oracle;

    mapping(bytes32 => bool) public currencyByExternal;

    // Decentralized oracle networks that feed into pricing aggregators
    mapping(bytes32 => AggregatorV2V3Interface) public aggregators;

    mapping(bytes32 => uint8) public currencyKeyDecimals;

    // List of aggregator keys for convenient iteration
    bytes32[] public aggregatorKeys;

    // Do not allow the oracle to submit times any further forward into the future than this constant.
    uint private constant ORACLE_FUTURE_LIMIT = 10 minutes;

    mapping(bytes32 => InversePricing) public inversePricing;

    bytes32[] public invertedKeys;

    mapping(bytes32 => uint) public currentRoundForRate;

    mapping(bytes32 => uint) public roundFrozen;

    /* ========== ADDRESS RESOLVER CONFIGURATION ========== */
    bytes32 private constant CONTRACT_EXCHANGER = "Exchanger";

    //
    // ========== CONSTRUCTOR ==========

    constructor(
        address _owner,
        address _oracle,
        address _resolver,
        bytes32[] memory _currencyKeys,
        uint[] memory _newRates
    ) public Owned(_owner) MixinSystemSettings(_resolver) {
        require(_currencyKeys.length == _newRates.length, "Currency key length and rate length must match.");

        oracle = _oracle;

        // The pUSD rate is always 1 and is never stale.
        _setRate("pUSD", SafeDecimalMath.unit(), now);

        internalUpdateRates(_currencyKeys, _newRates, now);
    }

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

    function setOracle(address _oracle) external onlyOwner {
        oracle = _oracle;
        emit OracleUpdated(oracle);
    }

    function setCurrencyToExternalAggregator(bytes32 _currencyKey, bool _set) external onlyOwner {
        currencyByExternal[_currencyKey] = _set;
    }

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

    function updateRates(
        bytes32[] calldata currencyKeys,
        uint[] calldata newRates,
        uint timeSent
    ) external onlyOracle returns (bool) {
        return internalUpdateRates(currencyKeys, newRates, timeSent);
    }

    function deleteRate(bytes32 currencyKey) external onlyOracle {
        require(_getRate(currencyKey) > 0, "Rate is zero");

        delete _rates[currencyKey][currentRoundForRate[currencyKey]];

        currentRoundForRate[currencyKey]--;

        emit RateDeleted(currencyKey);
    }

    function setInversePricing(
        bytes32 currencyKey,
        uint entryPoint,
        uint upperLimit,
        uint lowerLimit,
        bool freezeAtUpperLimit,
        bool freezeAtLowerLimit
    ) external onlyOwner {
        // 0 < lowerLimit < entryPoint => 0 < entryPoint
        require(lowerLimit > 0, "lowerLimit must be above 0");
        require(upperLimit > entryPoint, "upperLimit must be above the entryPoint");
        require(upperLimit < entryPoint.mul(2), "upperLimit must be less than double entryPoint");
        require(lowerLimit < entryPoint, "lowerLimit must be below the entryPoint");

        require(!(freezeAtUpperLimit && freezeAtLowerLimit), "Cannot freeze at both limits");

        InversePricing storage inverse = inversePricing[currencyKey];
        if (inverse.entryPoint == 0) {
            // then we are adding a new inverse pricing, so add this
            invertedKeys.push(currencyKey);
        }
        inverse.entryPoint = entryPoint;
        inverse.upperLimit = upperLimit;
        inverse.lowerLimit = lowerLimit;

        if (freezeAtUpperLimit || freezeAtLowerLimit) {
            // When indicating to freeze, we need to know the rate to freeze it at - either upper or lower
            // this is useful in situations where ExchangeRates is updated and there are existing inverted
            // rates already frozen in the current contract that need persisting across the upgrade

            inverse.frozenAtUpperLimit = freezeAtUpperLimit;
            inverse.frozenAtLowerLimit = freezeAtLowerLimit;
            uint roundId = _getCurrentRoundId(currencyKey);
            roundFrozen[currencyKey] = roundId;
            emit InversePriceFrozen(currencyKey, freezeAtUpperLimit ? upperLimit : lowerLimit, roundId, msg.sender);
        } else {
            // unfreeze if need be
            inverse.frozenAtUpperLimit = false;
            inverse.frozenAtLowerLimit = false;
            // remove any tracking
            roundFrozen[currencyKey] = 0;
        }

        // SIP-78
        uint rate = _getRate(currencyKey);
        if (rate > 0) {
            exchanger().setLastExchangeRateForPynth(currencyKey, rate);
        }

        emit InversePriceConfigured(currencyKey, entryPoint, upperLimit, lowerLimit);
    }

    function removeInversePricing(bytes32 currencyKey) external onlyOwner {
        require(inversePricing[currencyKey].entryPoint > 0, "No inverted price exists");

        delete inversePricing[currencyKey];

        // now remove inverted key from array
        bool wasRemoved = removeFromArray(currencyKey, invertedKeys);

        if (wasRemoved) {
            emit InversePriceConfigured(currencyKey, 0, 0, 0);
        }
    }

    function addAggregator(bytes32 currencyKey, address aggregatorAddress) external onlyOwner {
        AggregatorV2V3Interface aggregator = AggregatorV2V3Interface(aggregatorAddress);
        // This check tries to make sure that a valid aggregator is being added.
        // It checks if the aggregator is an existing smart contract that has implemented `latestTimestamp` function.

        require(aggregator.latestRound() >= 0, "Given Aggregator is invalid");
        uint8 decimals = aggregator.decimals();
        require(decimals <= 18, "Aggregator decimals should be lower or equal to 18");
        if (address(aggregators[currencyKey]) == address(0)) {
            aggregatorKeys.push(currencyKey);
        }
        aggregators[currencyKey] = aggregator;
        currencyKeyDecimals[currencyKey] = decimals;
        emit AggregatorAdded(currencyKey, address(aggregator));
    }

    function removeAggregator(bytes32 currencyKey) external onlyOwner {
        address aggregator = address(aggregators[currencyKey]);
        require(aggregator != address(0), "No aggregator exists for key");
        delete aggregators[currencyKey];
        delete currencyKeyDecimals[currencyKey];

        bool wasRemoved = removeFromArray(currencyKey, aggregatorKeys);

        if (wasRemoved) {
            emit AggregatorRemoved(currencyKey, aggregator);
        }
    }

    // SIP-75 Public keeper function to freeze a pynth that is out of bounds
    function freezeRate(bytes32 currencyKey) external {
        InversePricing storage inverse = inversePricing[currencyKey];
        require(inverse.entryPoint > 0, "Cannot freeze non-inverse rate");
        require(!inverse.frozenAtUpperLimit && !inverse.frozenAtLowerLimit, "The rate is already frozen");

        uint rate = _getRate(currencyKey);

        if (rate > 0 && (rate >= inverse.upperLimit || rate <= inverse.lowerLimit)) {
            inverse.frozenAtUpperLimit = (rate == inverse.upperLimit);
            inverse.frozenAtLowerLimit = (rate == inverse.lowerLimit);
            uint currentRoundId = _getCurrentRoundId(currencyKey);
            roundFrozen[currencyKey] = currentRoundId;
            emit InversePriceFrozen(currencyKey, rate, currentRoundId, msg.sender);
        } else {
            revert("Rate within bounds");
        }
    }

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

    function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
        bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
        bytes32[] memory newAddresses = new bytes32[](1);
        newAddresses[0] = CONTRACT_EXCHANGER;
        addresses = combineArrays(existingAddresses, newAddresses);
    }

    // SIP-75 View to determine if freezeRate can be called safely
    function canFreezeRate(bytes32 currencyKey) external view returns (bool) {
        InversePricing memory inverse = inversePricing[currencyKey];
        if (inverse.entryPoint == 0 || inverse.frozenAtUpperLimit || inverse.frozenAtLowerLimit) {
            return false;
        } else {
            uint rate = _getRate(currencyKey);
            return (rate > 0 && (rate >= inverse.upperLimit || rate <= inverse.lowerLimit));
        }
    }

    function currenciesUsingAggregator(address aggregator) external view returns (bytes32[] memory currencies) {
        uint count = 0;
        currencies = new bytes32[](aggregatorKeys.length);
        for (uint i = 0; i < aggregatorKeys.length; i++) {
            bytes32 currencyKey = aggregatorKeys[i];
            if (address(aggregators[currencyKey]) == aggregator) {
                currencies[count++] = currencyKey;
            }
        }
    }

    function rateStalePeriod() external view returns (uint) {
        return getRateStalePeriod();
    }

    function aggregatorWarningFlags() external view returns (address) {
        return getAggregatorWarningFlags();
    }

    function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time) {
        RateAndUpdatedTime memory rateAndTime = _getRateAndUpdatedTime(currencyKey);
        return (rateAndTime.rate, rateAndTime.time);
    }

    function getLastRoundIdBeforeElapsedSecs(
        bytes32 currencyKey,
        uint startingRoundId,
        uint startingTimestamp,
        uint timediff
    ) external view returns (uint) {
        uint roundId = startingRoundId;
        uint nextTimestamp = 0;
        while (true) {
            (, nextTimestamp) = _getRateAndTimestampAtRound(currencyKey, roundId + 1);
            // if there's no new round, then the previous roundId was the latest
            if (nextTimestamp == 0 || nextTimestamp > startingTimestamp + timediff) {
                return roundId;
            }
            roundId++;
        }
        return roundId;
    }

    function getCurrentRoundId(bytes32 currencyKey) external view returns (uint) {
        return _getCurrentRoundId(currencyKey);
    }

    function effectiveValueAtRound(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey,
        uint roundIdForSrc,
        uint roundIdForDest
    ) external view returns (uint value) {
        // If there's no change in the currency, then just return the amount they gave us
        if (sourceCurrencyKey == destinationCurrencyKey) return sourceAmount;

        (uint srcRate, ) = _getRateAndTimestampAtRound(sourceCurrencyKey, roundIdForSrc);
        (uint destRate, ) = _getRateAndTimestampAtRound(destinationCurrencyKey, roundIdForDest);
        if (destRate == 0) {
            // prevent divide-by 0 error (this can happen when roundIDs jump epochs due
            // to aggregator upgrades)
            return 0;
        }
        // Calculate the effective value by going from source -> USD -> destination
        value = sourceAmount.multiplyDecimalRound(srcRate).divideDecimalRound(destRate);
    }

    function rateAndTimestampAtRound(bytes32 currencyKey, uint roundId) external view returns (uint rate, uint time) {
        return _getRateAndTimestampAtRound(currencyKey, roundId);
    }

    function lastRateUpdateTimes(bytes32 currencyKey) external view returns (uint256) {
        return _getUpdatedTime(currencyKey);
    }

    function lastRateUpdateTimesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory) {
        uint[] memory lastUpdateTimes = new uint[](currencyKeys.length);

        for (uint i = 0; i < currencyKeys.length; i++) {
            lastUpdateTimes[i] = _getUpdatedTime(currencyKeys[i]);
        }

        return lastUpdateTimes;
    }

    function effectiveValue(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    ) external view returns (uint value) {
        (value, , ) = _effectiveValueAndRates(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);
    }

    function effectiveValueAndRates(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    )
        external
        view
        returns (
            uint value,
            uint sourceRate,
            uint destinationRate
        )
    {
        return _effectiveValueAndRates(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);
    }

    function rateForCurrency(bytes32 currencyKey) external view returns (uint) {
        return _getRateAndUpdatedTime(currencyKey).rate;
    }

    function ratesAndUpdatedTimeForCurrencyLastNRounds(bytes32 currencyKey, uint numRounds)
        external
        view
        returns (uint[] memory rates, uint[] memory times)
    {
        rates = new uint[](numRounds);
        times = new uint[](numRounds);

        uint roundId = _getCurrentRoundId(currencyKey);
        for (uint i = 0; i < numRounds; i++) {
            // fetch the rate and treat is as current, so inverse limits if frozen will always be applied
            // regardless of current rate
            (rates[i], times[i]) = _getRateAndTimestampAtRound(currencyKey, roundId);

            if (roundId == 0) {
                // if we hit the last round, then return what we have
                return (rates, times);
            } else {
                roundId--;
            }
        }
    }

    function ratesForCurrencies(bytes32[] calldata currencyKeys) external view returns (uint[] memory) {
        uint[] memory _localRates = new uint[](currencyKeys.length);

        for (uint i = 0; i < currencyKeys.length; i++) {
            _localRates[i] = _getRate(currencyKeys[i]);
        }

        return _localRates;
    }

    function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid) {
        RateAndUpdatedTime memory rateAndTime = _getRateAndUpdatedTime(currencyKey);

        if (currencyKey == "pUSD") {
            return (rateAndTime.rate, false);
        }
        return (
            rateAndTime.rate,
            _rateIsStaleWithTime(getRateStalePeriod(), rateAndTime.time) ||
                _rateIsFlagged(currencyKey, FlagsInterface(getAggregatorWarningFlags()))
        );
    }

    function ratesAndInvalidForCurrencies(bytes32[] calldata currencyKeys)
        external
        view
        returns (uint[] memory rates, bool anyRateInvalid)
    {
        rates = new uint[](currencyKeys.length);

        uint256 _rateStalePeriod = getRateStalePeriod();

        // fetch all flags at once
        bool[] memory flagList = getFlagsForRates(currencyKeys);

        for (uint i = 0; i < currencyKeys.length; i++) {
            // do one lookup of the rate & time to minimize gas
            RateAndUpdatedTime memory rateEntry = _getRateAndUpdatedTime(currencyKeys[i]);
            rates[i] = rateEntry.rate;
            if (!anyRateInvalid && currencyKeys[i] != "pUSD") {
                anyRateInvalid = flagList[i] || _rateIsStaleWithTime(_rateStalePeriod, rateEntry.time);
            }
        }
    }

    function rateIsStale(bytes32 currencyKey) external view returns (bool) {
        return _rateIsStale(currencyKey, getRateStalePeriod());
    }

    function rateIsFrozen(bytes32 currencyKey) external view returns (bool) {
        return _rateIsFrozen(currencyKey);
    }

    function rateIsInvalid(bytes32 currencyKey) external view returns (bool) {
        return
            _rateIsStale(currencyKey, getRateStalePeriod()) ||
            _rateIsFlagged(currencyKey, FlagsInterface(getAggregatorWarningFlags()));
    }

    function rateIsFlagged(bytes32 currencyKey) external view returns (bool) {
        return _rateIsFlagged(currencyKey, FlagsInterface(getAggregatorWarningFlags()));
    }

    function anyRateIsInvalid(bytes32[] calldata currencyKeys) external view returns (bool) {
        // Loop through each key and check whether the data point is stale.

        uint256 _rateStalePeriod = getRateStalePeriod();
        bool[] memory flagList = getFlagsForRates(currencyKeys);

        for (uint i = 0; i < currencyKeys.length; i++) {
            if (flagList[i] || _rateIsStale(currencyKeys[i], _rateStalePeriod)) {
                return true;
            }
        }

        return false;
    }

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

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

    function getFlagsForRates(bytes32[] memory currencyKeys) internal view returns (bool[] memory flagList) {
        FlagsInterface _flags = FlagsInterface(getAggregatorWarningFlags());

        // fetch all flags at once
        if (_flags != FlagsInterface(0)) {
            address[] memory _aggregators = new address[](currencyKeys.length);

            for (uint i = 0; i < currencyKeys.length; i++) {
                _aggregators[i] = address(aggregators[currencyKeys[i]]);
            }

            flagList = _flags.getFlags(_aggregators);
        } else {
            flagList = new bool[](currencyKeys.length);
        }
    }

    function _setRate(
        bytes32 currencyKey,
        uint256 rate,
        uint256 time
    ) internal {
        // Note: this will effectively start the rounds at 1, which matches Chainlink's Agggregators
        currentRoundForRate[currencyKey]++;

        _rates[currencyKey][currentRoundForRate[currencyKey]] = RateAndUpdatedTime({
            rate: uint216(rate),
            time: uint40(time)
        });
    }

    function nowPlusLimit()
        external
        view
        returns (
            uint,
            uint,
            uint
        )
    {
        return (now, ORACLE_FUTURE_LIMIT, now + ORACLE_FUTURE_LIMIT);
    }

    function internalUpdateRates(
        bytes32[] memory currencyKeys,
        uint[] memory newRates,
        uint timeSent
    ) internal returns (bool) {
        require(currencyKeys.length == newRates.length, "Currency key array length must match rates array length.");
        require(timeSent < (now + ORACLE_FUTURE_LIMIT), "Time is too far into the future");

        // Loop through each key and perform update.
        for (uint i = 0; i < currencyKeys.length; i++) {
            bytes32 currencyKey = currencyKeys[i];

            // Should not set any rate to zero ever, as no asset will ever be
            // truely worthless and still valid. In this scenario, we should
            // delete the rate and remove it from the system.
            require(newRates[i] != 0, "Zero is not a valid rate, please call deleteRate instead.");
            require(currencyKey != "pUSD", "Rate of pUSD cannot be updated, it's always UNIT.");

            // We should only update the rate if it's at least the same age as the last rate we've got.
            if (timeSent < _getUpdatedTime(currencyKey)) {
                continue;
            }

            // Ok, go ahead with the update.
            _setRate(currencyKey, newRates[i], timeSent);
        }

        emit RatesUpdated(currencyKeys, newRates);

        return true;
    }

    function removeFromArray(bytes32 entry, bytes32[] storage array) internal returns (bool) {
        for (uint i = 0; i < array.length; i++) {
            if (array[i] == entry) {
                delete array[i];

                // Copy the last key into the place of the one we just deleted
                // If there's only one key, this is array[0] = array[0].
                // If we're deleting the last one, it's also a NOOP in the same way.
                array[i] = array[array.length - 1];

                // Decrease the size of the array by one.
                array.length--;

                return true;
            }
        }
        return false;
    }

    function _rateOrInverted(
        bytes32 currencyKey,
        uint rate,
        uint roundId
    ) internal view returns (uint newRate) {
        // if an inverse mapping exists, adjust the price accordingly
        InversePricing memory inverse = inversePricing[currencyKey];
        if (inverse.entryPoint == 0 || rate == 0) {
            // when no inverse is set or when given a 0 rate, return the rate, regardless of the inverse status
            // (the latter is so when a new inverse is set but the underlying has no rate, it will return 0 as
            // the rate, not the lowerLimit)
            return rate;
        }

        newRate = rate;

        // Determine when round was frozen (if any)
        uint roundWhenRateFrozen = roundFrozen[currencyKey];
        // And if we're looking at a rate after frozen, and it's currently frozen, then apply the bounds limit even
        // if the current price is back within bounds
        if (roundId >= roundWhenRateFrozen && inverse.frozenAtUpperLimit) {
            newRate = inverse.upperLimit;
        } else if (roundId >= roundWhenRateFrozen && inverse.frozenAtLowerLimit) {
            newRate = inverse.lowerLimit;
        } else {
            // this ensures any rate outside the limit will never be returned
            uint doubleEntryPoint = inverse.entryPoint.mul(2);
            if (doubleEntryPoint <= rate) {
                // avoid negative numbers for unsigned ints, so set this to 0
                // which by the requirement that lowerLimit be > 0 will
                // cause this to freeze the price to the lowerLimit
                newRate = 0;
            } else {
                newRate = doubleEntryPoint.sub(rate);
            }

            // now ensure the rate is between the bounds
            if (newRate >= inverse.upperLimit) {
                newRate = inverse.upperLimit;
            } else if (newRate <= inverse.lowerLimit) {
                newRate = inverse.lowerLimit;
            }
        }
    }

    function _formatAggregatorAnswer(bytes32 currencyKey, int256 rate) internal view returns (uint) {
        require(rate >= 0, "Negative rate not supported");
        if (currencyKeyDecimals[currencyKey] > 0) {
            uint multiplier = 10**uint(SafeMath.sub(18, currencyKeyDecimals[currencyKey]));
            return uint(uint(rate).mul(multiplier));
        }
        return uint(rate);
    }

    function _isFromAggregator(bytes32 _currencyKey) internal view returns (bool) {
        return !currencyByExternal[_currencyKey] && aggregators[_currencyKey] != AggregatorV2V3Interface(0);
    }

    function _getRateAndUpdatedTime(bytes32 currencyKey) internal view returns (RateAndUpdatedTime memory) {
        AggregatorV2V3Interface aggregator = aggregators[currencyKey];

        if (_isFromAggregator(currencyKey)) {
            // this view from the aggregator is the most gas efficient but it can throw when there's no data,
            // so let's call it low-level to suppress any reverts
            bytes memory payload = abi.encodeWithSignature("latestRoundData()");
            // solhint-disable avoid-low-level-calls
            (bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

            if (success) {
                (uint80 roundId, int256 answer, , uint256 updatedAt, ) =
                    abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
                return
                    RateAndUpdatedTime({
                        rate: uint216(_rateOrInverted(currencyKey, _formatAggregatorAnswer(currencyKey, answer), roundId)),
                        time: uint40(updatedAt)
                    });
            }
        } else {
            uint roundId = currentRoundForRate[currencyKey];
            RateAndUpdatedTime memory entry = _rates[currencyKey][roundId];

            return RateAndUpdatedTime({rate: uint216(_rateOrInverted(currencyKey, entry.rate, roundId)), time: entry.time});
        }
    }

    function _getCurrentRoundId(bytes32 currencyKey) internal view returns (uint) {
        AggregatorV2V3Interface aggregator = aggregators[currencyKey];

        if (_isFromAggregator(currencyKey)) {
            return aggregator.latestRound();
        } else {
            return currentRoundForRate[currencyKey];
        }
    }

    function _getRateAndTimestampAtRound(bytes32 currencyKey, uint roundId) internal view returns (uint rate, uint time) {
        AggregatorV2V3Interface aggregator = aggregators[currencyKey];

        if (_isFromAggregator(currencyKey)) {
            // this view from the aggregator is the most gas efficient but it can throw when there's no data,
            // so let's call it low-level to suppress any reverts
            bytes memory payload = abi.encodeWithSignature("getRoundData(uint80)", roundId);
            // solhint-disable avoid-low-level-calls
            (bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

            if (success) {
                (, int256 answer, , uint256 updatedAt, ) =
                    abi.decode(returnData, (uint80, int256, uint256, uint256, uint80));
                return (_rateOrInverted(currencyKey, _formatAggregatorAnswer(currencyKey, answer), roundId), updatedAt);
            }
        } else {
            RateAndUpdatedTime memory update = _rates[currencyKey][roundId];
            return (_rateOrInverted(currencyKey, update.rate, roundId), update.time);
        }
    }

    function _getRate(bytes32 currencyKey) internal view returns (uint256) {
        return _getRateAndUpdatedTime(currencyKey).rate;
    }

    function _getUpdatedTime(bytes32 currencyKey) internal view returns (uint256) {
        return _getRateAndUpdatedTime(currencyKey).time;
    }

    function _effectiveValueAndRates(
        bytes32 sourceCurrencyKey,
        uint sourceAmount,
        bytes32 destinationCurrencyKey
    )
        internal
        view
        returns (
            uint value,
            uint sourceRate,
            uint destinationRate
        )
    {
        sourceRate = _getRate(sourceCurrencyKey);
        // If there's no change in the currency, then just return the amount they gave us
        if (sourceCurrencyKey == destinationCurrencyKey) {
            destinationRate = sourceRate;
            value = sourceAmount;
        } else {
            // Calculate the effective value by going from source -> USD -> destination
            destinationRate = _getRate(destinationCurrencyKey);
            // prevent divide-by 0 error (this happens if the dest is not a valid rate)
            if (destinationRate > 0) {
                value = sourceAmount.multiplyDecimalRound(sourceRate).divideDecimalRound(destinationRate);
            }
        }
    }

    function _rateIsStale(bytes32 currencyKey, uint _rateStalePeriod) internal view returns (bool) {
        // pUSD is a special case and is never stale (check before an SLOAD of getRateAndUpdatedTime)
        if (currencyKey == "pUSD") return false;

        return _rateIsStaleWithTime(_rateStalePeriod, _getUpdatedTime(currencyKey));
    }

    function _rateIsStaleWithTime(uint _rateStalePeriod, uint _time) internal view returns (bool) {
        return _time.add(_rateStalePeriod) < now;
    }

    function _rateIsFrozen(bytes32 currencyKey) internal view returns (bool) {
        InversePricing memory inverse = inversePricing[currencyKey];
        return inverse.frozenAtUpperLimit || inverse.frozenAtLowerLimit;
    }

    function _rateIsFlagged(bytes32 currencyKey, FlagsInterface flags) internal view returns (bool) {
        // pUSD is a special case and is never invalid
        if (currencyKey == "pUSD") return false;
        address aggregator = address(aggregators[currencyKey]);
        // when no aggregator or when the flags haven't been setup
        if (aggregator == address(0) || flags == FlagsInterface(0)) {
            return false;
        }
        return flags.getFlag(aggregator);
    }

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

    modifier onlyOracle {
        _onlyOracle();
        _;
    }

    function _onlyOracle() internal view {
        require(msg.sender == oracle, "Only the oracle can perform this action");
    }

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

    event OracleUpdated(address newOracle);
    event RatesUpdated(bytes32[] currencyKeys, uint[] newRates);
    event RateDeleted(bytes32 currencyKey);
    event InversePriceConfigured(bytes32 currencyKey, uint entryPoint, uint upperLimit, uint lowerLimit);
    event InversePriceFrozen(bytes32 currencyKey, uint rate, uint roundId, address initiator);
    event AggregatorAdded(bytes32 currencyKey, address aggregator);
    event AggregatorRemoved(bytes32 currencyKey, address aggregator);
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_resolver","type":"address"},{"internalType":"bytes32[]","name":"_currencyKeys","type":"bytes32[]"},{"internalType":"uint256[]","name":"_newRates","type":"uint256[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"address","name":"aggregator","type":"address"}],"name":"AggregatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"address","name":"aggregator","type":"address"}],"name":"AggregatorRemoved","type":"event"},{"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":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"entryPoint","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"upperLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lowerLimit","type":"uint256"}],"name":"InversePriceConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"address","name":"initiator","type":"address"}],"name":"InversePriceFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOracle","type":"address"}],"name":"OracleUpdated","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":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"RateDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"},{"indexed":false,"internalType":"uint256[]","name":"newRates","type":"uint256[]"}],"name":"RatesUpdated","type":"event"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"address","name":"aggregatorAddress","type":"address"}],"name":"addAggregator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"aggregatorKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aggregatorWarningFlags","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"aggregators","outputs":[{"internalType":"contract AggregatorV2V3Interface","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"}],"name":"anyRateIsInvalid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"canFreezeRate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"aggregator","type":"address"}],"name":"currenciesUsingAggregator","outputs":[{"internalType":"bytes32[]","name":"currencies","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"currencyByExternal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"currencyKeyDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"currentRoundForRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"deleteRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"effectiveValue","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"effectiveValueAndRates","outputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"sourceRate","type":"uint256"},{"internalType":"uint256","name":"destinationRate","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"roundIdForSrc","type":"uint256"},{"internalType":"uint256","name":"roundIdForDest","type":"uint256"}],"name":"effectiveValueAtRound","outputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"freezeRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"getCurrentRoundId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"uint256","name":"startingRoundId","type":"uint256"},{"internalType":"uint256","name":"startingTimestamp","type":"uint256"},{"internalType":"uint256","name":"timediff","type":"uint256"}],"name":"getLastRoundIdBeforeElapsedSecs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"inversePricing","outputs":[{"internalType":"uint256","name":"entryPoint","type":"uint256"},{"internalType":"uint256","name":"upperLimit","type":"uint256"},{"internalType":"uint256","name":"lowerLimit","type":"uint256"},{"internalType":"bool","name":"frozenAtUpperLimit","type":"bool"},{"internalType":"bool","name":"frozenAtLowerLimit","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"invertedKeys","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"lastRateUpdateTimes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"}],"name":"lastRateUpdateTimesForCurrencies","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"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":"nowPlusLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oracle","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":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateAndInvalid","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"bool","name":"isInvalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"uint256","name":"roundId","type":"uint256"}],"name":"rateAndTimestampAtRound","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateAndUpdatedTime","outputs":[{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateForCurrency","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateIsFlagged","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateIsFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateIsInvalid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"rateIsStale","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rateStalePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"}],"name":"ratesAndInvalidForCurrencies","outputs":[{"internalType":"uint256[]","name":"rates","type":"uint256[]"},{"internalType":"bool","name":"anyRateInvalid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"uint256","name":"numRounds","type":"uint256"}],"name":"ratesAndUpdatedTimeForCurrencyLastNRounds","outputs":[{"internalType":"uint256[]","name":"rates","type":"uint256[]"},{"internalType":"uint256[]","name":"times","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"}],"name":"ratesForCurrencies","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":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"removeAggregator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"removeInversePricing","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":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"roundFrozen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"_currencyKey","type":"bytes32"},{"internalType":"bool","name":"_set","type":"bool"}],"name":"setCurrencyToExternalAggregator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"uint256","name":"entryPoint","type":"uint256"},{"internalType":"uint256","name":"upperLimit","type":"uint256"},{"internalType":"uint256","name":"lowerLimit","type":"uint256"},{"internalType":"bool","name":"freezeAtUpperLimit","type":"bool"},{"internalType":"bool","name":"freezeAtLowerLimit","type":"bool"}],"name":"setInversePricing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"setOracle","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32[]","name":"currencyKeys","type":"bytes32[]"},{"internalType":"uint256[]","name":"newRates","type":"uint256[]"},{"internalType":"uint256","name":"timeSent","type":"uint256"}],"name":"updateRates","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506040516200517238038062005172833981810160405260a08110156200003757600080fd5b8151602083015160408085015160608601805192519496939591949391820192846401000000008211156200006b57600080fd5b9083019060208201858111156200008157600080fd5b82518660208202830111640100000000821117156200009f57600080fd5b82525081516020918201928201910280838360005b83811015620000ce578181015183820152602001620000b4565b5050505090500160405260200180516040519392919084640100000000821115620000f857600080fd5b9083019060208201858111156200010e57600080fd5b82518660208202830111640100000000821117156200012c57600080fd5b82525081516020918201928201910280838360005b838110156200015b57818101518382015260200162000141565b5050505090500160405250505082808660006001600160a01b0316816001600160a01b03161415620001d4576040805162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b038316908117825560408051928352602083019190915280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a150600280546001600160a01b0319166001600160a01b0392909216919091179055508051825114620002905760405162461bcd60e51b815260040180806020018281038252602f81526020018062005080602f913960400191505060405180910390fd5b83600560006101000a8154816001600160a01b0302191690836001600160a01b0316021790555062000346631c1554d160e21b736524b0ebc8baeb096031deb8243c5ce2585b669063907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156200030857600080fd5b505af41580156200031d573d6000803e3d6000fd5b505050506040513d60208110156200033457600080fd5b5051426001600160e01b036200036816565b6200035c8282426001600160e01b03620003e116565b50505050505062000c59565b6000838152600c60209081526040808320805460010190819055815180830183526001600160d81b03968716815264ffffffffff958616818501908152978552600484528285209185529252909120905181549451909216600160d81b029183166001600160d81b031990941693909317909116179055565b60008251845114620004255760405162461bcd60e51b8152600401808060200182810382526038815260200180620051016038913960400191505060405180910390fd5b610258420182106200047e576040805162461bcd60e51b815260206004820152601f60248201527f54696d6520697320746f6f2066617220696e746f207468652066757475726500604482015290519081900360640190fd5b60005b8451811015620005975760008582815181106200049a57fe5b60200260200101519050848281518110620004b157fe5b602002602001015160001415620004fa5760405162461bcd60e51b8152600401808060200182810382526039815260200180620051396039913960400191505060405180910390fd5b80631c1554d160e21b1415620005425760405162461bcd60e51b8152600401808060200182810382526031815260200180620050d06031913960400191505060405180910390fd5b62000556816001600160e01b036200066216565b8410156200056557506200058e565b6200058c818684815181106200057757fe5b6020026020010151866200036860201b60201c565b505b60010162000481565b507f1bc0fc8997efa076f59b5ef02c315bc5390f7a6d24d661ce12128c01a3b0ba578484604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101562000601578181015183820152602001620005e7565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156200064257818101518382015260200162000628565b5050505090500194505050505060405180910390a15060015b9392505050565b600062000678826001600160e01b036200068b16565b6020015164ffffffffff1690505b919050565b6200069562000c0f565b6000828152600760205260409020546001600160a01b0316620006c1836001600160e01b03620008f016565b15620008445760408051600481526024810182526020810180516001600160e01b0316633fabe5a360e21b1781529151815191926000926060926001600160a01b0387169286928291908083835b60208310620007305780518252601f1990920191602091820191016200070f565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d806000811462000792576040519150601f19603f3d011682016040523d82523d6000602084013e62000797565b606091505b509150915081156200083b5760008060008380602001905160a0811015620007be57600080fd5b50805160208201516060909201516040805180820190915291955091935090915080620008138b620007fa81876001600160e01b036200092c16565b6001600160501b0388166001600160e01b03620009f016565b6001600160d81b031681526020018264ffffffffff1681525097505050505050505062000686565b505050620008ea565b6000838152600c60205260409020546200085d62000c0f565b5060008481526004602090815260408083208484528252918290208251808401845290546001600160d81b038082168352600160d81b90910464ffffffffff1692820192909252825180840190935280519092918291620008c29189911686620009f0565b6001600160d81b03168152602001826020015164ffffffffff16815250935050505062000686565b50919050565b60008181526006602052604081205460ff161580156200092657506000828152600760205260409020546001600160a01b031615155b92915050565b60008082121562000984576040805162461bcd60e51b815260206004820152601b60248201527f4e656761746976652072617465206e6f7420737570706f727465640000000000604482015290519081900360640190fd5b60008381526008602052604090205460ff1615620008ea576000838152600860209081526040822054620009c89160129160ff16906200409862000b53821b17901c565b600a0a9050620009e7818462000bb160201b620038cd1790919060201c565b91505062000926565b6000620009fc62000c26565b506000848152600a6020908152604091829020825160a08101845281548082526001830154938201939093526002820154938101939093526003015460ff808216151560608501526101009091041615156080830152158062000a5d575083155b1562000a6d57839150506200065b565b6000858152600d602052604090205484925080841080159062000a91575081606001515b1562000aa4578160200151925062000b4a565b80841015801562000ab6575081608001515b1562000ac9578160400151925062000b4a565b600062000aea6002846000015162000bb160201b620038cd1790919060201c565b905085811162000afe576000935062000b1b565b62000b18868262000b5360201b620040981790919060201c565b93505b8260200151841062000b34578260200151935062000b48565b8260400151841162000b4857826040015193505b505b50509392505050565b60008282111562000bab576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60008262000bc25750600062000926565b8282028284828162000bd057fe5b04146200065b5760405162461bcd60e51b8152600401808060200182810382526021815260200180620050af6021913960400191505060405180910390fd5b604080518082019091526000808252602082015290565b6040518060a001604052806000815260200160008152602001600081526020016000151581526020016000151581525090565b6144178062000c696000396000f3fe608060405234801561001057600080fd5b50600436106103355760003560e01c8063728dec29116101b2578063af3aea86116100f9578063c8e5bbd5116100a2578063de02795e1161007c578063de02795e14610c8d578063de630ba714610caa578063eedb421e14610cb2578063fdadbc7e14610ccf57610335565b8063c8e5bbd514610b84578063c8e6f39514610c53578063ce8480ea14610c7057610335565b8063becb1954116100d3578063becb195414610a2d578063bfa005ce14610a52578063c2c8a67614610b1457610335565b8063af3aea861461099e578063b199c764146109bb578063b295ad34146109fa57610335565b80637f6e9d151161015b5780638da5cb5b116101355780638da5cb5b1461095c578063935f4abd14610964578063ac82f6081461098157610335565b80637f6e9d15146108f05780638295016a1461090d578063899ffef41461095457610335565b80637a018a1e1161018c5780637a018a1e146108985780637adbf973146108b55780637dc0d1d0146108e857610335565b8063728dec291461083c578063741853601461088857806379ba50971461089057610335565b80632bed9e0c116102815780634308a94f1161022a5780634f72def6116102045780634f72def6146107d157806353a47bb7146107ee578063654a60ac146107f65780637103353e1461081f57610335565b80634308a94f1461077657806345938849146107ac5780634c36b837146107c957610335565b80633375fcd11161025b5780633375fcd11461070357806338aa1b99146107205780633f0e084f1461073d57610335565b80632bed9e0c1461060d5780632d7371e11461062a5780632ea913d4146106e657610335565b8063109e46a2116102e3578063266da16b116102bd578063266da16b1461059d5780632678df96146105d25780632af64bd31461060557610335565b8063109e46a21461051c5780631627540c1461054b5780632528f0fe1461058057610335565b80630a7d36d1116103145780630a7d36d11461045c5780630c71cd23146104cc5780630ee4951b1461050257610335565b80629919c01461033a57806304f3bcec1461036b57806305a046e51461039c575b600080fd5b6103576004803603602081101561035057600080fd5b5035610cf2565b604080519115158252519081900360200190f35b610373610d0d565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b61040c600480360360208110156103b257600080fd5b8101906020810181356401000000008111156103cd57600080fd5b8201836020820111156103df57600080fd5b8035906020019184602083028401116401000000008311171561040157600080fd5b509092509050610d29565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610448578181015183820152602001610430565b505050509050019250505060405180910390f35b6103576004803603602081101561047257600080fd5b81019060208101813564010000000081111561048d57600080fd5b82018360208201111561049f57600080fd5b803590602001918460208302840111640100000000831117156104c157600080fd5b509092509050610dab565b6104e9600480360360208110156104e257600080fd5b5035610e5e565b6040805192835290151560208301528051918290030190f35b61050a610f24565b60408051918252519081900360200190f35b61050a6004803603608081101561053257600080fd5b5080359060208101359060408101359060600135610f34565b61057e6004803603602081101561056157600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610f77565b005b6103576004803603602081101561059657600080fd5b5035610ff8565b61050a600480360360a08110156105b357600080fd5b5080359060208101359060408101359060608101359060800135611018565b61040c600480360360208110156105e857600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611085565b610357611146565b61057e6004803603602081101561062357600080fd5b5035611290565b61064d6004803603604081101561064057600080fd5b50803590602001356113dc565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610691578181015183820152602001610679565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156106d05781810151838201526020016106b8565b5050505090500194505050505060405180910390f35b61050a600480360360208110156106fc57600080fd5b50356114cd565b6103576004803603602081101561071957600080fd5b50356114eb565b6103576004803603602081101561073657600080fd5b50356115b1565b61057e6004803603604081101561075357600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff166115bf565b6107936004803603602081101561078c57600080fd5b503561186f565b6040805192835260208301919091528051918290030190f35b61057e600480360360208110156107c257600080fd5b50356118bb565b6103736119a8565b61050a600480360360208110156107e757600080fd5b50356119b2565b6103736119bf565b61050a6004803603606081101561080c57600080fd5b50803590602081013590604001356119db565b6103736004803603602081101561083557600080fd5b50356119f3565b6108596004803603602081101561085257600080fd5b5035611a1b565b604080519586526020860194909452848401929092521515606084015215156080830152519081900360a00190f35b61057e611a4e565b61057e611c61565b61050a600480360360208110156108ae57600080fd5b5035611d5c565b61057e600480360360208110156108cb57600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16611d67565b610373611dee565b61050a6004803603602081101561090657600080fd5b5035611e0a565b6109366004803603606081101561092357600080fd5b5080359060208101359060400135611e1c565b60408051938452602084019290925282820152519081900360600190f35b61040c611e3c565b610373611eb8565b61050a6004803603602081101561097a57600080fd5b5035611ed4565b61050a6004803603602081101561099757600080fd5b5035611ee6565b610357600480360360208110156109b457600080fd5b5035611f15565b61057e600480360360c08110156109d157600080fd5b50803590602081013590604081013590606081013590608081013515159060a001351515611f20565b610a1760048036036020811015610a1057600080fd5b50356122f5565b6040805160ff9092168252519081900360200190f35b61057e60048036036040811015610a4357600080fd5b5080359060200135151561230a565b61035760048036036060811015610a6857600080fd5b810190602081018135640100000000811115610a8357600080fd5b820183602082011115610a9557600080fd5b80359060200191846020830284011164010000000083111715610ab757600080fd5b919390929091602081019035640100000000811115610ad557600080fd5b820183602082011115610ae757600080fd5b80359060200191846020830284011164010000000083111715610b0957600080fd5b919350915035612350565b61040c60048036036020811015610b2a57600080fd5b810190602081018135640100000000811115610b4557600080fd5b820183602082011115610b5757600080fd5b80359060200191846020830284011164010000000083111715610b7957600080fd5b5090925090506123d3565b610bf460048036036020811015610b9a57600080fd5b810190602081018135640100000000811115610bb557600080fd5b820183602082011115610bc757600080fd5b80359060200191846020830284011164010000000083111715610be957600080fd5b50909250905061244b565b604051808060200183151515158152602001828103825284818151815260200191508051906020019060200280838360005b83811015610c3e578181015183820152602001610c26565b50505050905001935050505060405180910390f35b61057e60048036036020811015610c6957600080fd5b50356125bf565b61050a60048036036020811015610c8657600080fd5b50356126cd565b61057e60048036036020811015610ca357600080fd5b50356126d8565b610936612900565b61035760048036036020811015610cc857600080fd5b503561290c565b61079360048036036040811015610ce557600080fd5b5080359060200135612921565b6000610d0582610d00612939565b612a0c565b90505b919050565b60025473ffffffffffffffffffffffffffffffffffffffff1681565b60608083839050604051908082528060200260200182016040528015610d59578160200160208202803883390190505b50905060005b83811015610da157610d82858583818110610d7657fe5b90506020020135612a57565b828281518110610d8e57fe5b6020908102919091010152600101610d5f565b5090505b92915050565b600080610db6612939565b90506060610df6858580806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612a7392505050565b905060005b84811015610e5257818181518110610e0f57fe5b602002602001015180610e395750610e39868683818110610e2c57fe5b9050602002013584612a0c565b15610e4a5760019350505050610da5565b600101610dfb565b50600095945050505050565b600080610e6961415f565b610e7284612d21565b9050837f70555344000000000000000000000000000000000000000000000000000000001415610ec557517affffffffffffffffffffffffffffffffffffffffffffffffffffff16915060009050610f1f565b8051610ee3610ed2612939565b836020015164ffffffffff1661300c565b80610efa5750610efa85610ef5613027565b6130c9565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff90911693509150505b915091565b6000610f2e612939565b90505b90565b600083815b610f468783600101613201565b915050801580610f57575083850181115b15610f6457509050610f6f565b600190910190610f39565b949350505050565b610f7f613471565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116811790915560408051918252517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229181900360200190a150565b600061100682610d00612939565b80610d055750610d0582610ef5613027565b60008386141561102957508361107c565b60006110358785613201565b50905060006110448685613201565b509050806110575760009250505061107c565b6110778161106b898563ffffffff6134c916565b9063ffffffff6134de16565b925050505b95945050505050565b6009546040805182815260208084028201019091526060916000919080156110b7578160200160208202803883390190505b50915060005b60095481101561113f576000600982815481106110d657fe5b6000918252602080832090910154808352600790915260409091205490915073ffffffffffffffffffffffffffffffffffffffff9081169086161415611136578084848060010195508151811061112957fe5b6020026020010181815250505b506001016110bd565b5050919050565b60006060611152611e3c565b905060005b815181101561128757600082828151811061116e57fe5b6020908102919091018101516000818152600383526040908190205460025482517f21f8a72100000000000000000000000000000000000000000000000000000000815260048101859052925193955073ffffffffffffffffffffffffffffffffffffffff918216949116926321f8a721926024808201939291829003018186803b1580156111fc57600080fd5b505afa158015611210573d6000803e3d6000fd5b505050506040513d602081101561122657600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614158061126d575060008181526003602052604090205473ffffffffffffffffffffffffffffffffffffffff16155b1561127e5760009350505050610f31565b50600101611157565b50600191505090565b611298613471565b60008181526007602052604090205473ffffffffffffffffffffffffffffffffffffffff168061130f576040805162461bcd60e51b815260206004820152601c60248201527f4e6f2061676772656761746f722065786973747320666f72206b657900000000604482015290519081900360640190fd5b600082815260076020908152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690556008909152812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905561137e8360096134f3565b905080156113d7576040805184815273ffffffffffffffffffffffffffffffffffffffff8416602082015281517fec70e890fc7db7de4059b114c9093a1f41283d18ffcfbcac45566feea4d4f777929181900390910190a15b505050565b60608082604051908082528060200260200182016040528015611409578160200160208202803883390190505b50915082604051908082528060200260200182016040528015611436578160200160208202803883390190505b5090506000611444856135d3565b905060005b848110156114c35761145b8683613201565b85838151811061146757fe5b6020026020010185848151811061147a57fe5b6020908102919091010191909152528161149657506114c69050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90910190600101611449565b50505b9250929050565b600b81815481106114da57fe5b600091825260209091200154905081565b60006114f5614176565b506000828152600a6020908152604091829020825160a08101845281548082526001830154938201939093526002820154938101939093526003015460ff8082161515606085015261010090910416151560808301521580611558575080606001515b80611564575080608001515b15611573576000915050610d08565b600061157e84611ee6565b90506000811180156115a257508160200151811015806115a2575081604001518111155b92505050610d08565b50919050565b6000610d0582610ef5613027565b6115c7613471565b600081905060008173ffffffffffffffffffffffffffffffffffffffff1663668a0f026040518163ffffffff1660e01b815260040160206040518083038186803b15801561161457600080fd5b505afa158015611628573d6000803e3d6000fd5b505050506040513d602081101561163e57600080fd5b50511015611693576040805162461bcd60e51b815260206004820152601b60248201527f476976656e2041676772656761746f7220697320696e76616c69640000000000604482015290519081900360640190fd5b60008173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156116db57600080fd5b505afa1580156116ef573d6000803e3d6000fd5b505050506040513d602081101561170557600080fd5b50519050601260ff8216111561174c5760405162461bcd60e51b81526004018080602001828103825260328152602001806143516032913960400191505060405180910390fd5b60008481526007602052604090205473ffffffffffffffffffffffffffffffffffffffff166117ab57600980546001810182556000919091527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af018490555b600084815260076020908152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8716908117909155600883529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff861617905580518781529182019290925281517f0bcae573430f69c5361e5d76534d3f61d2d803958778680cd74be9dc6299bc63929181900390910190a150505050565b60008061187a61415f565b61188384612d21565b80516020909101517affffffffffffffffffffffffffffffffffffffffffffffffffffff909116935064ffffffffff16915050915091565b6118c3613697565b60006118ce82611ee6565b11611920576040805162461bcd60e51b815260206004820152600c60248201527f52617465206973207a65726f0000000000000000000000000000000000000000604482015290519081900360640190fd5b6000818152600460209081526040808320600c808452828520805486529184528285208590559385905292825282547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01909255815183815291517fe69d655565c7ff1353d8eaeea62fb7904fa9696987431ec351be288c865f1ae19281900390910190a150565b6000610f2e613027565b600981815481106114da57fe5b60015473ffffffffffffffffffffffffffffffffffffffff1681565b60006119e88484846136ed565b509095945050505050565b60076020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b600a6020526000908152604090208054600182015460028301546003909301549192909160ff8082169161010090041685565b6060611a58611e3c565b905060005b8151811015611c5d576000828281518110611a7457fe5b602090810291909101810151600254604080517f5265736f6c766572206d697373696e67207461726765743a200000000000000081860152603980820185905282518083039091018152605982018084527fdacb2d01000000000000000000000000000000000000000000000000000000009052605d8201858152607d83019384528151609d840152815195975060009673ffffffffffffffffffffffffffffffffffffffff9095169563dacb2d01958995939492939260bd0191908501908083838c5b83811015611b50578181015183820152602001611b38565b50505050905090810190601f168015611b7d5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611b9b57600080fd5b505afa158015611baf573d6000803e3d6000fd5b505050506040513d6020811015611bc557600080fd5b505160008381526003602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915582518681529182015281519293507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68929081900390910190a15050600101611a5d565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611cb75760405162461bcd60e51b81526004018080602001828103825260358152602001806141e76035913960400191505060405180910390fd5b6000546001546040805173ffffffffffffffffffffffffffffffffffffffff938416815292909116602083015280517fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9281900390910190a160018054600080547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff841617909155169055565b6000610d05826135d3565b611d6f613471565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116919091179182905560408051929091168252517f3df77beb5db05fcdd70a30fc8adf3f83f9501b68579455adbd100b8180940394916020908290030190a150565b60055473ffffffffffffffffffffffffffffffffffffffff1681565b600d6020526000908152604090205481565b6000806000611e2c8686866136ed565b9250925092505b93509350939050565b606080611e4761373f565b60408051600180825281830190925291925060609190602080830190803883390190505090507f45786368616e676572000000000000000000000000000000000000000000000081600081518110611e9b57fe5b602002602001018181525050611eb1828261379e565b9250505090565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b600c6020526000908152604090205481565b6000611ef182612d21565b517affffffffffffffffffffffffffffffffffffffffffffffffffffff1692915050565b6000610d058261385a565b611f28613471565b60008311611f7d576040805162461bcd60e51b815260206004820152601a60248201527f6c6f7765724c696d6974206d7573742062652061626f76652030000000000000604482015290519081900360640190fd5b848411611fbb5760405162461bcd60e51b81526004018080602001828103825260278152602001806143bc6027913960400191505060405180910390fd5b611fcc85600263ffffffff6138cd16565b84106120095760405162461bcd60e51b815260040180806020018281038252602e815260200180614323602e913960400191505060405180910390fd5b8483106120475760405162461bcd60e51b81526004018080602001828103825260278152602001806142fc6027913960400191505060405180910390fd5b8180156120515750805b156120a3576040805162461bcd60e51b815260206004820152601c60248201527f43616e6e6f7420667265657a6520617420626f7468206c696d69747300000000604482015290519081900360640190fd5b6000868152600a6020526040902080546120ed57600b80546001810182556000919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9018790555b858155600181018590556002810184905582806121075750815b156121dc576003810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016841515177fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010084151502179055600061216f886135d3565b6000898152600d6020526040902081905590507ff72828471e37526c68fd812a1fa6eeff993c3f81bd96c0242dc5b3e144145df088856121af57866121b1565b875b604080519283526020830191909152818101849052336060830152519081900360800190a150612216565b6003810180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690556000878152600d60205260408120555b600061222188611ee6565b905080156122a457612231613926565b73ffffffffffffffffffffffffffffffffffffffff166357630a4089836040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b15801561228b57600080fd5b505af115801561229f573d6000803e3d6000fd5b505050505b60408051898152602081018990528082018890526060810187905290517f37efb38e92b0f94698f6df0c9070e2f00946862a042ac09e34ae8c547684240a9181900360800190a15050505050505050565b60086020526000908152604090205460ff1681565b612312613471565b60009182526006602052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055565b600061235a613697565b6123c986868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a02828101820190935289825290935089925088918291850190849080828437600092019190915250879250613951915050565b9695505050505050565b60608083839050604051908082528060200260200182016040528015612403578160200160208202803883390190505b50905060005b83811015610da15761242c85858381811061242057fe5b90506020020135611ee6565b82828151811061243857fe5b6020908102919091010152600101612409565b606060008383905060405190808252806020026020018201604052801561247c578160200160208202803883390190505b5091506000612489612939565b905060606124c9868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250612a7392505050565b905060005b858110156125b5576124de61415f565b6124f98888848181106124ed57fe5b90506020020135612d21565b905080600001517affffffffffffffffffffffffffffffffffffffffffffffffffffff1686838151811061252957fe5b60200260200101818152505084158015612575575087878381811061254a57fe5b905060200201357f705553440000000000000000000000000000000000000000000000000000000014155b156125ac5782828151811061258657fe5b6020026020010151806125a957506125a984826020015164ffffffffff1661300c565b94505b506001016124ce565b5050509250929050565b6125c7613471565b6000818152600a6020526040902054612627576040805162461bcd60e51b815260206004820152601860248201527f4e6f20696e766572746564207072696365206578697374730000000000000000604482015290519081900360640190fd5b6000818152600a60205260408120818155600181018290556002810182905560030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016905561267a82600b6134f3565b90508015611c5d5760408051838152600060208201819052818301819052606082015290517f37efb38e92b0f94698f6df0c9070e2f00946862a042ac09e34ae8c547684240a9181900360800190a15050565b6000610d0582612a57565b6000818152600a602052604090208054612739576040805162461bcd60e51b815260206004820152601e60248201527f43616e6e6f7420667265657a65206e6f6e2d696e766572736520726174650000604482015290519081900360640190fd5b600381015460ff1615801561275857506003810154610100900460ff16155b6127a9576040805162461bcd60e51b815260206004820152601a60248201527f546865207261746520697320616c72656164792066726f7a656e000000000000604482015290519081900360640190fd5b60006127b483611ee6565b90506000811180156127d857508160010154811015806127d8575081600201548111155b156128b357600182015460038301805460028501548414610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090921693851493909317169190911790556000612851846135d3565b6000858152600d6020908152604091829020839055815187815290810185905280820183905233606082015290519192507ff72828471e37526c68fd812a1fa6eeff993c3f81bd96c0242dc5b3e144145df0919081900360800190a1506113d7565b6040805162461bcd60e51b815260206004820152601260248201527f526174652077697468696e20626f756e64730000000000000000000000000000604482015290519081900360640190fd5b42610258808201909192565b60066020526000908152604090205460ff1681565b60008061292e8484613201565b915091509250929050565b6000612943613bc5565b73ffffffffffffffffffffffffffffffffffffffff166323257c2b7f53797374656d53657474696e67730000000000000000000000000000000000007f726174655374616c65506572696f6400000000000000000000000000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b1580156129db57600080fd5b505afa1580156129ef573d6000803e3d6000fd5b505050506040513d6020811015612a0557600080fd5b5051905090565b6000827f70555344000000000000000000000000000000000000000000000000000000001415612a3e57506000610da5565b612a5082612a4b85612a57565b61300c565b9392505050565b6000612a6282612d21565b6020015164ffffffffff1692915050565b60606000612a7f613027565b905073ffffffffffffffffffffffffffffffffffffffff811615612cee5760608351604051908082528060200260200182016040528015612aca578160200160208202803883390190505b50905060005b8451811015612b595760076000868381518110612ae957fe5b6020026020010151815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828281518110612b2c57fe5b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152600101612ad0565b506040517f7d723cac00000000000000000000000000000000000000000000000000000000815260206004820181815283516024840152835173ffffffffffffffffffffffffffffffffffffffff861693637d723cac93869392839260440191808601910280838360005b83811015612bdc578181015183820152602001612bc4565b505050509050019250505060006040518083038186803b158015612bff57600080fd5b505afa158015612c13573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526020811015612c5a57600080fd5b8101908080516040519392919084640100000000821115612c7a57600080fd5b908301906020820185811115612c8f57600080fd5b8251866020820283011164010000000082111715612cac57600080fd5b82525081516020918201928201910280838360005b83811015612cd9578181015183820152602001612cc1565b505050509050016040525050509250506115ab565b8251604051908082528060200260200182016040528015612d19578160200160208202803883390190505b509392505050565b612d2961415f565b60008281526007602052604090205473ffffffffffffffffffffffffffffffffffffffff16612d5783613bf0565b15612f2b5760408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167ffeaf968c0000000000000000000000000000000000000000000000000000000017815291518151919260009260609273ffffffffffffffffffffffffffffffffffffffff87169286928291908083835b60208310612e1c57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612ddf565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114612e7c576040519150601f19603f3d011682016040523d82523d6000602084013e612e81565b606091505b50915091508115612f235760008060008380602001905160a0811015612ea657600080fd5b50805160208201516060909201516040805180820190915291955091935090915080612ee88b612ed68187613c35565b8769ffffffffffffffffffff16613cde565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff1681526020018264ffffffffff16815250975050505050505050610d08565b5050506115ab565b6000838152600c6020526040902054612f4261415f565b5060008481526004602090815260408083208484528252918290208251808401845290547affffffffffffffffffffffffffffffffffffffffffffffffffffff80821683527b0100000000000000000000000000000000000000000000000000000090910464ffffffffff1692820192909252825180840190935280519092918291612fd19189911686613cde565b7affffffffffffffffffffffffffffffffffffffffffffffffffffff168152602001826020015164ffffffffff168152509350505050610d08565b60004261301f838563ffffffff613e1c16565b109392505050565b6000613031613bc5565b73ffffffffffffffffffffffffffffffffffffffff16639ee5955a7f53797374656d53657474696e67730000000000000000000000000000000000007f61676772656761746f725761726e696e67466c616773000000000000000000006040518363ffffffff1660e01b8152600401808381526020018281526020019250505060206040518083038186803b1580156129db57600080fd5b6000827f705553440000000000000000000000000000000000000000000000000000000014156130fb57506000610da5565b60008381526007602052604090205473ffffffffffffffffffffffffffffffffffffffff16801580613141575073ffffffffffffffffffffffffffffffffffffffff8316155b15613150576000915050610da5565b8273ffffffffffffffffffffffffffffffffffffffff1663357e47fe826040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b1580156131cd57600080fd5b505afa1580156131e1573d6000803e3d6000fd5b505050506040513d60208110156131f757600080fd5b5051949350505050565b600082815260076020526040812054819073ffffffffffffffffffffffffffffffffffffffff1661323185613bf0565b156133c95760408051602480820187905282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f9a6fc8f50000000000000000000000000000000000000000000000000000000017815291518151919260009260609273ffffffffffffffffffffffffffffffffffffffff87169286928291908083835b6020831061330557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016132c8565b6001836020036101000a038019825116818451168082178552505050505050905001915050600060405180830381855afa9150503d8060008114613365576040519150601f19603f3d011682016040523d82523d6000602084013e61336a565b606091505b509150915081156133c1576000808280602001905160a081101561338d57600080fd5b50602081015160609091015190925090506133b28a6133ac8185613c35565b8b613cde565b975095506114c6945050505050565b505050613469565b6133d161415f565b5060008581526004602090815260408083208784528252918290208251808401909352547affffffffffffffffffffffffffffffffffffffffffffffffffffff81168084527b0100000000000000000000000000000000000000000000000000000090910464ffffffffff169183019190915261345090879087613cde565b60209091015190935064ffffffffff1691506114c69050565b509250929050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146134c75760405162461bcd60e51b815260040180806020018281038252602f815260200180614243602f913960400191505060405180910390fd5b565b6000612a508383670de0b6b3a7640000613e76565b6000612a508383670de0b6b3a7640000613eb3565b6000805b82548110156135c9578383828154811061350d57fe5b906000526020600020015414156135c15782818154811061352a57fe5b6000918252602082200155825483907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811061356557fe5b906000526020600020015483828154811061357c57fe5b60009182526020909120015582546135b6847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83016141a9565b506001915050610da5565b6001016134f7565b5060009392505050565b60008181526007602052604081205473ffffffffffffffffffffffffffffffffffffffff1661360183613bf0565b15613681578073ffffffffffffffffffffffffffffffffffffffff1663668a0f026040518163ffffffff1660e01b815260040160206040518083038186803b15801561364c57600080fd5b505afa158015613660573d6000803e3d6000fd5b505050506040513d602081101561367657600080fd5b50519150610d089050565b50506000818152600c6020526040902054610d08565b60055473ffffffffffffffffffffffffffffffffffffffff1633146134c75760405162461bcd60e51b815260040180806020018281038252602781526020018061421c6027913960400191505060405180910390fd5b60008060006136fb86611ee6565b91508386141561370f575083915080611e33565b61371884611ee6565b90508015611e33576137348161106b878563ffffffff6134c916565b925093509350939050565b604080516001808252818301909252606091602080830190803883390190505090507f466c657869626c6553746f7261676500000000000000000000000000000000008160008151811061378f57fe5b60200260200101818152505090565b606081518351016040519080825280602002602001820160405280156137ce578160200160208202803883390190505b50905060005b8351811015613810578381815181106137e957fe5b60200260200101518282815181106137fd57fe5b60209081029190910101526001016137d4565b5060005b82518110156138535782818151811061382957fe5b602002602001015182828651018151811061384057fe5b6020908102919091010152600101613814565b5092915050565b6000613864614176565b506000828152600a6020908152604091829020825160a0810184528154815260018201549281019290925260028101549282019290925260039091015460ff808216151560608401819052610100909204161515608083015280612a5057506080015192915050565b6000826138dc57506000610da5565b828202828482816138e957fe5b0414612a505760405162461bcd60e51b81526004018080602001828103825260218152602001806142726021913960400191505060405180910390fd5b6000610f2e7f45786368616e6765720000000000000000000000000000000000000000000000613ee5565b600082518451146139935760405162461bcd60e51b81526004018080602001828103825260388152602001806142c46038913960400191505060405180910390fd5b610258420182106139eb576040805162461bcd60e51b815260206004820152601f60248201527f54696d6520697320746f6f2066617220696e746f207468652066757475726500604482015290519081900360640190fd5b60005b8451811015613aff576000858281518110613a0557fe5b60200260200101519050848281518110613a1b57fe5b602002602001015160001415613a625760405162461bcd60e51b81526004018080602001828103825260398152602001806143836039913960400191505060405180910390fd5b807f70555344000000000000000000000000000000000000000000000000000000001415613ac15760405162461bcd60e51b81526004018080602001828103825260318152602001806142936031913960400191505060405180910390fd5b613aca81612a57565b841015613ad75750613af7565b613af581868481518110613ae757fe5b602002602001015186613fdb565b505b6001016139ee565b507f1bc0fc8997efa076f59b5ef02c315bc5390f7a6d24d661ce12128c01a3b0ba578484604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613b67578181015183820152602001613b4f565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613ba6578181015183820152602001613b8e565b5050505090500194505050505060405180910390a15060019392505050565b6000610f2e7f466c657869626c6553746f726167650000000000000000000000000000000000613ee5565b60008181526006602052604081205460ff16158015610d0557505060009081526007602052604090205473ffffffffffffffffffffffffffffffffffffffff16151590565b600080821215613c8c576040805162461bcd60e51b815260206004820152601b60248201527f4e656761746976652072617465206e6f7420737570706f727465640000000000604482015290519081900360640190fd5b60008381526008602052604090205460ff16156115ab57600083815260086020526040812054613cc19060129060ff16614098565b600a0a9050613cd6838263ffffffff6138cd16565b915050610da5565b6000613ce8614176565b506000848152600a6020908152604091829020825160a08101845281548082526001830154938201939093526002820154938101939093526003015460ff8082161515606085015261010090910416151560808301521580613d48575083155b15613d565783915050612a50565b6000858152600d6020526040902054849250808410801590613d79575081606001515b15613d8a5781602001519250613e13565b808410158015613d9b575081608001515b15613dac5781604001519250613e13565b8151600090613dc290600263ffffffff6138cd16565b9050858111613dd45760009350613de7565b613de4818763ffffffff61409816565b93505b82602001518410613dfe5782602001519350613e11565b82604001518411613e1157826040015193505b505b50509392505050565b600082820183811015612a50576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b600080600a8304613e8d868663ffffffff6138cd16565b81613e9457fe5b0490506005600a825b0610613ea757600a015b600a9004949350505050565b600080613ed984613ecd87600a870263ffffffff6138cd16565b9063ffffffff6140f516565b90506005600a82613e9d565b60008181526003602090815260408083205481517f4d697373696e6720616464726573733a2000000000000000000000000000000093810193909352603180840186905282518085039091018152605190930190915273ffffffffffffffffffffffffffffffffffffffff1690816138535760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613fa0578181015183820152602001613f88565b50505050905090810190601f168015613fcd5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6000838152600c60209081526040808320805460010190819055815180830183527affffffffffffffffffffffffffffffffffffffffffffffffffffff968716815264ffffffffff9586168185019081529785526004845282852091855292529091209051815494519092167b01000000000000000000000000000000000000000000000000000000029183167fffffffffff00000000000000000000000000000000000000000000000000000090941693909317909116179055565b6000828211156140ef576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b600080821161414b576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b600082848161415657fe5b04949350505050565b604080518082019091526000808252602082015290565b6040518060a001604052806000815260200160008152602001600081526020016000151581526020016000151581525090565b8154818355818111156113d7576000838152602090206113d7918101908301610f3191905b808211156141e257600081556001016141ce565b509056fe596f75206d757374206265206e6f6d696e61746564206265666f726520796f752063616e20616363657074206f776e6572736869704f6e6c7920746865206f7261636c652063616e20706572666f726d207468697320616374696f6e4f6e6c792074686520636f6e7472616374206f776e6572206d617920706572666f726d207468697320616374696f6e536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7752617465206f6620705553442063616e6e6f7420626520757064617465642c206974277320616c7761797320554e49542e43757272656e6379206b6579206172726179206c656e677468206d757374206d61746368207261746573206172726179206c656e6774682e6c6f7765724c696d6974206d7573742062652062656c6f772074686520656e747279506f696e7475707065724c696d6974206d757374206265206c657373207468616e20646f75626c6520656e747279506f696e7441676772656761746f7220646563696d616c732073686f756c64206265206c6f776572206f7220657175616c20746f2031385a65726f206973206e6f7420612076616c696420726174652c20706c656173652063616c6c2064656c6574655261746520696e73746561642e75707065724c696d6974206d7573742062652061626f76652074686520656e747279506f696e74a265627a7a72315820aad6e657c9957994f42fe5d035b09b42c3a1504f66917e9ad3d3f3e18eecf9e964736f6c6343000510003243757272656e6379206b6579206c656e67746820616e642072617465206c656e677468206d757374206d617463682e536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7752617465206f6620705553442063616e6e6f7420626520757064617465642c206974277320616c7761797320554e49542e43757272656e6379206b6579206172726179206c656e677468206d757374206d61746368207261746573206172726179206c656e6774682e5a65726f206973206e6f7420612076616c696420726174652c20706c656173652063616c6c2064656c6574655261746520696e73746561642e000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324000000000000000000000000bfe5a66062589452331acce2a589c75efcd3bd3e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

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

000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324000000000000000000000000bfe5a66062589452331acce2a589c75efcd3bd3e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _owner (address): 0x166220468aff631290b7e1ddff7f45b052de8324
Arg [1] : _oracle (address): 0x166220468aff631290b7e1ddff7f45b052de8324
Arg [2] : _resolver (address): 0xbfe5a66062589452331acce2a589c75efcd3bd3e

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324
Arg [1] : 000000000000000000000000166220468aff631290b7e1ddff7f45b052de8324
Arg [2] : 000000000000000000000000bfe5a66062589452331acce2a589c75efcd3bd3e
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [4] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000


Library Used


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading