Sepolia Testnet

Contract

0xAC873015EbEd44711B65C762d35b94DC8b6d30eE

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Accept Ownership59471492024-05-21 9:41:48113 days ago1716284508IN
0xAC873015...C8b6d30eE
0 ETH0.0026986495.355281
Nominate New Own...59471482024-05-21 9:41:36113 days ago1716284496IN
0xAC873015...C8b6d30eE
0 ETH0.0046168297.7065334
0x6080604050281992024-01-05 18:01:00250 days ago1704477660IN
 Create: ExchangerWithFeeRecAlternativesLightChain
0 ETH0.1567735733.59778294

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
66740072024-09-11 20:10:241 hr ago1726085424
0xAC873015...C8b6d30eE
0.00033441 ETH
66740072024-09-11 20:10:241 hr ago1726085424
0xAC873015...C8b6d30eE
0.00033441 ETH
66739932024-09-11 20:06:361 hr ago1726085196
0xAC873015...C8b6d30eE
0.00033441 ETH
66739182024-09-11 19:47:121 hr ago1726084032
0xAC873015...C8b6d30eE
0.00033441 ETH
66739182024-09-11 19:47:121 hr ago1726084032
0xAC873015...C8b6d30eE
0.00033441 ETH
66738942024-09-11 19:41:121 hr ago1726083672
0xAC873015...C8b6d30eE
0.00033441 ETH
66738942024-09-11 19:41:121 hr ago1726083672
0xAC873015...C8b6d30eE
0.00033441 ETH
66738482024-09-11 19:29:481 hr ago1726082988
0xAC873015...C8b6d30eE
0.00033441 ETH
66738482024-09-11 19:29:481 hr ago1726082988
0xAC873015...C8b6d30eE
0.00033441 ETH
66738402024-09-11 19:27:361 hr ago1726082856
0xAC873015...C8b6d30eE
0.00033441 ETH
66738402024-09-11 19:27:361 hr ago1726082856
0xAC873015...C8b6d30eE
0.00033441 ETH
66737872024-09-11 19:14:121 hr ago1726082052
0xAC873015...C8b6d30eE
0.00033441 ETH
66737872024-09-11 19:14:121 hr ago1726082052
0xAC873015...C8b6d30eE
0.00033441 ETH
66737752024-09-11 19:11:362 hrs ago1726081896
0xAC873015...C8b6d30eE
0.00033441 ETH
66737752024-09-11 19:11:362 hrs ago1726081896
0xAC873015...C8b6d30eE
0.00033441 ETH
66737292024-09-11 18:59:242 hrs ago1726081164
0xAC873015...C8b6d30eE
0.00033441 ETH
66737292024-09-11 18:59:242 hrs ago1726081164
0xAC873015...C8b6d30eE
0.00033441 ETH
66737212024-09-11 18:57:482 hrs ago1726081068
0xAC873015...C8b6d30eE
0.00033441 ETH
66737212024-09-11 18:57:482 hrs ago1726081068
0xAC873015...C8b6d30eE
0.00033441 ETH
66737182024-09-11 18:57:002 hrs ago1726081020
0xAC873015...C8b6d30eE
0.00033441 ETH
66737182024-09-11 18:57:002 hrs ago1726081020
0xAC873015...C8b6d30eE
0.00033441 ETH
66737072024-09-11 18:54:482 hrs ago1726080888
0xAC873015...C8b6d30eE
0.00033441 ETH
66737072024-09-11 18:54:482 hrs ago1726080888
0xAC873015...C8b6d30eE
0.00033441 ETH
66736892024-09-11 18:50:242 hrs ago1726080624
0xAC873015...C8b6d30eE
0.00033441 ETH
66736892024-09-11 18:50:242 hrs ago1726080624
0xAC873015...C8b6d30eE
0.00033441 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ExchangerWithFeeRecAlternativesLightChain

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 20 : ExchangerWithFeeRecAlternativesLightChain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Inheritance
import "./Exchanger.sol";

// Internal references
// import "./MinimalProxyFactory.sol";
import "../interfaces/IAddressResolver.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IExchanger.sol";

contract ExchangerWithFeeRecAlternativesLightChain is Exchanger {
    // bytes32 public constant override CONTRACT_NAME = "ExchangerWithFeeRecAlternatives";

    using SafeMath for uint256;

    struct ExchangeVolumeAtPeriod {
        uint64 time;
        uint192 volume;
    }

    ExchangeVolumeAtPeriod public lastAtomicVolume;
    uint16 internal constant PT_EXCHANGE = 5;

    constructor(
        address _owner,
        address _resolver
    )
        // MinimalProxyFactory()
        Exchanger(_owner, _resolver)
    {}

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

    function getAmountsForAtomicExchange(
        uint256 sourceAmount,
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) external view returns (uint256 amountReceived, uint256 fee, uint256 exchangeFeeRate) {
        (amountReceived, fee, exchangeFeeRate, , , ) = _getAmountsForAtomicExchangeMinusFees(
            sourceAmount,
            sourceCurrencyKey,
            destinationCurrencyKey
        );
    }

    function getSendExchangeGasFee(
        address _account,
        bytes32 _sourceKey,
        uint256 _sourceAmount,
        bytes32 _destKey,
        bytes32 _bridgeName,
        uint16 _destChainId
    ) external view returns (uint256) {
        uint256 amountReceived;
        uint256 fee;

        (amountReceived, fee, , , , ) = _getAmountsForAtomicExchangeMinusFees(_sourceAmount, _sourceKey, _destKey);
        bytes memory lzPayload = abi.encode(
            PT_EXCHANGE,
            abi.encodePacked(_account),
            _sourceKey,
            _sourceAmount,
            _destKey,
            amountReceived,
            fee,
            _destChainId
        );

        return synthrBridge(_bridgeName).calcFee(lzPayload, PT_EXCHANGE, _destChainId);
    }

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

    function exchangeAtomically(
        uint256 minAmount,
        IExchanger.ExchangeArgs calldata args,
        bytes32 bridgeName
    ) external payable onlyWrappedSynthrorSynth returns (uint256 amountReceived) {
        (amountReceived, ) = _exchangeAtomically(args, bridgeName);

        require(amountReceived >= minAmount, "The amount received is below the minimum amount specified.");
    }

    function _exchangeAtomically(
        IExchanger.ExchangeArgs memory _args,
        bytes32 bridgeName
    ) internal returns (uint256 amountReceived, uint256 fee) {
        IExchanger.ExchangeArgs memory args = _args;
        _ensureCanExchange(args.sourceCurrencyKey, args.sourceAmount, args.destCurrencyKey);
        require(!exchangeRates().synthTooVolatileForAtomicExchange(args.sourceCurrencyKey), "Src synth value is volatile.");
        require(!exchangeRates().synthTooVolatileForAtomicExchange(args.destCurrencyKey), "Dest synth value is volatile.");

        (args.sourceAmount, args.reclaimed, args.refunded) = _settleAndCalcSourceAmountRemaining(
            args.sourceAmount,
            args.fromAccount,
            args.sourceCurrencyKey
        );

        // If, after settlement the user has no balance left (highly unlikely), then return to prevent
        // emitting events of 0 and don't revert so as to ensure the settlement queue is emptied
        if (args.sourceAmount == 0) {
            return (0, 0);
        }

        uint256 exchangeFeeRate;
        uint256 systemConvertedAmount;
        uint256 systemSourceRate;
        uint256 systemDestinationRate;

        // Note: also ensures the given synths are allowed to be atomically exchanged
        (
            amountReceived, // output amount with fee taken out (denominated in dest currency)
            fee, // fee amount (denominated in dest currency)
            exchangeFeeRate, // applied fee rate
            systemConvertedAmount, // current system value without fees (denominated in dest currency)
            systemSourceRate, // current system rate for src currency
            systemDestinationRate // current system rate for dest currency
        ) = _getAmountsForAtomicExchangeMinusFees(args.sourceAmount, args.sourceCurrencyKey, args.destCurrencyKey);

        args.destAmount = amountReceived;
        args.fee = fee;

        // SIP-65: Decentralized Circuit Breaker (checking current system rates)
        if (_exchangeRatesCircuitBroken(args.sourceCurrencyKey, args.destCurrencyKey)) {
            return (0, 0);
        }

        // Sanity check atomic output's value against current system value (checking atomic rates)
        require(
            !exchangeCircuitBreaker().isDeviationAboveThreshold(systemConvertedAmount, amountReceived.add(fee)),
            "Atomic rate deviates too much"
        );

        // Determine sUSD value of exchange
        uint256 sourceSusdValue;
        if (args.sourceCurrencyKey == sUSD) {
            // Use after-settled amount as this is amount converted (not sourceAmount)
            sourceSusdValue = _args.sourceAmount;
        } else if (args.destCurrencyKey == sUSD) {
            // In this case the systemConvertedAmount would be the fee-free sUSD value of the source synth
            sourceSusdValue = systemConvertedAmount;
        } else {
            // Otherwise, convert source to sUSD value
            (uint256 amountReceivedInUSD, uint256 sUsdFee, , , , ) = _getAmountsForAtomicExchangeMinusFees(
                _args.sourceAmount,
                args.sourceCurrencyKey,
                sUSD
            );
            sourceSusdValue = amountReceivedInUSD.add(sUsdFee);
        }

        // Check and update atomic volume limit
        _checkAndUpdateAtomicVolume(sourceSusdValue);

        // Note: We don't need to check their balance as the _convert() below will do a safe subtraction which requires
        // the subtraction to not overflow, which would happen if their balance is not sufficient.
        _convert(args, bridgeName);

        // Let the DApps know there was a Synth exchange
        emit SynthExchange(
            args.fromAccount,
            args.destAccount,
            args.sourceCurrencyKey,
            args.sourceAmount,
            args.destCurrencyKey,
            amountReceived,
            args.fee,
            args.destChainId
        );
        // Emit separate event to track atomic exchanges
        emit AtomicSynthExchange(
            args.fromAccount,
            args.destAccount,
            args.sourceCurrencyKey,
            args.sourceAmount,
            args.destCurrencyKey,
            amountReceived,
            args.fee,
            args.destChainId
        );
    }

    function _checkAndUpdateAtomicVolume(uint256 sourceSusdValue) internal {
        uint256 currentVolume = uint256(lastAtomicVolume.time) == block.timestamp
            ? uint256(lastAtomicVolume.volume).add(sourceSusdValue)
            : sourceSusdValue;
        require(currentVolume <= getAtomicMaxVolumePerBlock(), "Surpassed volume limit");
        lastAtomicVolume.time = uint64(block.timestamp);
        lastAtomicVolume.volume = uint192(currentVolume); // Protected by volume limit check above
    }

    function _feeRateForAtomicExchange(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) internal view returns (uint256) {
        // Get the exchange fee rate as per source and destination currencyKey
        uint256 baseRate = getAtomicExchangeFeeRate(sourceCurrencyKey).add(getAtomicExchangeFeeRate(destinationCurrencyKey));
        if (baseRate == 0) {
            // If no atomic rate was set, fallback to the regular exchange rate
            baseRate = getExchangeFeeRate(sourceCurrencyKey).add(getExchangeFeeRate(destinationCurrencyKey));
        }

        return baseRate;
    }

    function _getAmountsForAtomicExchangeMinusFees(
        uint256 sourceAmount,
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    )
        internal
        view
        returns (
            uint256 amountReceived,
            uint256 fee,
            uint256 exchangeFeeRate,
            uint256 systemConvertedAmount,
            uint256 systemSourceRate,
            uint256 systemDestinationRate
        )
    {
        uint256 destinationAmount;
        (destinationAmount, systemConvertedAmount, systemSourceRate, systemDestinationRate) = exchangeRates()
            .effectiveAtomicValueAndRates(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);

        exchangeFeeRate = _feeRateForAtomicExchange(sourceCurrencyKey, destinationCurrencyKey);
        amountReceived = _deductFeesFromAmount(destinationAmount, exchangeFeeRate);
        fee = destinationAmount.sub(amountReceived);
    }

    event AtomicSynthExchange(
        address indexed account,
        address indexed toAddress,
        bytes32 fromCurrencyKey,
        uint256 fromAmount,
        bytes32 toCurrencyKey,
        uint256 toAmount,
        uint256 fee,
        uint16 destChainId
    );
}

File 2 of 20 : SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, 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;
    }
}

File 3 of 20 : IAddressResolver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IAddressResolver {
    function getAddress(bytes32 name) external view returns (address);

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

    function getAvailableBridge(bytes32 bridgeName) external view returns (address);

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

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

File 4 of 20 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC20 {
    // ERC20 Optional Views
    function name() external view returns (string memory);

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

    function decimals() external view returns (uint8);

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

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

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

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

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

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

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

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

File 5 of 20 : IExchangeCircuitBreaker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IExchangeCircuitBreaker {
    // Views

    function exchangeRates() external view returns (address);

    function rateWithInvalid(bytes32 currencyKey) external view returns (uint256, bool);

    function priceDeviationThresholdFactor() external view returns (uint256);

    function isDeviationAboveThreshold(uint256 base, uint256 comparison) external view returns (bool);

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

    // Mutative functions
    function resetLastExchangeRate(bytes32[] calldata currencyKeys) external;

    function rateWithBreakCircuit(bytes32 currencyKey) external returns (uint256 lastValidRate, bool circuitBroken);
}

File 6 of 20 : IExchanger.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IExchanger {
    struct ExchangeEntrySettlement {
        bytes32 src;
        uint256 amount;
        bytes32 dest;
        uint256 reclaim;
        uint256 rebate;
        uint256 srcRoundIdAtPeriodEnd;
        uint256 destRoundIdAtPeriodEnd;
        uint256 timestamp;
    }

    struct ExchangeEntry {
        uint256 sourceRate;
        uint256 destinationRate;
        uint256 destinationAmount;
        uint256 exchangeFeeRate;
        uint256 exchangeDynamicFeeRate;
        uint256 roundIdForSrc;
        uint256 roundIdForDest;
    }

    struct ExchangeArgs {
        address fromAccount;
        address destAccount;
        bytes32 sourceCurrencyKey;
        bytes32 destCurrencyKey;
        uint256 sourceAmount;
        uint256 destAmount;
        uint256 fee;
        uint256 reclaimed;
        uint256 refunded;
        uint16 destChainId;
        bool erc20Payment;
    }

    // Views
    function calculateAmountAfterSettlement(
        address from,
        bytes32 currencyKey,
        uint256 amount,
        uint256 refunded
    ) external view returns (uint256 amountAfterSettlement);

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

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

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

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

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

    function dynamicFeeRateForExchange(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) external view returns (uint256 feeRate, bool tooVolatile);

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

    // function priceDeviationThresholdFactor() external view returns (uint256);

    // function waitingPeriodSecs() external view returns (uint256);

    // function lastExchangeRate(bytes32 currencyKey) external view returns (uint256);

    // Mutative functions
    function exchange(ExchangeArgs calldata args, bytes32 bridgeName) external payable returns (uint256 amountReceived);

    function exchangeAtomically(
        uint256 minAmount,
        ExchangeArgs calldata args,
        bytes32 bridgeName
    ) external payable returns (uint256 amountReceived);

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

    function suspendSynthWithInvalidRate(bytes32 currencyKey) external;

    function updateDestinationForExchange(address recipient, bytes32 destinationKey, uint256 destinationAmount) external;
}

File 7 of 20 : IExchangeRates.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IExchangeRates {
    // Structs
    struct RateAndUpdatedTime {
        uint216 rate;
        uint40 time;
    }

    // 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 anyRateIsInvalidAtRound(bytes32[] calldata currencyKeys, uint256[] calldata roundIds) external view returns (bool);

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

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

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

    function effectiveValueAndRatesAtRound(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    ) external view returns (uint256 value, uint256 sourceRate, uint256 destinationRate);

    function effectiveAtomicValueAndRates(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey
    ) external view returns (uint256 value, uint256 systemValue, uint256 systemSourceRate, uint256 systemDestinationRate);

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

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

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

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

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

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

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

    function rateIsFlagged(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 (uint256);

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

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

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

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

    function rateWithSafetyChecks(bytes32 currencyKey) external returns (uint256 rate, bool broken, bool invalid);
}

File 8 of 20 : IExchangeState.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IExchangeState {
    // Views
    struct ExchangeEntry {
        bytes32 src;
        uint256 amount;
        bytes32 dest;
        uint256 amountReceived;
        uint256 exchangeFeeRate;
        uint256 timestamp;
        uint256 roundIdForSrc;
        uint256 roundIdForDest;
    }

    function getLengthOfEntries(address account, bytes32 currencyKey) external view returns (uint256);

    function getEntryAt(
        address account,
        bytes32 currencyKey,
        uint256 index
    )
        external
        view
        returns (
            bytes32 src,
            uint256 amount,
            bytes32 dest,
            uint256 amountReceived,
            uint256 exchangeFeeRate,
            uint256 timestamp,
            uint256 roundIdForSrc,
            uint256 roundIdForDest
        );

    function getMaxTimestamp(address account, bytes32 currencyKey) external view returns (uint256);

    // Mutative functions
    function appendExchangeEntry(
        address account,
        bytes32 src,
        uint256 amount,
        bytes32 dest,
        uint256 amountReceived,
        uint256 exchangeFeeRate,
        uint256 timestamp,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    ) external;

    function removeEntries(address account, bytes32 currencyKey) external;
}

File 9 of 20 : IFlexibleStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IFlexibleStorage {
    // Views
    function getUIntValue(bytes32 contractName, bytes32 record) external view returns (uint256);

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

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

    function getIntValues(bytes32 contractName, bytes32[] calldata records) external view returns (int256[] 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, uint256 value) external;

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

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

    function setIntValues(bytes32 contractName, bytes32[] calldata records, int256[] 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;
}

File 10 of 20 : IIssuer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/ISynth.sol";

interface IIssuer {
    // Views

    function allNetworksDebtInfo() external view returns (uint256 debt, uint256 sharesSupply);

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

    function availableSynthCount() external view returns (uint256);

    function availableSynths(uint256 index) external view returns (ISynth);

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

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

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

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

    function debtBalanceOf(address issuer) external view returns (uint256 debtBalance);

    function issuanceRatio() external view returns (uint256);

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

    function maxIssuableSynths(address issuer) external view returns (uint256 maxIssuable);

    function minimumStakeTime() external view returns (uint256);

    function remainingIssuableSynths(
        address issuer
    ) external view returns (uint256 maxIssuable, uint256 alreadyIssued, uint256 totalSystemDebt);

    function synths(bytes32 currencyKey) external view returns (ISynth);

    function getSynths(bytes32[] calldata currencyKeys) external view returns (ISynth[] memory);

    function synthsByAddress(address synthAddress) external view returns (bytes32);

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

    function checkFreeCollateral(
        address _issuer,
        bytes32 _collateralKey,
        uint16 _chainId
    ) external view returns (uint256 withdrawableSynthr);

    function issueSynths(
        address from,
        uint256 amount,
        uint256 destChainId
    ) external returns (uint256 synthAmount, uint256 debtShare);

    function issueMaxSynths(address from, uint256 destChainId) external returns (uint256 synthAmount, uint256 debtShare);

    function burnSynths(
        address from,
        bytes32 synthKey,
        uint256 amount
    ) external returns (uint256 synthAmount, uint256 debtShare, uint256 reclaimed, uint256 refunded);

    function burnSynthsToTarget(
        address from,
        bytes32 synthKey
    ) external returns (uint256 synthAmount, uint256 debtShare, uint256 reclaimed, uint256 refunded);

    function burnForRedemption(address deprecatedSynthProxy, address account, uint256 balance) external;

    function burnSynthsWithoutDebt(bytes32 currencyKey, address from, uint amount) external returns (uint256 burnAmount);

    function synthIssueFromSynthrSwap(address _account, bytes32 _synthKey, uint256 _synthAmount) external;

    function liquidateAccount(
        address account,
        bytes32 collateralKey,
        uint16 chainId,
        bool isSelfLiquidation
    ) external returns (uint256 totalRedeemed, uint256 amountToLiquidate, uint256 sharesToRemove);

    function destIssue(address _account, bytes32 _synthKey, uint256 _synthAmount) external;

    function destBurn(address _account, bytes32 _synthKey, uint256 _synthAmount) external returns (uint256);

    function transferMargin(address account, uint256 marginDelta) external returns (uint256);

    function destTransferMargin(address _account, uint256 _marginDelta, bytes32 _marketKey) external returns (bool);

    function setCurrentPeriodId(uint128 periodId) external;
}

File 11 of 20 : ISynth.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ISynth {
    // Views
    function balanceOf(address _account) external view returns (uint256);

    function currencyKey() external view returns (bytes32);

    function transferableSynths(address account) external view returns (uint256);

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

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

    function burn(address account, uint256 amount) external;

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

File 12 of 20 : ISynthrBridgeLightChain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IExchanger.sol";

interface ISynthrBridge {
    /* ========== MUTATIVE FUNCTIONS ========== */
    function sendDepositCollateral(address account, bytes32 collateralKey, uint256 amount, bool erc20Payment) external payable;

    function sendBurn(address account, bytes32 synthKey, uint256 amount, bool erc20Payment) external payable;

    function sendExchange(
        address account,
        bytes32 srcSynthKey,
        bytes32 dstSynthKey,
        uint256 srcAmount,
        uint256 dstAmount,
        uint256 reclaimed,
        uint256 refunded,
        uint256 fee,
        uint16 dstChainId,
        bool erc20Payment
    ) external payable;

    function sendBridgeSyToken(
        address account,
        bytes32 synthKey,
        uint256 amount,
        uint16 dstChainId,
        bool erc20Payment
    ) external payable;

    function sendTransferMargin(address account, uint256 amount, bytes32 marketKey, bool erc20Payment) external payable;

    function sendCrossSwapSyAssetToNative(
        address account,
        bytes32 srcKey,
        uint256 srcAmount,
        bytes32 dstKey,
        uint256 dstAmount,
        uint16 dstChainId,
        uint256 fee,
        bytes calldata dexPayload,
        address dexAddress,
        bool erc20Payment
    ) external payable;

    function sendCrossSwapNativeToSyAsset(
        address account,
        bytes32 srcKey,
        uint256 srcAmount,
        bytes32 dstKey,
        uint256 dstAmount,
        uint16 dstChainId,
        uint256 fee,
        bool erc20Payment
    ) external payable;

    function sendCrossSwapNativeToNative(
        address account,
        bytes32 srcKey,
        uint256 srcAmount,
        bytes32 dstKey,
        uint256 dstAmount,
        uint16 dstChainId,
        uint256 fee,
        address dexAddress,
        bytes calldata dexPayload,
        bool erc20Payment
    ) external payable;

    function sendCrossSwapSyAssetToNativeWithDex(
        address account,
        bytes32 srcKey,
        uint256 srcAmount,
        bytes32 dstKey,
        uint256 dstAmount,
        uint16 dstChainId,
        uint256 fee,
        bool erc20Payment
    ) external payable;

    function sendCrossSwapNativeToNativeWithDex(
        address account,
        bytes32 srcKey,
        uint256 srcAmount,
        bytes32 dstKey,
        uint256 dstAmount,
        uint16 dstChainId,
        uint256 fee,
        bool erc20Payment
    ) external payable;

    // function sendExchange(IExchanger.ExchangeArgs calldata args) external payable;

    function calcFee(bytes memory lzPayload, uint16 packetType, uint16 dstChainId) external view returns (uint256 lzFee);
}

File 13 of 20 : ISystemStatus.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ISystemStatus {
    struct Status {
        bool canSuspend;
        bool canResume;
    }

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

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

    function requireSystemActive() external view;

    function systemSuspended() external view returns (bool);

    function requireIssuanceActive() external view;

    function requireExchangeActive() external view;

    function requireFuturesActive() external view;

    function requireFuturesMarketActive(bytes32 marketKey) external view;

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

    function requireSynthActive(bytes32 currencyKey) external view;

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

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

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

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

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

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

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

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

    function futuresMarketSuspension(bytes32 marketKey) external view returns (bool suspended, uint248 reason);

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

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

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

    // Restricted functions
    function suspendIssuance(uint256 reason) external;

    function suspendSynth(bytes32 currencyKey, uint256 reason) external;

    function suspendFuturesMarket(bytes32 marketKey, uint256 reason) external;

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

File 14 of 20 : IWrappedSynthr.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./ISynth.sol";

interface IWrappedSynthr {
    // Views
    function isWaitingPeriod(bytes32 currencyKey) external view returns (bool);

    function chainBalanceOf(address account, uint16 _chainId) external view returns (uint256);

    function chainBalanceOfPerKey(address _account, bytes32 _collateralKey, uint16 _chainId) external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function collateralCurrency(bytes32 _collateralKey) external view returns (address);

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

    // Mutative Functions
    function burnSynths(uint256 amount, bytes32 synthKey) external;

    function withdrawCollateral(bytes32 collateralKey, uint256 collateralAmount) external;

    function burnSynthsToTarget(bytes32 synthKey) external;

    function destBurn(address _account, bytes32 _synthKey, uint256 _synthAmount) external;

    function exchange(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        uint16 destChainId
    ) external returns (uint256 amountReceived);

    function exchangeWithTracking(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        address rewardAddress,
        bytes32 trackingCode,
        uint16 destChainId
    ) external payable returns (uint256 amountReceived);

    // function exchangeWithTrackingForInitiator(
    //     bytes32 sourceCurrencyKey,
    //     uint256 sourceAmount,
    //     bytes32 destinationCurrencyKey,
    //     address rewardAddress,
    //     bytes32 trackingCode,
    //     uint16 destChainId
    // ) external payable returns (uint256 amountReceived);

    function exchangeOnBehalfWithTracking(
        address exchangeForAddress,
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        address rewardAddress,
        bytes32 trackingCode,
        uint16 destChainId
    ) external returns (uint256 amountReceived);

    function exchangeAtomically(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        bytes32 trackingCode,
        uint256 minAmount,
        uint16 destChainId
    ) external payable returns (uint256 amountReceived);

    function issueMaxSynths(uint16 destChainId) external payable;

    function issueSynths(bytes32 currencyKey, uint256 amount, uint256 synthToMint, uint16 destChainId) external payable;

    // Liquidations
    function liquidateDelinquentAccount(address account, bytes32 collateralKey) external returns (bool);

    function liquidateSelf(bytes32 collateralKey) external returns (bool);
}

File 15 of 20 : AddressResolverLightChain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Inheritance
import "./Owned.sol";
import "../interfaces/IAddressResolver.sol";

// Internal references
import "../interfaces/IIssuer.sol";
import "./MixinResolver.sol";

contract AddressResolverLightChain is Owned, IAddressResolver {
    mapping(bytes32 => address) public repository;
    mapping(bytes32 => address) public availableBridge;
    mapping(address => bool) public isBridge;

    bytes32[] public bridgeList;

    constructor(address _owner) Owned(_owner) {}

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

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

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

    function addAvailableBridge(bytes32 bridgeName, address bridgeAddress) external onlyOwner {
        _addAvailableBridge(bridgeName, bridgeAddress);
    }

    function removeAvailableBridge(bytes32 bridgeName) external onlyOwner {
        _removeAvailableBridge(bridgeName);
    }

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

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

    /* ========== PRIVATE FUNCTIONS ========== */
    function _addAvailableBridge(bytes32 bridgeName, address bridgeAddress) private {
        if (availableBridge[bridgeName] != address(0)) {
            _removeAvailableBridge(bridgeName);
        }
        availableBridge[bridgeName] = bridgeAddress;
        isBridge[bridgeAddress] = true;
        bridgeList.push(bridgeName);
        emit AddBridge(bridgeName, bridgeAddress);
    }

    function _removeAvailableBridge(bytes32 bridgeName) private {
        require(availableBridge[bridgeName] != address(0), "The bridge no exist.");
        uint lastBridgeNumber = bridgeList.length - 1;
        for (uint ii = 0; ii <= lastBridgeNumber; ii++) {
            if (bridgeList[ii] == bridgeName) {
                bridgeList[ii] = bridgeList[lastBridgeNumber];
                bridgeList.pop();
                break;
            }
        }
        address bridgeToRemove = availableBridge[bridgeName];
        delete availableBridge[bridgeName];
        delete isBridge[bridgeToRemove];
        emit RemoveBridge(bridgeName, bridgeToRemove);
    }

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

    function areAddressesImported(bytes32[] calldata names, address[] calldata destinations) external view returns (bool) {
        for (uint256 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 getSynth(bytes32 key) external view returns (address) {
        IIssuer issuer = IIssuer(repository["Issuer"]);
        require(address(issuer) != address(0), "Cannot find Issuer address");
        return address(issuer.synths(key));
    }

    function getAvailableBridge(bytes32 bridgeName) external view returns (address) {
        return availableBridge[bridgeName];
    }

    function getBridgeList() external view returns (bytes32[] memory) {
        return bridgeList;
    }

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

    event AddressImported(bytes32 name, address destination);
    event AddBridge(bytes32 indexed bridgeName, address bridgeAddress);
    event RemoveBridge(bytes32 indexed bridgeName, address bridgeAddress);
}

File 16 of 20 : Exchanger.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
import "./Owned.sol";
import "./MixinResolver.sol";
import "./MixinSystemSettings.sol";
import "../interfaces/IExchanger.sol";

// Libraries
import "./SafeDecimalMath.sol";

// Internal references
import "../interfaces/ISystemStatus.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IExchangeState.sol";
import "../interfaces/IExchangeRates.sol";
import "../interfaces/IExchangeCircuitBreaker.sol";
import "../interfaces/IWrappedSynthr.sol";
import "../interfaces/IIssuer.sol";
import "../interfaces/ISynthrBridgeLightChain.sol";

contract Exchanger is Owned, MixinSystemSettings {
    using SafeMath for uint256;
    using SafeDecimalMath for uint256;

    bytes32 public constant CONTRACT_NAME = "Exchanger";

    bytes32 internal constant sUSD = "sUSD";

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

    bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
    bytes32 private constant CONTRACT_EXCHANGESTATE = "ExchangeState";
    bytes32 private constant CONTRACT_EXRATES = "ExchangeRates";
    bytes32 private constant CONTRACT_WRAPPED_SYNTHR = "WrappedSynthr";
    bytes32 private constant CONTRACT_ISSUER = "Issuer";
    bytes32 private constant CONTRACT_CIRCUIT_BREAKER = "ExchangeCircuitBreaker";

    constructor(address _owner, address _resolver) Owned(_owner) MixinSystemSettings(_resolver) {}

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

    function resolverAddressesRequired() public view override returns (bytes32[] memory addresses) {
        bytes32[] memory existingAddresses = MixinSystemSettings.resolverAddressesRequired();
        bytes32[] memory newAddresses = new bytes32[](6);
        newAddresses[0] = CONTRACT_SYSTEMSTATUS;
        newAddresses[1] = CONTRACT_EXCHANGESTATE;
        newAddresses[2] = CONTRACT_EXRATES;
        newAddresses[3] = CONTRACT_WRAPPED_SYNTHR;
        newAddresses[4] = CONTRACT_ISSUER;
        newAddresses[5] = CONTRACT_CIRCUIT_BREAKER;
        addresses = combineArrays(existingAddresses, newAddresses);
    }

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

    function exchangeState() internal view returns (IExchangeState) {
        return IExchangeState(requireAndGetAddress(CONTRACT_EXCHANGESTATE));
    }

    function exchangeRates() internal view returns (IExchangeRates) {
        return IExchangeRates(requireAndGetAddress(CONTRACT_EXRATES));
    }

    function synthrBridge(bytes32 bridgeName) internal view returns (ISynthrBridge) {
        return ISynthrBridge(resolver.getAvailableBridge(bridgeName));
    }

    function exchangeCircuitBreaker() internal view returns (IExchangeCircuitBreaker) {
        return IExchangeCircuitBreaker(requireAndGetAddress(CONTRACT_CIRCUIT_BREAKER));
    }

    function wrappedSynthr() internal view returns (IWrappedSynthr) {
        return IWrappedSynthr(requireAndGetAddress(CONTRACT_WRAPPED_SYNTHR));
    }

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

    function maxSecsLeftInWaitingPeriod(address account, bytes32 currencyKey) public view returns (uint256) {
        return secsLeftInWaitingPeriodForExchange(exchangeState().getMaxTimestamp(account, currencyKey));
    }

    function settlementOwing(
        address account,
        bytes32 currencyKey
    ) public view returns (uint256 reclaimAmount, uint256 rebateAmount, uint256 numEntries) {
        (reclaimAmount, rebateAmount, numEntries, ) = _settlementOwing(account, currencyKey);
    }

    // Internal function to aggregate each individual rebate and reclaim entry for a synth
    function _settlementOwing(
        address account,
        bytes32 currencyKey
    )
        internal
        view
        returns (uint256 reclaimAmount, uint256 rebateAmount, uint256 numEntries, IExchanger.ExchangeEntrySettlement[] memory)
    {
        // Need to sum up all reclaim and rebate amounts for the user and the currency key
        numEntries = exchangeState().getLengthOfEntries(account, currencyKey);

        // For each unsettled exchange

        IExchanger.ExchangeEntrySettlement[] memory settlements = new IExchanger.ExchangeEntrySettlement[](numEntries);
        for (uint256 i = 0; i < numEntries; i++) {
            uint256 reclaims;
            uint256 rebate;
            // fetch the entry from storage

            IExchangeState.ExchangeEntry memory exchangeEntry = _getExchangeEntry(account, currencyKey, i);

            // determine the last round ids for src and dest pairs when period ended or latest if not over
            (uint256 srcRoundIdAtPeriodEnd, uint256 destRoundIdAtPeriodEnd) = getRoundIdsAtPeriodEnd(exchangeEntry);

            // given these round ids, determine what effective value they should have received
            (uint256 destinationAmount, , ) = exchangeRates().effectiveValueAndRatesAtRound(
                exchangeEntry.src,
                exchangeEntry.amount,
                exchangeEntry.dest,
                srcRoundIdAtPeriodEnd,
                destRoundIdAtPeriodEnd
            );

            // and deduct the fee from this amount using the exchangeFeeRate from storage
            uint256 amountShouldHaveReceived = _deductFeesFromAmount(destinationAmount, exchangeEntry.exchangeFeeRate);

            // SIP-65 settlements where the amount at end of waiting period is beyond the threshold, then
            // settle with no reclaim or rebate
            bool sip65condition = exchangeCircuitBreaker().isDeviationAboveThreshold(
                exchangeEntry.amountReceived,
                amountShouldHaveReceived
            );
            if (!sip65condition) {
                if (exchangeEntry.amountReceived > amountShouldHaveReceived) {
                    // if they received more than they should have, add to the reclaim tally
                    reclaims = exchangeEntry.amountReceived.sub(amountShouldHaveReceived);
                    reclaimAmount = reclaimAmount.add(reclaims);
                } else if (amountShouldHaveReceived > exchangeEntry.amountReceived) {
                    // if less, add to the rebate tally
                    rebate = amountShouldHaveReceived.sub(exchangeEntry.amountReceived);
                    rebateAmount = rebateAmount.add(rebate);
                }
            }

            settlements[i] = IExchanger.ExchangeEntrySettlement({
                src: exchangeEntry.src,
                amount: exchangeEntry.amount,
                dest: exchangeEntry.dest,
                reclaim: reclaims,
                rebate: rebate,
                srcRoundIdAtPeriodEnd: srcRoundIdAtPeriodEnd,
                destRoundIdAtPeriodEnd: destRoundIdAtPeriodEnd,
                timestamp: exchangeEntry.timestamp
            });
        }

        return (reclaimAmount, rebateAmount, numEntries, settlements);
    }

    function _getExchangeEntry(
        address account,
        bytes32 currencyKey,
        uint256 index
    ) internal view returns (IExchangeState.ExchangeEntry memory) {
        (
            bytes32 src,
            uint256 amount,
            bytes32 dest,
            uint256 amountReceived,
            uint256 exchangeFeeRate,
            uint256 timestamp,
            uint256 roundIdForSrc,
            uint256 roundIdForDest
        ) = exchangeState().getEntryAt(account, currencyKey, index);

        return
            IExchangeState.ExchangeEntry({
                src: src,
                amount: amount,
                dest: dest,
                amountReceived: amountReceived,
                exchangeFeeRate: exchangeFeeRate,
                timestamp: timestamp,
                roundIdForSrc: roundIdForSrc,
                roundIdForDest: roundIdForDest
            });
    }

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

    function calculateAmountAfterSettlement(
        address from,
        bytes32 currencyKey,
        uint256 amount,
        uint256 refunded
    ) public view returns (uint256 amountAfterSettlement) {
        amountAfterSettlement = amount;

        // balance of a synth will show an amount after settlement
        uint256 balanceOfSourceAfterSettlement = IERC20(address(issuer().synths(currencyKey))).balanceOf(from);

        if (refunded > 0) {
            amountAfterSettlement = amountAfterSettlement.add(refunded);
        }
        // when there isn't enough supply (either due to reclamation settlement or because the number is too high)
        if (amountAfterSettlement > balanceOfSourceAfterSettlement) {
            // then the amount to exchange is reduced to their remaining supply
            amountAfterSettlement = balanceOfSourceAfterSettlement;
        }
    }

    function isSynthRateInvalid(bytes32 currencyKey) external view returns (bool) {
        (, bool invalid) = exchangeCircuitBreaker().rateWithInvalid(currencyKey);
        return invalid;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */
    function exchange(
        IExchanger.ExchangeArgs calldata args,
        bytes32 bridgeName
    ) external payable onlyWrappedSynthrorSynth returns (uint256 amountReceived) {
        (amountReceived, ) = _exchange(args, bridgeName);
    }

    function updateDestinationForExchange(
        address recipient,
        bytes32 destinationKey,
        uint256 destinationAmount
    ) public onlySynthrBridge {
        require(destinationKey != bytes32(0), "dest key didn't set.");
        ISynth dest = issuer().synths(destinationKey);
        dest.issue(recipient, destinationAmount);
        emit DestIssueForExchange(recipient, destinationKey, destinationAmount);
    }

    function _settleAndCalcSourceAmountRemaining(
        uint256 sourceAmount,
        address from,
        bytes32 sourceCurrencyKey
    ) internal returns (uint256 sourceAmountAfterSettlement, uint256 reclaimed, uint256 refunded) {
        uint256 numEntriesSettled;
        (reclaimed, refunded, numEntriesSettled) = _internalSettle(from, sourceCurrencyKey);

        sourceAmountAfterSettlement = sourceAmount;

        // when settlement was required
        if (numEntriesSettled > 0) {
            // ensure the sourceAmount takes this into account
            sourceAmountAfterSettlement = calculateAmountAfterSettlement(from, sourceCurrencyKey, sourceAmount, refunded);
        }
    }

    function _exchange(
        IExchanger.ExchangeArgs memory args,
        bytes32 bridgeName
    ) internal returns (uint256 amountReceived, uint256 fee) {
        require(args.sourceAmount > 0, "Zero amount");

        // Using struct to resolve stack too deep error
        IExchanger.ExchangeEntry memory entry;

        entry.roundIdForSrc = exchangeRates().getCurrentRoundId(args.sourceCurrencyKey);
        entry.roundIdForDest = exchangeRates().getCurrentRoundId(args.destCurrencyKey);

        (args.sourceAmount, args.reclaimed, args.refunded) = _settleAndCalcSourceAmountRemaining(
            args.sourceAmount,
            args.fromAccount,
            args.sourceCurrencyKey
        );

        // If, after settlement the user has no balance left (highly unlikely), then return to prevent
        // emitting events of 0 and don't revert so as to ensure the settlement queue is emptied
        if (args.sourceAmount == 0) {
            return (0, 0);
        }

        (entry.destinationAmount, entry.sourceRate, entry.destinationRate) = exchangeRates().effectiveValueAndRatesAtRound(
            args.sourceCurrencyKey,
            args.sourceAmount,
            args.destCurrencyKey,
            entry.roundIdForSrc,
            entry.roundIdForDest
        );

        _ensureCanExchangeAtRound(args.sourceCurrencyKey, args.destCurrencyKey, entry.roundIdForSrc, entry.roundIdForDest);

        // SIP-65: Decentralized Circuit Breaker
        // mutative call to suspend system if the rate is invalid
        if (_exchangeRatesCircuitBroken(args.sourceCurrencyKey, args.destCurrencyKey)) {
            return (0, 0);
        }

        bool tooVolatile;
        (entry.exchangeFeeRate, tooVolatile) = _feeRateForExchangeAtRounds(
            args.sourceCurrencyKey,
            args.destCurrencyKey,
            entry.roundIdForSrc,
            entry.roundIdForDest
        );

        if (tooVolatile) {
            // do not exchange if rates are too volatile, this to prevent charging
            // dynamic fees that are over the max value
            return (0, 0);
        }

        amountReceived = _deductFeesFromAmount(entry.destinationAmount, entry.exchangeFeeRate);
        // Note: `fee` is denominated in the destinationCurrencyKey.
        fee = entry.destinationAmount.sub(amountReceived);

        args.destAmount = amountReceived;
        args.fee = fee;

        // Note: We don't need to check their balance as the _convert() below will do a safe subtraction which requires
        // the subtraction to not overflow, which would happen if their balance is not sufficient.
        _convert(args, bridgeName);

        // Let the DApps know there was a Synth exchange
        emit SynthExchange(
            args.fromAccount,
            args.destAccount,
            args.sourceCurrencyKey,
            args.sourceAmount,
            args.destCurrencyKey,
            amountReceived,
            args.fee,
            args.destChainId
        );

        // Emit separate event to track tracking exchanges
        emit TrackingSynthExchange(
            args.fromAccount,
            args.destAccount,
            args.sourceCurrencyKey,
            args.sourceAmount,
            args.destCurrencyKey,
            amountReceived,
            args.fee,
            args.destChainId
        );

        // if the waiting period is gt 0
        if (getWaitingPeriodSecs() > 0) {
            // persist the exchange information for the dest key
            appendExchange(
                args.destAccount,
                args.sourceCurrencyKey,
                args.sourceAmount,
                args.destCurrencyKey,
                amountReceived,
                entry.exchangeFeeRate
            );
        }
    }

    // SIP-65: Decentralized Circuit Breaker
    function _exchangeRatesCircuitBroken(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) internal returns (bool circuitBroken) {
        // check both currencies unless they're sUSD, since its rate is never invalid (gas savings)
        if (sourceCurrencyKey != sUSD) {
            (, circuitBroken) = exchangeCircuitBreaker().rateWithBreakCircuit(sourceCurrencyKey);
        }

        if (destinationCurrencyKey != sUSD) {
            // we're not skipping the suspension check if the circuit was broken already
            // this is not terribly important, but is more consistent (so that results don't
            // depend on which synth is source and which is destination)
            bool destCircuitBroken;
            (, destCircuitBroken) = exchangeCircuitBreaker().rateWithBreakCircuit(destinationCurrencyKey);
            circuitBroken = circuitBroken || destCircuitBroken;
        }
    }

    function _convert(
        IExchanger.ExchangeArgs memory args, // uint256 sourceAmountAfterSettlement, // uint256 amountReceived, // uint256 fee,
        bytes32 bridgeName
    ) internal {
        // Burn the source amount
        issuer().synths(args.sourceCurrencyKey).burn(args.fromAccount, args.sourceAmount);

        if (args.destChainId == 0) {
            ISynth dest = issuer().synths(args.destCurrencyKey);

            dest.issue(args.destAccount, args.destAmount);
        }
        if (args.fee > 0 && args.destCurrencyKey != sUSD) {
            // Normalize fee to sUSD
            // Note: `fee` is being reused to avoid stack too deep errors.
            args.fee = exchangeRates().effectiveValue(args.destCurrencyKey, args.fee, sUSD);
        }

        synthrBridge(bridgeName).sendExchange{value: msg.value}(
            args.fromAccount,
            args.sourceCurrencyKey,
            args.destCurrencyKey,
            args.sourceAmount,
            args.destAmount,
            args.reclaimed,
            args.refunded,
            args.fee,
            args.destChainId,
            args.erc20Payment
        );
    }

    // Note: this function can intentionally be called by anyone on behalf of anyone else (the caller just pays the gas)
    function settle(
        address from,
        bytes32 currencyKey
    ) external returns (uint256 reclaimed, uint256 refunded, uint256 numEntriesSettled) {
        systemStatus().requireSynthActive(currencyKey);
        return _internalSettle(from, currencyKey);
    }

    function suspendSynthWithInvalidRate(bytes32 currencyKey) external {
        systemStatus().requireSystemActive();
        // SIP-65: Decentralized Circuit Breaker
        (, bool circuitBroken) = exchangeCircuitBreaker().rateWithBreakCircuit(currencyKey);
        require(circuitBroken, "Synth price is valid");
    }

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

    function _ensureCanExchange(bytes32 sourceCurrencyKey, uint256 sourceAmount, bytes32 destinationCurrencyKey) internal view {
        // require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");
        require(sourceAmount > 0, "Zero amount");

        bytes32[] memory synthKeys = new bytes32[](2);
        synthKeys[0] = sourceCurrencyKey;
        synthKeys[1] = destinationCurrencyKey;
        require(!exchangeRates().anyRateIsInvalid(synthKeys), "src/dest value is invalid.");
    }

    function _ensureCanExchangeAtRound(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    ) internal view {
        // require(sourceCurrencyKey != destinationCurrencyKey, "Can't be same synth");

        bytes32[] memory synthKeys = new bytes32[](2);
        synthKeys[0] = sourceCurrencyKey;
        synthKeys[1] = destinationCurrencyKey;

        uint256[] memory roundIds = new uint256[](2);
        roundIds[0] = roundIdForSrc;
        roundIds[1] = roundIdForDest;
        require(!exchangeRates().anyRateIsInvalidAtRound(synthKeys, roundIds), "src/dest value is invalid.");
    }

    function _internalSettle(
        address from,
        bytes32 currencyKey
    ) internal returns (uint256 reclaimed, uint256 refunded, uint256 numEntriesSettled) {
        require(maxSecsLeftInWaitingPeriod(from, currencyKey) == 0, "Cannot settle during waiting period");

        (
            uint256 reclaimAmount,
            uint256 rebateAmount,
            uint256 entries,
            IExchanger.ExchangeEntrySettlement[] memory settlements
        ) = _settlementOwing(from, currencyKey);

        if (reclaimAmount > rebateAmount) {
            reclaimed = reclaimAmount.sub(rebateAmount);
            reclaim(from, currencyKey, reclaimed);
        } else if (rebateAmount > reclaimAmount) {
            refunded = rebateAmount.sub(reclaimAmount);
            refund(from, currencyKey, refunded);
        }

        // // by checking a reclaim or refund we also check that the currency key is still a valid synth,
        // // as the deviation check will return 0 if the synth has been removed.
        // if (updateCache && (reclaimed > 0 || refunded > 0)) {
        //     bytes32[] memory key = new bytes32[](1);
        //     key[0] = currencyKey;
        //     debtCache().updateCachedSynthDebts(key);
        // }

        // emit settlement event for each settled exchange entry
        for (uint256 i = 0; i < settlements.length; i++) {
            emit ExchangeEntrySettled(
                from,
                settlements[i].src,
                settlements[i].amount,
                settlements[i].dest,
                settlements[i].reclaim,
                settlements[i].rebate,
                settlements[i].srcRoundIdAtPeriodEnd,
                settlements[i].destRoundIdAtPeriodEnd,
                settlements[i].timestamp
            );
        }

        numEntriesSettled = entries;

        // Now remove all entries, even if no reclaim and no rebate
        exchangeState().removeEntries(from, currencyKey);
    }

    function reclaim(address from, bytes32 currencyKey, uint256 amount) internal {
        // burn amount from user
        issuer().synths(currencyKey).burn(from, amount);
        emit ExchangeReclaim(from, currencyKey, amount);
    }

    function refund(address from, bytes32 currencyKey, uint256 amount) internal {
        // issue amount to user
        issuer().synths(currencyKey).issue(from, amount);
        emit ExchangeRebate(from, currencyKey, amount);
    }

    function secsLeftInWaitingPeriodForExchange(uint256 timestamp) internal view returns (uint256) {
        uint256 _waitingPeriodSecs = getWaitingPeriodSecs();
        if (timestamp == 0 || block.timestamp >= timestamp.add(_waitingPeriodSecs)) {
            return 0;
        }

        return timestamp.add(_waitingPeriodSecs).sub(block.timestamp);
    }

    /* ========== Exchange Related Fees ========== */
    /// @notice public function to get the total fee rate for a given exchange
    /// @param sourceCurrencyKey The source currency key
    /// @param destinationCurrencyKey The destination currency key
    /// @return The exchange fee rate, and whether the rates are too volatile
    function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view returns (uint256) {
        (uint256 feeRate, bool tooVolatile) = _feeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);
        require(!tooVolatile, "too volatile");
        return feeRate;
    }

    /// @notice public function to get the dynamic fee rate for a given exchange
    /// @param sourceCurrencyKey The source currency key
    /// @param destinationCurrencyKey The destination currency key
    /// @return feeRate The exchange dynamic fee rate
    /// @return tooVolatile if rates are too volatile
    function dynamicFeeRateForExchange(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) external view returns (uint256 feeRate, bool tooVolatile) {
        return _dynamicFeeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);
    }

    /// @notice Calculate the exchange fee for a given source and destination currency key
    /// @param sourceCurrencyKey The source currency key
    /// @param destinationCurrencyKey The destination currency key
    /// @return feeRate The exchange fee rate
    /// @return tooVolatile The exchange dynamic fee rate and if rates are too volatile
    function _feeRateForExchange(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) internal view returns (uint256 feeRate, bool tooVolatile) {
        // Get the exchange fee rate as per the source currencyKey and destination currencyKey
        uint256 baseRate = getExchangeFeeRate(sourceCurrencyKey).add(getExchangeFeeRate(destinationCurrencyKey));
        uint256 dynamicFee;
        (dynamicFee, tooVolatile) = _dynamicFeeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);
        return (baseRate.add(dynamicFee), tooVolatile);
    }

    /// @notice Calculate the exchange fee for a given source and destination currency key
    /// @param sourceCurrencyKey The source currency key
    /// @param destinationCurrencyKey The destination currency key
    /// @param roundIdForSrc The round id of the source currency.
    /// @param roundIdForDest The round id of the target currency.
    /// @return feeRate The exchange fee rate
    /// @return tooVolatile The exchange dynamic fee rate
    function _feeRateForExchangeAtRounds(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    ) internal view returns (uint256 feeRate, bool tooVolatile) {
        // Get the exchange fee rate as per the source currencyKey and destination currencyKey
        uint256 baseRate = getExchangeFeeRate(sourceCurrencyKey).add(getExchangeFeeRate(destinationCurrencyKey));
        uint256 dynamicFee;
        (dynamicFee, tooVolatile) = _dynamicFeeRateForExchangeAtRounds(
            sourceCurrencyKey,
            destinationCurrencyKey,
            roundIdForSrc,
            roundIdForDest
        );
        return (baseRate.add(dynamicFee), tooVolatile);
    }

    function _dynamicFeeRateForExchange(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) internal view returns (uint256 dynamicFee, bool tooVolatile) {
        DynamicFeeConfig memory config = getExchangeDynamicFeeConfig();
        (uint256 dynamicFeeDst, bool dstVolatile) = _dynamicFeeRateForCurrency(destinationCurrencyKey, config);
        (uint256 dynamicFeeSrc, bool srcVolatile) = _dynamicFeeRateForCurrency(sourceCurrencyKey, config);
        dynamicFee = dynamicFeeDst.add(dynamicFeeSrc);
        // cap to maxFee
        bool overMax = dynamicFee > config.maxFee;
        dynamicFee = overMax ? config.maxFee : dynamicFee;
        return (dynamicFee, overMax || dstVolatile || srcVolatile);
    }

    function _dynamicFeeRateForExchangeAtRounds(
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    ) internal view returns (uint256 dynamicFee, bool tooVolatile) {
        DynamicFeeConfig memory config = getExchangeDynamicFeeConfig();
        (uint256 dynamicFeeDst, bool dstVolatile) = _dynamicFeeRateForCurrencyRound(
            destinationCurrencyKey,
            roundIdForDest,
            config
        );
        (uint256 dynamicFeeSrc, bool srcVolatile) = _dynamicFeeRateForCurrencyRound(sourceCurrencyKey, roundIdForSrc, config);
        dynamicFee = dynamicFeeDst.add(dynamicFeeSrc);
        // cap to maxFee
        bool overMax = dynamicFee > config.maxFee;
        dynamicFee = overMax ? config.maxFee : dynamicFee;
        return (dynamicFee, overMax || dstVolatile || srcVolatile);
    }

    /// @notice Get dynamic dynamicFee for a given currency key (SIP-184)
    /// @param currencyKey The given currency key
    /// @param config dynamic fee calculation configuration params
    /// @return dynamicFee The dynamic fee
    /// @return tooVolatile if it exceeds max dynamic fee set in config
    function _dynamicFeeRateForCurrency(
        bytes32 currencyKey,
        DynamicFeeConfig memory config
    ) internal view returns (uint256 dynamicFee, bool tooVolatile) {
        // no dynamic dynamicFee for sUSD or too few rounds
        if (currencyKey == sUSD || config.rounds <= 1) {
            return (0, false);
        }
        uint256 roundId = exchangeRates().getCurrentRoundId(currencyKey);
        return _dynamicFeeRateForCurrencyRound(currencyKey, roundId, config);
    }

    /// @notice Get dynamicFee for a given currency key (SIP-184)
    /// @param currencyKey The given currency key
    /// @param roundId The round id
    /// @param config dynamic fee calculation configuration params
    /// @return dynamicFee The dynamic fee
    /// @return tooVolatile if it exceeds max dynamic fee set in config
    function _dynamicFeeRateForCurrencyRound(
        bytes32 currencyKey,
        uint256 roundId,
        DynamicFeeConfig memory config
    ) internal view returns (uint256 dynamicFee, bool tooVolatile) {
        // no dynamic dynamicFee for sUSD or too few rounds
        if (currencyKey == sUSD || config.rounds <= 1) {
            return (0, false);
        }
        uint256[] memory prices;
        (prices, ) = exchangeRates().ratesAndUpdatedTimeForCurrencyLastNRounds(currencyKey, config.rounds, roundId);
        dynamicFee = _dynamicFeeCalculation(prices, config.threshold, config.weightDecay);
        // cap to maxFee
        bool overMax = dynamicFee > config.maxFee;
        dynamicFee = overMax ? config.maxFee : dynamicFee;
        return (dynamicFee, overMax);
    }

    /// @notice Calculate dynamic fee according to SIP-184
    /// @param prices A list of prices from the current round to the previous rounds
    /// @param threshold A threshold to clip the price deviation ratop
    /// @param weightDecay A weight decay constant
    /// @return uint dynamic fee rate as decimal
    function _dynamicFeeCalculation(
        uint256[] memory prices,
        uint256 threshold,
        uint256 weightDecay
    ) internal pure returns (uint256) {
        // don't underflow
        if (prices.length == 0) {
            return 0;
        }

        uint256 dynamicFee = 0; // start with 0
        // go backwards in price array
        for (uint256 i = prices.length - 1; i > 0; i--) {
            // apply decay from previous round (will be 0 for first round)
            dynamicFee = dynamicFee.multiplyDecimal(weightDecay);
            // calculate price deviation
            uint256 deviation = _thresholdedAbsDeviationRatio(prices[i - 1], prices[i], threshold);
            // add to total fee
            dynamicFee = dynamicFee.add(deviation);
        }
        return dynamicFee;
    }

    /// absolute price deviation ratio used by dynamic fee calculation
    /// deviationRatio = (abs(current - previous) / previous) - threshold
    /// if negative, zero is returned
    function _thresholdedAbsDeviationRatio(
        uint256 price,
        uint256 previousPrice,
        uint256 threshold
    ) internal pure returns (uint256) {
        if (previousPrice == 0) {
            return 0; // don't divide by zero
        }
        // abs difference between prices
        uint256 absDelta = price > previousPrice ? price - previousPrice : previousPrice - price;
        // relative to previous price
        uint256 deviationRatio = absDelta.divideDecimal(previousPrice);
        // only the positive difference from threshold
        return deviationRatio > threshold ? deviationRatio - threshold : 0;
    }

    function getAmountsForExchange(
        uint256 sourceAmount,
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey
    ) external view returns (uint256 amountReceived, uint256 fee, uint256 exchangeFeeRate) {
        // The checks are added for consistency with the checks performed in _exchange()
        // The reverts (instead of no-op returns) are used order to prevent incorrect usage in calling contracts
        // (The no-op in _exchange() is in order to trigger system suspension if needed)

        // check synths active
        systemStatus().requireSynthActive(sourceCurrencyKey);
        systemStatus().requireSynthActive(destinationCurrencyKey);

        // check rates don't deviate above ciruit breaker allowed deviation
        (, bool srcInvalid) = exchangeCircuitBreaker().rateWithInvalid(sourceCurrencyKey);
        (, bool dstInvalid) = exchangeCircuitBreaker().rateWithInvalid(destinationCurrencyKey);
        require(!srcInvalid, "source synth rate invalid");
        require(!dstInvalid, "destination synth rate invalid");

        // check rates not stale or flagged
        _ensureCanExchange(sourceCurrencyKey, sourceAmount, destinationCurrencyKey);

        bool tooVolatile;
        (exchangeFeeRate, tooVolatile) = _feeRateForExchange(sourceCurrencyKey, destinationCurrencyKey);

        // check rates volatility result
        require(!tooVolatile, "exchange rates too volatile");

        (uint256 destinationAmount, , ) = exchangeRates().effectiveValueAndRates(
            sourceCurrencyKey,
            sourceAmount,
            destinationCurrencyKey
        );

        amountReceived = _deductFeesFromAmount(destinationAmount, exchangeFeeRate);
        fee = destinationAmount.sub(amountReceived);
    }

    function _deductFeesFromAmount(
        uint256 destinationAmount,
        uint256 exchangeFeeRate
    ) internal pure returns (uint256 amountReceived) {
        amountReceived = destinationAmount.multiplyDecimal(SafeDecimalMath.unit().sub(exchangeFeeRate));
    }

    function appendExchange(
        address account,
        bytes32 src,
        uint256 amount,
        bytes32 dest,
        uint256 amountReceived,
        uint256 exchangeFeeRate
    ) internal {
        IExchangeRates exRates = exchangeRates();
        uint256 roundIdForSrc = exRates.getCurrentRoundId(src);
        uint256 roundIdForDest = exRates.getCurrentRoundId(dest);
        exchangeState().appendExchangeEntry(
            account,
            src,
            amount,
            dest,
            amountReceived,
            exchangeFeeRate,
            block.timestamp,
            roundIdForSrc,
            roundIdForDest
        );

        emit ExchangeEntryAppended(account, src, amount, dest, amountReceived, exchangeFeeRate, roundIdForSrc, roundIdForDest);
    }

    function getRoundIdsAtPeriodEnd(
        IExchangeState.ExchangeEntry memory exchangeEntry
    ) internal view returns (uint256 srcRoundIdAtPeriodEnd, uint256 destRoundIdAtPeriodEnd) {
        IExchangeRates exRates = exchangeRates();
        uint256 _waitingPeriodSecs = getWaitingPeriodSecs();

        srcRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
            exchangeEntry.src,
            exchangeEntry.roundIdForSrc,
            exchangeEntry.timestamp,
            _waitingPeriodSecs
        );
        destRoundIdAtPeriodEnd = exRates.getLastRoundIdBeforeElapsedSecs(
            exchangeEntry.dest,
            exchangeEntry.roundIdForDest,
            exchangeEntry.timestamp,
            _waitingPeriodSecs
        );
    }

    // ========== MODIFIERS ==========

    modifier onlyWrappedSynthrorSynth() {
        IWrappedSynthr _wrappedSynthr = wrappedSynthr();
        require(
            msg.sender == address(_wrappedSynthr) || issuer().synthsByAddress(msg.sender) != bytes32(0),
            "Exchanger: Only wrappedSynthr or a synth contract can perform this action"
        );
        _;
    }

    modifier onlySynthrBridge() {
        require(resolver.isBridge(msg.sender), "Exchanger: Only synthr bridge can call");
        _;
    }

    // ========== EVENTS ==========
    event ExchangeEntryAppended(
        address indexed account,
        bytes32 src,
        uint256 amount,
        bytes32 dest,
        uint256 amountReceived,
        uint256 exchangeFeeRate,
        uint256 roundIdForSrc,
        uint256 roundIdForDest
    );

    event ExchangeEntrySettled(
        address indexed from,
        bytes32 src,
        uint256 amount,
        bytes32 dest,
        uint256 reclaim,
        uint256 rebate,
        uint256 srcRoundIdAtPeriodEnd,
        uint256 destRoundIdAtPeriodEnd,
        uint256 exchangeTimestamp
    );
    event SynthExchange(
        address indexed account,
        address indexed toAddress,
        bytes32 fromCurrencyKey,
        uint256 fromAmount,
        bytes32 toCurrencyKey,
        uint256 toAmount,
        uint256 fee,
        uint16 destChainId
    );

    event TrackingSynthExchange(
        address indexed account,
        address indexed toAddress,
        bytes32 fromCurrencyKey,
        uint256 fromAmount,
        bytes32 toCurrencyKey,
        uint256 toAmount,
        uint256 fee,
        uint16 destChainId
    );
    event ExchangeReclaim(address indexed account, bytes32 currencyKey, uint256 amount);
    event ExchangeRebate(address indexed account, bytes32 currencyKey, uint256 amount);
    event DestIssueForExchange(address indexed account, bytes32 currencyKey, uint256 amount);
}

File 17 of 20 : MixinResolver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Internal references
import "./AddressResolverLightChain.sol";

contract MixinResolver {
    AddressResolverLightChain public resolver;

    mapping(bytes32 => address) private addressCache;

    constructor(address _resolver) {
        resolver = AddressResolverLightChain(_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 (uint256 i = 0; i < first.length; i++) {
            combination[i] = first[i];
        }

        for (uint256 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 virtual returns (bytes32[] memory addresses) {}

    function rebuildCache() public {
        bytes32[] memory requiredAddresses = resolverAddressesRequired();
        // The resolver must call this function whenver it updates its state
        for (uint256 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 (uint256 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);
}

File 18 of 20 : MixinSystemSettings.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./MixinResolver.sol";

// Internal references
import "../interfaces/IFlexibleStorage.sol";

contract MixinSystemSettings is MixinResolver {
    // must match the one defined SystemSettingsLib, defined in both places due to sol v0.5 limitations
    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_RATE_STALE_PERIOD = "rateStalePeriod";
    /* ========== Exchange Fees Related ========== */
    bytes32 internal constant SETTING_EXCHANGE_FEE_RATE = "exchangeFeeRate";
    bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_THRESHOLD = "exchangeDynamicFeeThreshold";
    bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY = "exchangeDynamicFeeWeightDecay";
    bytes32 internal constant SETTING_EXCHANGE_DYNAMIC_FEE_ROUNDS = "exchangeDynamicFeeRounds";
    bytes32 internal constant SETTING_EXCHANGE_MAX_DYNAMIC_FEE = "exchangeMaxDynamicFee";
    /* ========== End Exchange Fees Related ========== */
    bytes32 internal constant SETTING_AGGREGATOR_WARNING_FLAGS = "aggregatorWarningFlags";
    bytes32 internal constant SETTING_ATOMIC_MAX_VOLUME_PER_BLOCK = "atomicMaxVolumePerBlock";
    bytes32 internal constant SETTING_ATOMIC_TWAP_WINDOW = "atomicTwapWindow";
    bytes32 internal constant SETTING_ATOMIC_EQUIVALENT_FOR_DEX_PRICING = "atomicEquivalentForDexPricing";
    bytes32 internal constant SETTING_ATOMIC_EXCHANGE_FEE_RATE = "atomicExchangeFeeRate";
    bytes32 internal constant SETTING_ATOMIC_VOLATILITY_CONSIDERATION_WINDOW = "atomicVolConsiderationWindow";
    bytes32 internal constant SETTING_ATOMIC_VOLATILITY_UPDATE_THRESHOLD = "atomicVolUpdateThreshold";
    bytes32 internal constant SETTING_PURE_CHAINLINK_PRICE_FOR_ATOMIC_SWAPS_ENABLED = "pureChainlinkForAtomicsEnabled";

    bytes32 internal constant CONTRACT_FLEXIBLESTORAGE = "FlexibleStorage";

    struct DynamicFeeConfig {
        uint256 threshold;
        uint256 weightDecay;
        uint256 rounds;
        uint256 maxFee;
    }

    constructor(address _resolver) MixinResolver(_resolver) {}

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

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

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

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

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

    /* ========== Exchange Related Fees ========== */
    function getExchangeFeeRate(bytes32 currencyKey) internal view returns (uint256) {
        return
            flexibleStorage().getUIntValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_EXCHANGE_FEE_RATE, currencyKey))
            );
    }

    /// @notice Get exchange dynamic fee related keys
    /// @return threshold, weight decay, rounds, and max fee
    function getExchangeDynamicFeeConfig() internal view returns (DynamicFeeConfig memory) {
        bytes32[] memory keys = new bytes32[](4);
        keys[0] = SETTING_EXCHANGE_DYNAMIC_FEE_THRESHOLD;
        keys[1] = SETTING_EXCHANGE_DYNAMIC_FEE_WEIGHT_DECAY;
        keys[2] = SETTING_EXCHANGE_DYNAMIC_FEE_ROUNDS;
        keys[3] = SETTING_EXCHANGE_MAX_DYNAMIC_FEE;
        uint256[] memory values = flexibleStorage().getUIntValues(SETTING_CONTRACT_NAME, keys);
        return DynamicFeeConfig({threshold: values[0], weightDecay: values[1], rounds: values[2], maxFee: values[3]});
    }

    /* ========== End Exchange Related Fees ========== */

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

    function getAtomicMaxVolumePerBlock() internal view returns (uint256) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ATOMIC_MAX_VOLUME_PER_BLOCK);
    }

    function getAtomicTwapWindow() internal view returns (uint256) {
        return flexibleStorage().getUIntValue(SETTING_CONTRACT_NAME, SETTING_ATOMIC_TWAP_WINDOW);
    }

    function getAtomicEquivalentForDexPricing(bytes32 currencyKey) internal view returns (address) {
        return
            flexibleStorage().getAddressValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_ATOMIC_EQUIVALENT_FOR_DEX_PRICING, currencyKey))
            );
    }

    function getAtomicExchangeFeeRate(bytes32 currencyKey) internal view returns (uint256) {
        return
            flexibleStorage().getUIntValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_ATOMIC_EXCHANGE_FEE_RATE, currencyKey))
            );
    }

    function getAtomicVolatilityConsiderationWindow(bytes32 currencyKey) internal view returns (uint256) {
        return
            flexibleStorage().getUIntValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_ATOMIC_VOLATILITY_CONSIDERATION_WINDOW, currencyKey))
            );
    }

    function getAtomicVolatilityUpdateThreshold(bytes32 currencyKey) internal view returns (uint256) {
        return
            flexibleStorage().getUIntValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_ATOMIC_VOLATILITY_UPDATE_THRESHOLD, currencyKey))
            );
    }

    function getPureChainlinkPriceForAtomicSwapsEnabled(bytes32 currencyKey) internal view returns (bool) {
        return
            flexibleStorage().getBoolValue(
                SETTING_CONTRACT_NAME,
                keccak256(abi.encodePacked(SETTING_PURE_CHAINLINK_PRICE_FOR_ATOMIC_SWAPS_ENABLED, currencyKey))
            );
    }
}

File 19 of 20 : Owned.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor(address _owner) {
        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);
}

File 20 of 20 : SafeDecimalMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Libraries
// import "openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol";
import "../externals/openzeppelin/SafeMath.sol";

library SafeDecimalMath {
    using SafeMath for uint256;

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

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

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

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

    /**
     * @return Provides an interface to PRECISE_UNIT.
     */
    function preciseUnit() external pure returns (uint256) {
        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(uint256 x, uint256 y) internal pure returns (uint256) {
        /* 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(uint256 x, uint256 y, uint256 precisionUnit) private pure returns (uint256) {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        uint256 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(uint256 x, uint256 y) internal pure returns (uint256) {
        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(uint256 x, uint256 y) internal pure returns (uint256) {
        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(uint256 x, uint256 y) internal pure returns (uint256) {
        /* 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(uint256 x, uint256 y, uint256 precisionUnit) private pure returns (uint256) {
        uint256 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(uint256 x, uint256 y) internal pure returns (uint256) {
        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(uint256 x, uint256 y) internal pure returns (uint256) {
        return _divideDecimalRound(x, y, PRECISE_UNIT);
    }

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

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

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

        return quotientTimesTen / 10;
    }

    // Computes `a - b`, setting the value to 0 if b > a.
    function floorsub(uint256 a, uint256 b) internal pure returns (uint256) {
        return b >= a ? 0 : a - b;
    }

    /* ---------- Utilities ---------- */
    /*
     * Absolute value of the input, returned as a signed number.
     */
    function signedAbs(int256 x) internal pure returns (int256) {
        return x < 0 ? -x : x;
    }

    /*
     * Absolute value of the input, returned as an unsigned number.
     */
    function abs(int256 x) internal pure returns (uint256) {
        return uint256(signedAbs(x));
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/lightChains/SafeDecimalMath.sol": {
      "SafeDecimalMath": "0xa1e534c7ae316eae135b89a76c7335e78fb1681e"
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_resolver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"bytes32","name":"fromCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"toCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"destChainId","type":"uint16"}],"name":"AtomicSynthExchange","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":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DestIssueForExchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bytes32","name":"src","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"dest","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amountReceived","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exchangeFeeRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundIdForSrc","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundIdForDest","type":"uint256"}],"name":"ExchangeEntryAppended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"bytes32","name":"src","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"dest","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"reclaim","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rebate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"srcRoundIdAtPeriodEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"destRoundIdAtPeriodEnd","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exchangeTimestamp","type":"uint256"}],"name":"ExchangeEntrySettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ExchangeRebate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ExchangeReclaim","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":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"bytes32","name":"fromCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"toCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"destChainId","type":"uint16"}],"name":"SynthExchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"bytes32","name":"fromCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"toCurrencyKey","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"destChainId","type":"uint16"}],"name":"TrackingSynthExchange","type":"event"},{"inputs":[],"name":"CONTRACT_NAME","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"bytes32","name":"currencyKey","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"refunded","type":"uint256"}],"name":"calculateAmountAfterSettlement","outputs":[{"internalType":"uint256","name":"amountAfterSettlement","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"dynamicFeeRateForExchange","outputs":[{"internalType":"uint256","name":"feeRate","type":"uint256"},{"internalType":"bool","name":"tooVolatile","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"fromAccount","type":"address"},{"internalType":"address","name":"destAccount","type":"address"},{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"reclaimed","type":"uint256"},{"internalType":"uint256","name":"refunded","type":"uint256"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bool","name":"erc20Payment","type":"bool"}],"internalType":"struct IExchanger.ExchangeArgs","name":"args","type":"tuple"},{"internalType":"bytes32","name":"bridgeName","type":"bytes32"}],"name":"exchange","outputs":[{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minAmount","type":"uint256"},{"components":[{"internalType":"address","name":"fromAccount","type":"address"},{"internalType":"address","name":"destAccount","type":"address"},{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destCurrencyKey","type":"bytes32"},{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"reclaimed","type":"uint256"},{"internalType":"uint256","name":"refunded","type":"uint256"},{"internalType":"uint16","name":"destChainId","type":"uint16"},{"internalType":"bool","name":"erc20Payment","type":"bool"}],"internalType":"struct IExchanger.ExchangeArgs","name":"args","type":"tuple"},{"internalType":"bytes32","name":"bridgeName","type":"bytes32"}],"name":"exchangeAtomically","outputs":[{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"feeRateForExchange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"getAmountsForAtomicExchange","outputs":[{"internalType":"uint256","name":"amountReceived","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"exchangeFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"sourceCurrencyKey","type":"bytes32"},{"internalType":"bytes32","name":"destinationCurrencyKey","type":"bytes32"}],"name":"getAmountsForExchange","outputs":[{"internalType":"uint256","name":"amountReceived","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"exchangeFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bytes32","name":"_sourceKey","type":"bytes32"},{"internalType":"uint256","name":"_sourceAmount","type":"uint256"},{"internalType":"bytes32","name":"_destKey","type":"bytes32"},{"internalType":"bytes32","name":"_bridgeName","type":"bytes32"},{"internalType":"uint16","name":"_destChainId","type":"uint16"}],"name":"getSendExchangeGasFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isResolverCached","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"isSynthRateInvalid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAtomicVolume","outputs":[{"internalType":"uint64","name":"time","type":"uint64"},{"internalType":"uint192","name":"volume","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"maxSecsLeftInWaitingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rebuildCache","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resolver","outputs":[{"internalType":"contract AddressResolverLightChain","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"resolverAddressesRequired","outputs":[{"internalType":"bytes32[]","name":"addresses","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"settle","outputs":[{"internalType":"uint256","name":"reclaimed","type":"uint256"},{"internalType":"uint256","name":"refunded","type":"uint256"},{"internalType":"uint256","name":"numEntriesSettled","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"settlementOwing","outputs":[{"internalType":"uint256","name":"reclaimAmount","type":"uint256"},{"internalType":"uint256","name":"rebateAmount","type":"uint256"},{"internalType":"uint256","name":"numEntries","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"currencyKey","type":"bytes32"}],"name":"suspendSynthWithInvalidRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes32","name":"destinationKey","type":"bytes32"},{"internalType":"uint256","name":"destinationAmount","type":"uint256"}],"name":"updateDestinationForExchange","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50604051620053fe380380620053fe833981016040819052620000349162000135565b81818080836001600160a01b038116620000945760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f74206265203000000000000000604482015260640160405180910390fd5b600080546001600160a01b0319166001600160a01b03831690811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a150600280546001600160a01b0319166001600160a01b0392909216919091179055506200016d9350505050565b80516001600160a01b03811681146200013057600080fd5b919050565b600080604083850312156200014957600080fd5b620001548362000118565b9150620001646020840162000118565b90509250929050565b615281806200017d6000396000f3fe60806040526004361061014b5760003560e01c806357af302c116100b65780638da5cb5b1161006f5780638da5cb5b146103bc5780638e52049c146103dc57806397935a3d14610437578063c39def0b1461044a578063dc703e731461047f578063f450aa341461049f57600080fd5b806357af302c14610310578063614d08f81461033057806364b6aef814610350578063741853601461037057806379ba509714610385578063899ffef41461039a57600080fd5b806319d5c6651161010857806319d5c665146102305780631a5c60951461026b5780631b16802c1461028b5780632af64bd3146102ab5780634c268fc8146102d057806353a47bb7146102f057600080fd5b806304f3bcec14610150578063059c29ec1461018d5780630b9e31c9146101bb5780630be5396a146101dd57806314ef073a146101f05780631627540c14610210575b600080fd5b34801561015c57600080fd5b50600254610170906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561019957600080fd5b506101ad6101a83660046148df565b6104bf565b604051908152602001610184565b3480156101c757600080fd5b506101db6101d636600461490b565b61055c565b005b6101ad6101eb36600461493d565b61068c565b3480156101fc57600080fd5b506101db61020b366004614975565b6107e6565b34801561021c57600080fd5b506101db61022b3660046149aa565b610a37565b34801561023c57600080fd5b5061025061024b3660046148df565b610a93565b60408051938452602084019290925290820152606001610184565b34801561027757600080fd5b506101ad6102863660046149c7565b610ab1565b34801561029757600080fd5b506102506102a63660046148df565b610b01565b3480156102b757600080fd5b506102c0610b82565b6040519015158152602001610184565b3480156102dc57600080fd5b506101ad6102eb3660046149e9565b610ca2565b3480156102fc57600080fd5b50600154610170906001600160a01b031681565b34801561031c57600080fd5b506102c061032b36600461490b565b610dcf565b34801561033c57600080fd5b506101ad6822bc31b430b733b2b960b91b81565b34801561035c57600080fd5b506101ad61036b366004614a36565b610e5e565b34801561037c57600080fd5b506101db610f6d565b34801561039157600080fd5b506101db6110fd565b3480156103a657600080fd5b506103af6111e7565b6040516101849190614acb565b3480156103c857600080fd5b50600054610170906001600160a01b031681565b3480156103e857600080fd5b5060045461040f9067ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610184565b6101ad610445366004614ade565b61134c565b34801561045657600080fd5b5061046a6104653660046149c7565b611425565b60408051928352901515602083015201610184565b34801561048b57600080fd5b5061025061049a366004614b0b565b61143e565b3480156104ab57600080fd5b506102506104ba366004614b0b565b611461565b60006105536104cc611803565b604051631e280db960e31b81526001600160a01b03868116600483015260248201869052919091169063f1406dc89060440160206040518083038186803b15801561051657600080fd5b505afa15801561052a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054e9190614b37565b611823565b90505b92915050565b61056461186f565b6001600160a01b031663086dabd16040518163ffffffff1660e01b815260040160006040518083038186803b15801561059c57600080fd5b505afa1580156105b0573d6000803e3d6000fd5b5050505060006105be611889565b6001600160a01b031663a47af19e836040518263ffffffff1660e01b81526004016105eb91815260200190565b6040805180830381600087803b15801561060457600080fd5b505af1158015610618573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063c9190614b5e565b915050806106885760405162461bcd60e51b815260206004820152601460248201527314de5b9d1a081c1c9a58d9481a5cc81d985b1a5960621b60448201526064015b60405180910390fd5b5050565b6000806106976118ad565b9050336001600160a01b0382161480610731575060006106b56118c8565b6040516316b2213f60e01b81523360048201526001600160a01b0391909116906316b2213f9060240160206040518083038186803b1580156106f657600080fd5b505afa15801561070a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072e9190614b37565b14155b61074d5760405162461bcd60e51b815260040161067f90614b8e565b61076561075f36869003860186614c48565b846118dc565b509150848210156107de5760405162461bcd60e51b815260206004820152603a60248201527f54686520616d6f756e742072656365697665642069732062656c6f772074686560448201527f206d696e696d756d20616d6f756e74207370656369666965642e000000000000606482015260840161067f565b509392505050565b600254604051633933006760e11b81523360048201526001600160a01b039091169063726600ce9060240160206040518083038186803b15801561082957600080fd5b505afa15801561083d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108619190614cf2565b6108bc5760405162461bcd60e51b815260206004820152602660248201527f45786368616e6765723a204f6e6c792073796e746872206272696467652063616044820152651b8818d85b1b60d21b606482015260840161067f565b816109005760405162461bcd60e51b81526020600482015260146024820152733232b9ba1035b2bc903234b23713ba1039b2ba1760611b604482015260640161067f565b600061090a6118c8565b6001600160a01b03166332608039846040518263ffffffff1660e01b815260040161093791815260200190565b60206040518083038186803b15801561094f57600080fd5b505afa158015610963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109879190614d0f565b60405163219e412d60e21b81526001600160a01b038681166004830152602482018590529192509082169063867904b490604401600060405180830381600087803b1580156109d557600080fd5b505af11580156109e9573d6000803e3d6000fd5b505060408051868152602081018690526001600160a01b03881693507f6aef294c17150fbb566095411a70428ebfaac62db39b78dd3a83afc9456c78b692500160405180910390a250505050565b610a3f611df0565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6000806000610aa28585611e64565b50919790965090945092505050565b6000806000610ac08585612204565b9150915080156107de5760405162461bcd60e51b815260206004820152600c60248201526b746f6f20766f6c6174696c6560a01b604482015260640161067f565b6000806000610b0e61186f565b6001600160a01b03166342a28e21856040518263ffffffff1660e01b8152600401610b3b91815260200190565b60006040518083038186803b158015610b5357600080fd5b505afa158015610b67573d6000803e3d6000fd5b50505050610b75858561224b565b9250925092509250925092565b600080610b8d6111e7565b905060005b8151811015610c99576000828281518110610baf57610baf614d2c565b602090810291909101810151600081815260039092526040918290205460025492516321f8a72160e01b8152600481018390529193506001600160a01b039081169216906321f8a7219060240160206040518083038186803b158015610c1457600080fd5b505afa158015610c28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c9190614d0f565b6001600160a01b0316141580610c7757506000818152600360205260409020546001600160a01b0316155b15610c86576000935050505090565b5080610c9181614d58565b915050610b92565b50600191505090565b816000610cad6118c8565b6001600160a01b03166332608039866040518263ffffffff1660e01b8152600401610cda91815260200190565b60206040518083038186803b158015610cf257600080fd5b505afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190614d0f565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a082319060240160206040518083038186803b158015610d6d57600080fd5b505afa158015610d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da59190614b37565b90508215610dba57610db7828461250f565b91505b80821115610dc6578091505b50949350505050565b600080610dda611889565b6001600160a01b031663cb1ec317846040518263ffffffff1660e01b8152600401610e0791815260200190565b604080518083038186803b158015610e1e57600080fd5b505afa158015610e32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e569190614b5e565b949350505050565b6000806000610e6e87898861256e565b50506040805160608f901b6bffffffffffffffffffffffff191660208201528151601481830301815260348201909252949650929450600093610ec5936005935091508c908c908c90899089908d90605401614dc0565b6040516020818303038152906040529050610edf86612642565b6001600160a01b0316632bb821bd826005886040518463ffffffff1660e01b8152600401610f0f93929190614e15565b60206040518083038186803b158015610f2757600080fd5b505afa158015610f3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5f9190614b37565b9a9950505050505050505050565b6000610f776111e7565b905060005b8151811015610688576000828281518110610f9957610f99614d2c565b602002602001015190506000600260009054906101000a90046001600160a01b03166001600160a01b031663dacb2d01838460405160200161100791907f5265736f6c766572206d697373696e67207461726765743a20000000000000008152601981019190915260390190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611033929190614e43565b60206040518083038186803b15801561104b57600080fd5b505afa15801561105f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110839190614d0f565b60008381526003602090815260409182902080546001600160a01b0319166001600160a01b0385169081179091558251868152918201529192507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68910160405180910390a1505080806110f590614d58565b915050610f7c565b6001546001600160a01b031633146111755760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b606482015260840161067f565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b606060006111f36126be565b60408051600680825260e08201909252919250600091906020820160c0803683370190505090506b53797374656d53746174757360a01b8160008151811061123d5761123d614d2c565b6020026020010181815250506c45786368616e6765537461746560981b8160018151811061126d5761126d614d2c565b6020026020010181815250506c45786368616e6765526174657360981b8160028151811061129d5761129d614d2c565b6020026020010181815250506c2bb930b83832b229bcb73a343960991b816003815181106112cd576112cd614d2c565b6020026020010181815250506524b9b9bab2b960d11b816004815181106112f6576112f6614d2c565b6020026020010181815250507522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b8160058151811061132f5761132f614d2c565b6020026020010181815250506113458282612715565b9250505090565b6000806113576118ad565b9050336001600160a01b03821614806113f1575060006113756118c8565b6040516316b2213f60e01b81523360048201526001600160a01b0391909116906316b2213f9060240160206040518083038186803b1580156113b657600080fd5b505afa1580156113ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ee9190614b37565b14155b61140d5760405162461bcd60e51b815260040161067f90614b8e565b610dc661141f36869003860186614c48565b84612827565b6000806114328484612cf0565b915091505b9250929050565b600080600061144e86868661256e565b50939a9299509097509095505050505050565b600080600061146e61186f565b6001600160a01b03166342a28e21866040518263ffffffff1660e01b815260040161149b91815260200190565b60006040518083038186803b1580156114b357600080fd5b505afa1580156114c7573d6000803e3d6000fd5b505050506114d361186f565b6001600160a01b03166342a28e21856040518263ffffffff1660e01b815260040161150091815260200190565b60006040518083038186803b15801561151857600080fd5b505afa15801561152c573d6000803e3d6000fd5b50505050600061153a611889565b6001600160a01b031663cb1ec317876040518263ffffffff1660e01b815260040161156791815260200190565b604080518083038186803b15801561157e57600080fd5b505afa158015611592573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b69190614b5e565b91505060006115c3611889565b6001600160a01b031663cb1ec317876040518263ffffffff1660e01b81526004016115f091815260200190565b604080518083038186803b15801561160757600080fd5b505afa15801561161b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163f9190614b5e565b91505081156116905760405162461bcd60e51b815260206004820152601960248201527f736f757263652073796e7468207261746520696e76616c696400000000000000604482015260640161067f565b80156116de5760405162461bcd60e51b815260206004820152601e60248201527f64657374696e6174696f6e2073796e7468207261746520696e76616c69640000604482015260640161067f565b6116e9878988612d6c565b60006116f58888612204565b909450905080156117485760405162461bcd60e51b815260206004820152601b60248201527f65786368616e676520726174657320746f6f20766f6c6174696c650000000000604482015260640161067f565b6000611752612ee1565b60405163414a80b560e11b8152600481018b9052602481018c9052604481018a90526001600160a01b039190911690638295016a9060640160606040518083038186803b1580156117a257600080fd5b505afa1580156117b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117da9190614e5c565b505090506117e88186612efc565b96506117f48188612f88565b95505050505093509350939050565b600061181e6c45786368616e6765537461746560981b612fe6565b905090565b60008061182e61305b565b90508215806118465750611842838261250f565b4210155b156118545750600092915050565b61186842611862858461250f565b90612f88565b9392505050565b600061181e6b53797374656d53746174757360a01b612fe6565b600061181e7522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b612fe6565b600061181e6c2bb930b83832b229bcb73a343960991b612fe6565b600061181e6524b9b9bab2b960d11b612fe6565b60008060008490506118fb816040015182608001518360600151612d6c565b611903612ee1565b6001600160a01b0316638661cc7b82604001516040518263ffffffff1660e01b815260040161193491815260200190565b60206040518083038186803b15801561194c57600080fd5b505afa158015611960573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119849190614cf2565b156119d15760405162461bcd60e51b815260206004820152601c60248201527f5372632073796e74682076616c756520697320766f6c6174696c652e00000000604482015260640161067f565b6119d9612ee1565b6001600160a01b0316638661cc7b82606001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b60206040518083038186803b158015611a2257600080fd5b505afa158015611a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5a9190614cf2565b15611aa75760405162461bcd60e51b815260206004820152601d60248201527f446573742073796e74682076616c756520697320766f6c6174696c652e000000604482015260640161067f565b611abe81608001518260000151836040015161310a565b61010084015260e083015260808201819052611ae1576000809250925050611437565b600080600080611afe85608001518660400151876060015161256e565b60a08b0186905260c08b0185905260408b015160608c0151969d50949b5092985090965094509250611b2f91613143565b15611b4557600080965096505050505050611437565b611b4d611889565b6001600160a01b03166378cb51cb84611b668a8a61250f565b6040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186803b158015611ba257600080fd5b505afa158015611bb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bda9190614cf2565b15611c275760405162461bcd60e51b815260206004820152601d60248201527f41746f6d6963207261746520646576696174657320746f6f206d756368000000604482015260640161067f565b6000631cd554d160e21b86604001511415611c4757506080890151611c9e565b631cd554d160e21b86606001511415611c61575082611c9e565b600080611c7e8c608001518960400151631cd554d160e21b61256e565b5050505091509150611c99818361250f90919063ffffffff16565b925050505b611ca781613285565b611cb1868a613331565b85602001516001600160a01b031686600001516001600160a01b03167f44dad12059e2c487e34548d3f2853cd851983ebd74f59ae89f55db7f0f5234ea886040015189608001518a606001518d8c60c001518d6101200151604051611d4296959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a385602001516001600160a01b031686600001516001600160a01b03167fa15ae7ed6a9ceafa6788f8020c73234a073e5fd311aca9b3250c93d374c07785886040015189608001518a606001518d8c60c001518d6101200151604051611ddb96959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a35050505050509250929050565b6000546001600160a01b03163314611e625760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b606482015260840161067f565b565b60008060006060611e73611803565b60405163b44e975360e01b81526001600160a01b03888116600483015260248201889052919091169063b44e97539060440160206040518083038186803b158015611ebd57600080fd5b505afa158015611ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef59190614b37565b915060008267ffffffffffffffff811115611f1257611f12614bfd565b604051908082528060200260200182016040528015611f9157816020015b611f7e60405180610100016040528060008019168152602001600081526020016000801916815260200160008152602001600081526020016000815260200160008152602001600081525090565b815260200190600190039081611f305790505b50905060005b838110156121f8576000806000611faf8b8b866136db565b9050600080611fbd83613810565b915091506000611fcb612ee1565b84516020860151604080880151905162d9ccd960e71b815260048101939093526024830191909152604482015260648101859052608481018490526001600160a01b039190911690636ce66c809060a40160606040518083038186803b15801561203457600080fd5b505afa158015612048573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206c9190614e5c565b505090506000612080828660800151612efc565b9050600061208c611889565b6001600160a01b03166378cb51cb8760600151846040518363ffffffff1660e01b81526004016120c6929190918252602082015260400190565b60206040518083038186803b1580156120de57600080fd5b505afa1580156120f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121169190614cf2565b90508061217657818660600151111561214b5760608601516121389083612f88565b97506121448e8961250f565b9d50612176565b8560600151821115612176576060860151612167908390612f88565b96506121738d8861250f565b9c505b6040518061010001604052808760000151815260200187602001518152602001876040015181526020018981526020018881526020018681526020018581526020018760a001518152508a8a815181106121d2576121d2614d2c565b6020026020010181905250505050505050505080806121f090614d58565b915050611f97565b50905092959194509250565b60008060006122246122158561396d565b61221e8761396d565b9061250f565b905060006122328686612cf0565b93509050612240828261250f565b935050509250929050565b600080600061225a85856104bf565b156122b35760405162461bcd60e51b815260206004820152602360248201527f43616e6e6f7420736574746c6520647572696e672077616974696e67207065726044820152621a5bd960ea1b606482015260840161067f565b6000806000806122c38989611e64565b9350935093509350828411156122ef576122dd8484612f88565b96506122ea898989613a51565b61230e565b8383111561230e576123018385612f88565b955061230e898988613b84565b60005b815181101561249457896001600160a01b03167f8e3ad1f68bec55de3b6fa12ae2674a2a683a17c918a4cbf5157ac5d9ddc6e94083838151811061235757612357614d2c565b60200260200101516000015184848151811061237557612375614d2c565b60200260200101516020015185858151811061239357612393614d2c565b6020026020010151604001518686815181106123b1576123b1614d2c565b6020026020010151606001518787815181106123cf576123cf614d2c565b6020026020010151608001518888815181106123ed576123ed614d2c565b602002602001015160a0015189898151811061240b5761240b614d2c565b602002602001015160c001518a8a8151811061242957612429614d2c565b602002602001015160e0015160405161247a989796959493929190978852602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b60405180910390a28061248c81614d58565b915050612311565b508194506124a0611803565b604051636869eb1560e11b81526001600160a01b038b81166004830152602482018b9052919091169063d0d3d62a90604401600060405180830381600087803b1580156124ec57600080fd5b505af1158015612500573d6000803e3d6000fd5b50505050505050509250925092565b60008061251c8385614e8a565b9050838110156105535760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015260640161067f565b6000806000806000806000612581612ee1565b604051622a943760e51b8152600481018b9052602481018c9052604481018a90526001600160a01b03919091169063055286e09060640160806040518083038186803b1580156125d057600080fd5b505afa1580156125e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126089190614ea2565b91965094509250905061261b8989613cae565b94506126278186612efc565b96506126338188612f88565b95505093975093979195509350565b60025460405162c8ac9760e41b8152600481018390526000916001600160a01b031690630c8ac9709060240160206040518083038186803b15801561268657600080fd5b505afa15801561269a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105569190614d0f565b604080516001808252818301909252606091602080830190803683370190505090506e466c657869626c6553746f7261676560881b8160008151811061270657612706614d2c565b60200260200101818152505090565b6060815183516127259190614e8a565b67ffffffffffffffff81111561273d5761273d614bfd565b604051908082528060200260200182016040528015612766578160200160208202803683370190505b50905060005b83518110156127be5783818151811061278757612787614d2c565b60200260200101518282815181106127a1576127a1614d2c565b6020908102919091010152806127b681614d58565b91505061276c565b5060005b8251811015612820578281815181106127dd576127dd614d2c565b6020026020010151828286516127f39190614e8a565b8151811061280357612803614d2c565b60209081029190910101528061281881614d58565b9150506127c2565b5092915050565b600080600084608001511161286c5760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b604482015260640161067f565b6128ac6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6128b4612ee1565b6001600160a01b0316637a018a1e86604001516040518263ffffffff1660e01b81526004016128e591815260200190565b60206040518083038186803b1580156128fd57600080fd5b505afa158015612911573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129359190614b37565b60a0820152612942612ee1565b6001600160a01b0316637a018a1e86606001516040518263ffffffff1660e01b815260040161297391815260200190565b60206040518083038186803b15801561298b57600080fd5b505afa15801561299f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c39190614b37565b60c08201526080850151855160408701516129df92919061310a565b61010088015260e087015260808601819052612a02576000809250925050611437565b612a0a612ee1565b6040808701516080880151606089015160a086015160c0870151945162d9ccd960e71b8152600481019490945260248401929092526044830152606482015260848101919091526001600160a01b039190911690636ce66c809060a40160606040518083038186803b158015612a7f57600080fd5b505afa158015612a93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab79190614e5c565b60208401528252604080830191909152850151606086015160a083015160c0840151612ae593929190613ce2565b612af785604001518660600151613143565b15612b09576000809250925050611437565b6000612b27866040015187606001518460a001518560c00151613e81565b606084019190915290508015612b4557600080935093505050611437565b612b5782604001518360600151612efc565b6040830151909450612b699085612f88565b60a0870185905260c087018190529250612b838686613331565b85602001516001600160a01b031686600001516001600160a01b03167f44dad12059e2c487e34548d3f2853cd851983ebd74f59ae89f55db7f0f5234ea886040015189608001518a60600151898c60c001518d6101200151604051612c1496959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a385602001516001600160a01b031686600001516001600160a01b03167fb5826da57a48b0e5d9160b88cdc4a9c303be1746b0619e42e51afe8fe353bbd9886040015189608001518a60600151898c60c001518d6101200151604051612cad96959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a36000612cbf61305b565b1115612ce757612ce78660200151876040015188608001518960600151888760600151613ec6565b50509250929050565b6000806000612cfd6140df565b9050600080612d0c8684614353565b91509150600080612d1d8986614353565b9092509050612d2c848361250f565b6060860151909750871180612d415787612d47565b85606001515b9750878180612d535750845b80612d5b5750825b975097505050505050509250929050565b60008211612daa5760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b604482015260640161067f565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612ddf57612ddf614d2c565b6020026020010181815250508181600181518110612dff57612dff614d2c565b602002602001018181525050612e13612ee1565b6001600160a01b0316630a7d36d1826040518263ffffffff1660e01b8152600401612e3e9190614acb565b60206040518083038186803b158015612e5657600080fd5b505afa158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e9190614cf2565b15612edb5760405162461bcd60e51b815260206004820152601a60248201527f7372632f646573742076616c756520697320696e76616c69642e000000000000604482015260640161067f565b50505050565b600061181e6c45786368616e6765526174657360981b612fe6565b6000610553612f818373a1e534c7ae316eae135b89a76c7335e78fb1681e63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015612f4957600080fd5b505af4158015612f5d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118629190614b37565b8490614420565b600082821115612fda5760405162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015260640161067f565b6000610e568385614ed8565b600081815260036020908152604080832054905170026b4b9b9b4b7339030b2323932b9b99d1607d1b92810192909252603182018490526001600160a01b03169081151590605101604051602081830303815290604052906128205760405162461bcd60e51b815260040161067f9190614eef565b6000613065614442565b6040516323257c2b60e01b81526d53797374656d53657474696e677360901b60048201527077616974696e67506572696f645365637360781b60248201526001600160a01b0391909116906323257c2b906044015b60206040518083038186803b1580156130d257600080fd5b505afa1580156130e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181e9190614b37565b60008060008061311a868661224b565b8996509194509250905080156131395761313686868985610ca2565b93505b5093509350939050565b6000631cd554d160e21b83146131dd5761315b611889565b6001600160a01b031663a47af19e846040518263ffffffff1660e01b815260040161318891815260200190565b6040805180830381600087803b1580156131a157600080fd5b505af11580156131b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d99190614b5e565b9150505b631cd554d160e21b82146105565760006131f5611889565b6001600160a01b031663a47af19e846040518263ffffffff1660e01b815260040161322291815260200190565b6040805180830381600087803b15801561323b57600080fd5b505af115801561324f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132739190614b5e565b915082905080610e5657509392505050565b60045460009067ffffffffffffffff1642146132a157816132be565b6004546132be90600160401b90046001600160c01b03168361250f565b90506132c861445f565b8111156133105760405162461bcd60e51b815260206004820152601660248201527514dd5c9c185cdcd959081d9bdb1d5b59481b1a5b5a5d60521b604482015260640161067f565b6001600160c01b0316600160401b0267ffffffffffffffff42161760045550565b6133396118c8565b6001600160a01b0316633260803983604001516040518263ffffffff1660e01b815260040161336a91815260200190565b60206040518083038186803b15801561338257600080fd5b505afa158015613396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ba9190614d0f565b82516080840151604051632770a7eb60e21b81526001600160a01b0392831660048201526024810191909152911690639dc29fac90604401600060405180830381600087803b15801561340c57600080fd5b505af1158015613420573d6000803e3d6000fd5b5050505081610120015161ffff16600014156135335760006134406118c8565b6001600160a01b0316633260803984606001516040518263ffffffff1660e01b815260040161347191815260200190565b60206040518083038186803b15801561348957600080fd5b505afa15801561349d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c19190614d0f565b602084015160a085015160405163219e412d60e21b81526001600160a01b039283166004820152602481019190915291925082169063867904b490604401600060405180830381600087803b15801561351957600080fd5b505af115801561352d573d6000803e3d6000fd5b50505050505b60008260c001511180156135525750631cd554d160e21b826060015114155b156135fc5761355f612ee1565b606083015160c0840151604051631952982b60e21b815260048101929092526024820152631cd554d160e21b60448201526001600160a01b03919091169063654a60ac9060640160206040518083038186803b1580156135be57600080fd5b505afa1580156135d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f69190614b37565b60c08301525b61360581612642565b82516040808501516060860151608087015160a088015160e08901516101008a015160c08b01516101208c01516101408d01519851632028bac560e21b81526001600160a01b039a8b166004820152602481019890985260448801969096526064870194909452608486019290925260a485015260c484015260e483015261ffff166101048201529015156101248201529116906380a2eb14903490610144016000604051808303818588803b1580156136be57600080fd5b505af11580156136d2573d6000803e3d6000fd5b50505050505050565b61372960405180610100016040528060008019168152602001600081526020016000801916815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008060008060008060008061373d611803565b604051630acc3f5b60e11b81526001600160a01b038e81166004830152602482018e9052604482018d905291909116906315987eb6906064016101006040518083038186803b15801561378f57600080fd5b505afa1580156137a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c79190614f02565b60408051610100810182529889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e08201529c9b505050505050505050505050565b600080600061381d612ee1565b9050600061382961305b565b855160c087015160a088015160405163084f235160e11b8152600481019390935260248301919091526044820152606481018290529091506001600160a01b0383169063109e46a29060840160206040518083038186803b15801561388d57600080fd5b505afa1580156138a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c59190614b37565b60408681015160e088015160a0890151925163084f235160e11b8152600481019290925260248201526044810191909152606481018390529094506001600160a01b0383169063109e46a29060840160206040518083038186803b15801561392c57600080fd5b505afa158015613940573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139649190614b37565b92505050915091565b6000613977614442565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b6e65786368616e67654665655261746560881b856040516020016139c5929190918252602082015260400190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401613a01929190918252602082015260400190565b60206040518083038186803b158015613a1957600080fd5b505afa158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105569190614b37565b613a596118c8565b6001600160a01b03166332608039836040518263ffffffff1660e01b8152600401613a8691815260200190565b60206040518083038186803b158015613a9e57600080fd5b505afa158015613ab2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ad69190614d0f565b604051632770a7eb60e21b81526001600160a01b038581166004830152602482018490529190911690639dc29fac90604401600060405180830381600087803b158015613b2257600080fd5b505af1158015613b36573d6000803e3d6000fd5b505060408051858152602081018590526001600160a01b03871693507f491df6adf9cabe8ca514806effd6b6b6475572dc88fe4b8b58d0a20ecf45e1059250015b60405180910390a2505050565b613b8c6118c8565b6001600160a01b03166332608039836040518263ffffffff1660e01b8152600401613bb991815260200190565b60206040518083038186803b158015613bd157600080fd5b505afa158015613be5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c099190614d0f565b60405163219e412d60e21b81526001600160a01b03858116600483015260248201849052919091169063867904b490604401600060405180830381600087803b158015613c5557600080fd5b505af1158015613c69573d6000803e3d6000fd5b505060408051858152602081018590526001600160a01b03871693507f93751433c6897553c8950f14ccc193ccffb8f539f7421ffde9af83b9b7dae1a8925001613b77565b600080613cc6613cbd846144ce565b61221e866144ce565b90508061055357610e56613cd98461396d565b61221e8661396d565b6040805160028082526060820183526000926020830190803683370190505090508481600081518110613d1757613d17614d2c565b6020026020010181815250508381600181518110613d3757613d37614d2c565b6020908102919091010152604080516002808252606082019092526000918160200160208202803683370190505090508381600081518110613d7b57613d7b614d2c565b6020026020010181815250508281600181518110613d9b57613d9b614d2c565b602002602001018181525050613daf612ee1565b6001600160a01b031663d89ee86183836040518363ffffffff1660e01b8152600401613ddc929190614f5f565b60206040518083038186803b158015613df457600080fd5b505afa158015613e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e2c9190614cf2565b15613e795760405162461bcd60e51b815260206004820152601a60248201527f7372632f646573742076616c756520697320696e76616c69642e000000000000604482015260640161067f565b505050505050565b6000806000613e9b613e928761396d565b61221e8961396d565b90506000613eab8888888861452c565b93509050613eb9828261250f565b9350505094509492505050565b6000613ed0612ee1565b604051633d00c50f60e11b8152600481018890529091506000906001600160a01b03831690637a018a1e9060240160206040518083038186803b158015613f1657600080fd5b505afa158015613f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4e9190614b37565b604051633d00c50f60e11b8152600481018790529091506000906001600160a01b03841690637a018a1e9060240160206040518083038186803b158015613f9457600080fd5b505afa158015613fa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fcc9190614b37565b9050613fd6611803565b604051630f2a761760e21b81526001600160a01b038b81166004830152602482018b9052604482018a9052606482018990526084820188905260a482018790524260c483015260e4820185905261010482018490529190911690633ca9d85c9061012401600060405180830381600087803b15801561405457600080fd5b505af1158015614068573d6000803e3d6000fd5b5050604080518b8152602081018b9052908101899052606081018890526080810187905260a0810185905260c081018490526001600160a01b038c1692507f62e40d554c7abcdd31074960d8347a2225daeb04d93bc748f049ba2ce9462398915060e00160405180910390a2505050505050505050565b61410a6040518060800160405280600081526020016000815260200160008152602001600081525090565b60408051600480825260a08201909252600091602082016080803683370190505090507f65786368616e676544796e616d69634665655468726573686f6c6400000000008160008151811061416157614161614d2c565b6020026020010181815250507f65786368616e676544796e616d69634665655765696768744465636179000000816001815181106141a1576141a1614d2c565b6020026020010181815250507f65786368616e676544796e616d6963466565526f756e64730000000000000000816002815181106141e1576141e1614d2c565b6020026020010181815250507465786368616e67654d617844796e616d696346656560581b8160038151811061421957614219614d2c565b602002602001018181525050600061422f614442565b6001600160a01b031663b67fa7ed6d53797374656d53657474696e677360901b846040518363ffffffff1660e01b815260040161426d929190614fb6565b60006040518083038186803b15801561428557600080fd5b505afa158015614299573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526142c1919081019061506a565b90506040518060800160405280826000815181106142e1576142e1614d2c565b602002602001015181526020018260018151811061430157614301614d2c565b602002602001015181526020018260028151811061432157614321614d2c565b602002602001015181526020018260038151811061434157614341614d2c565b60200260200101518152509250505090565b600080631cd554d160e21b84148061437057506001836040015111155b1561438057506000905080611437565b600061438a612ee1565b6001600160a01b0316637a018a1e866040518263ffffffff1660e01b81526004016143b791815260200190565b60206040518083038186803b1580156143cf57600080fd5b505afa1580156143e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144079190614b37565b90506144148582866145ac565b92509250509250929050565b600061442e6012600a615183565b61443884846146b6565b610553919061518f565b600061181e6e466c657869626c6553746f7261676560881b612fe6565b6000614469614442565b6040516323257c2b60e01b81526d53797374656d53657474696e677360901b60048201527f61746f6d69634d6178566f6c756d65506572426c6f636b00000000000000000060248201526001600160a01b0391909116906323257c2b906044016130ba565b60006144d8614442565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7461746f6d696345786368616e67654665655261746560581b856040516020016139c5929190918252602082015260400190565b60008060006145396140df565b90506000806145498887856145ac565b9150915060008061455b8b8a876145ac565b909250905061456a848361250f565b606086015190975087118061457f5787614585565b85606001515b97508781806145915750845b806145995750825b9750975050505050505094509492505050565b600080631cd554d160e21b8514806145c957506001836040015111155b156145d9575060009050806146ae565b60606145e3612ee1565b6040808601519051630ed7624560e41b8152600481018990526024810191909152604481018790526001600160a01b03919091169063ed7624509060640160006040518083038186803b15801561463957600080fd5b505afa15801561464d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261467591908101906151b1565b508091505061468d8185600001518660200151614735565b60608501519093508311806146a257836146a8565b84606001515b93509150505b935093915050565b6000826146c557506000610556565b60006146d18385615215565b9050826146de858361518f565b146105535760405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b606482015260840161067f565b600083516000141561474957506000611868565b6000806001865161475a9190614ed8565b90505b8015610dc65761476d8285614420565b915060006147b887614780600185614ed8565b8151811061479057614790614d2c565b60200260200101518884815181106147aa576147aa614d2c565b6020026020010151886147da565b90506147c4838261250f565b92505080806147d290615234565b91505061475d565b6000826147e957506000611868565b6000838511614801576147fc8585614ed8565b61480b565b61480b8486614ed8565b90506000614819828661483d565b9050838111614829576000614833565b6148338482614ed8565b9695505050505050565b6000610553826148596148526012600a615183565b86906146b6565b9060008082116148ab5760405162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015260640161067f565b6000610e56838561518f565b6001600160a01b03811681146148cc57600080fd5b50565b80356148da816148b7565b919050565b600080604083850312156148f257600080fd5b82356148fd816148b7565b946020939093013593505050565b60006020828403121561491d57600080fd5b5035919050565b6000610160828403121561493757600080fd5b50919050565b60008060006101a0848603121561495357600080fd5b833592506149648560208601614924565b915061018084013590509250925092565b60008060006060848603121561498a57600080fd5b8335614995816148b7565b95602085013595506040909401359392505050565b6000602082840312156149bc57600080fd5b8135610553816148b7565b600080604083850312156149da57600080fd5b50508035926020909101359150565b600080600080608085870312156149ff57600080fd5b8435614a0a816148b7565b966020860135965060408601359560600135945092505050565b803561ffff811681146148da57600080fd5b60008060008060008060c08789031215614a4f57600080fd5b8635614a5a816148b7565b955060208701359450604087013593506060870135925060808701359150614a8460a08801614a24565b90509295509295509295565b600081518084526020808501945080840160005b83811015614ac057815187529582019590820190600101614aa4565b509495945050505050565b6020815260006105536020830184614a90565b6000806101808385031215614af257600080fd5b614afc8484614924565b94610160939093013593505050565b600080600060608486031215614b2057600080fd5b505081359360208301359350604090920135919050565b600060208284031215614b4957600080fd5b5051919050565b80151581146148cc57600080fd5b60008060408385031215614b7157600080fd5b825191506020830151614b8381614b50565b809150509250929050565b60208082526049908201527f45786368616e6765723a204f6e6c79207772617070656453796e746872206f7260408201527f20612073796e746820636f6e74726163742063616e20706572666f726d20746860608201526834b99030b1ba34b7b760b91b608082015260a00190565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715614c3757614c37614bfd565b60405290565b80356148da81614b50565b60006101608284031215614c5b57600080fd5b614c63614c13565b614c6c836148cf565b8152614c7a602084016148cf565b602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e0820152610100808401358183015250610120614cd5818501614a24565b90820152610140614ce7848201614c3d565b908201529392505050565b600060208284031215614d0457600080fd5b815161055381614b50565b600060208284031215614d2157600080fd5b8151610553816148b7565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614d6c57614d6c614d42565b5060010190565b6000815180845260005b81811015614d9957602081850181015186830182015201614d7d565b81811115614dab576000602083870101525b50601f01601f19169290920160200192915050565b600061010061ffff808c168452816020850152614ddf8285018c614d73565b604085019a909a5260608401989098525050608081019490945260a084019290925260c083015290911660e09091015292915050565b606081526000614e286060830186614d73565b61ffff94851660208401529290931660409091015292915050565b828152604060208201526000610e566040830184614d73565b600080600060608486031215614e7157600080fd5b8351925060208401519150604084015190509250925092565b60008219821115614e9d57614e9d614d42565b500190565b60008060008060808587031215614eb857600080fd5b505082516020840151604085015160609095015191969095509092509050565b600082821015614eea57614eea614d42565b500390565b6020815260006105536020830184614d73565b600080600080600080600080610100898b031215614f1f57600080fd5b505086516020880151604089015160608a015160808b015160a08c015160c08d015160e0909d0151959e949d50929b919a50985090965094509092509050565b604081526000614f726040830185614a90565b82810360208481019190915284518083528582019282019060005b81811015614fa957845183529383019391830191600101614f8d565b5090979650505050505050565b828152604060208201526000610e566040830184614a90565b600082601f830112614fe057600080fd5b8151602067ffffffffffffffff80831115614ffd57614ffd614bfd565b8260051b604051601f19603f8301168101818110848211171561502257615022614bfd565b60405293845285810183019383810192508785111561504057600080fd5b83870191505b8482101561505f57815183529183019190830190615046565b979650505050505050565b60006020828403121561507c57600080fd5b815167ffffffffffffffff81111561509357600080fd5b610e5684828501614fcf565b600181815b808511156150da5781600019048211156150c0576150c0614d42565b808516156150cd57918102915b93841c93908002906150a4565b509250929050565b6000826150f157506001610556565b816150fe57506000610556565b8160018114615114576002811461511e5761513a565b6001915050610556565b60ff84111561512f5761512f614d42565b50506001821b610556565b5060208310610133831016604e8410600b841016171561515d575081810a610556565b615167838361509f565b806000190482111561517b5761517b614d42565b029392505050565b600061055383836150e2565b6000826151ac57634e487b7160e01b600052601260045260246000fd5b500490565b600080604083850312156151c457600080fd5b825167ffffffffffffffff808211156151dc57600080fd5b6151e886838701614fcf565b935060208501519150808211156151fe57600080fd5b5061520b85828601614fcf565b9150509250929050565b600081600019048311821515161561522f5761522f614d42565b500290565b60008161524357615243614d42565b50600019019056fea26469706673582212205e97664359ef89e58d79a3d91702723d13c434c02e8ab4116bfea81d532c41cc64736f6c634300080900330000000000000000000000006f808ae3445a711ecaa4da5c8330b051541a4de000000000000000000000000014611eee8b9586d4af1f651682dca8e2c9daa234

Deployed Bytecode

0x60806040526004361061014b5760003560e01c806357af302c116100b65780638da5cb5b1161006f5780638da5cb5b146103bc5780638e52049c146103dc57806397935a3d14610437578063c39def0b1461044a578063dc703e731461047f578063f450aa341461049f57600080fd5b806357af302c14610310578063614d08f81461033057806364b6aef814610350578063741853601461037057806379ba509714610385578063899ffef41461039a57600080fd5b806319d5c6651161010857806319d5c665146102305780631a5c60951461026b5780631b16802c1461028b5780632af64bd3146102ab5780634c268fc8146102d057806353a47bb7146102f057600080fd5b806304f3bcec14610150578063059c29ec1461018d5780630b9e31c9146101bb5780630be5396a146101dd57806314ef073a146101f05780631627540c14610210575b600080fd5b34801561015c57600080fd5b50600254610170906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561019957600080fd5b506101ad6101a83660046148df565b6104bf565b604051908152602001610184565b3480156101c757600080fd5b506101db6101d636600461490b565b61055c565b005b6101ad6101eb36600461493d565b61068c565b3480156101fc57600080fd5b506101db61020b366004614975565b6107e6565b34801561021c57600080fd5b506101db61022b3660046149aa565b610a37565b34801561023c57600080fd5b5061025061024b3660046148df565b610a93565b60408051938452602084019290925290820152606001610184565b34801561027757600080fd5b506101ad6102863660046149c7565b610ab1565b34801561029757600080fd5b506102506102a63660046148df565b610b01565b3480156102b757600080fd5b506102c0610b82565b6040519015158152602001610184565b3480156102dc57600080fd5b506101ad6102eb3660046149e9565b610ca2565b3480156102fc57600080fd5b50600154610170906001600160a01b031681565b34801561031c57600080fd5b506102c061032b36600461490b565b610dcf565b34801561033c57600080fd5b506101ad6822bc31b430b733b2b960b91b81565b34801561035c57600080fd5b506101ad61036b366004614a36565b610e5e565b34801561037c57600080fd5b506101db610f6d565b34801561039157600080fd5b506101db6110fd565b3480156103a657600080fd5b506103af6111e7565b6040516101849190614acb565b3480156103c857600080fd5b50600054610170906001600160a01b031681565b3480156103e857600080fd5b5060045461040f9067ffffffffffffffff811690600160401b90046001600160c01b031682565b6040805167ffffffffffffffff90931683526001600160c01b03909116602083015201610184565b6101ad610445366004614ade565b61134c565b34801561045657600080fd5b5061046a6104653660046149c7565b611425565b60408051928352901515602083015201610184565b34801561048b57600080fd5b5061025061049a366004614b0b565b61143e565b3480156104ab57600080fd5b506102506104ba366004614b0b565b611461565b60006105536104cc611803565b604051631e280db960e31b81526001600160a01b03868116600483015260248201869052919091169063f1406dc89060440160206040518083038186803b15801561051657600080fd5b505afa15801561052a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061054e9190614b37565b611823565b90505b92915050565b61056461186f565b6001600160a01b031663086dabd16040518163ffffffff1660e01b815260040160006040518083038186803b15801561059c57600080fd5b505afa1580156105b0573d6000803e3d6000fd5b5050505060006105be611889565b6001600160a01b031663a47af19e836040518263ffffffff1660e01b81526004016105eb91815260200190565b6040805180830381600087803b15801561060457600080fd5b505af1158015610618573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061063c9190614b5e565b915050806106885760405162461bcd60e51b815260206004820152601460248201527314de5b9d1a081c1c9a58d9481a5cc81d985b1a5960621b60448201526064015b60405180910390fd5b5050565b6000806106976118ad565b9050336001600160a01b0382161480610731575060006106b56118c8565b6040516316b2213f60e01b81523360048201526001600160a01b0391909116906316b2213f9060240160206040518083038186803b1580156106f657600080fd5b505afa15801561070a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072e9190614b37565b14155b61074d5760405162461bcd60e51b815260040161067f90614b8e565b61076561075f36869003860186614c48565b846118dc565b509150848210156107de5760405162461bcd60e51b815260206004820152603a60248201527f54686520616d6f756e742072656365697665642069732062656c6f772074686560448201527f206d696e696d756d20616d6f756e74207370656369666965642e000000000000606482015260840161067f565b509392505050565b600254604051633933006760e11b81523360048201526001600160a01b039091169063726600ce9060240160206040518083038186803b15801561082957600080fd5b505afa15801561083d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108619190614cf2565b6108bc5760405162461bcd60e51b815260206004820152602660248201527f45786368616e6765723a204f6e6c792073796e746872206272696467652063616044820152651b8818d85b1b60d21b606482015260840161067f565b816109005760405162461bcd60e51b81526020600482015260146024820152733232b9ba1035b2bc903234b23713ba1039b2ba1760611b604482015260640161067f565b600061090a6118c8565b6001600160a01b03166332608039846040518263ffffffff1660e01b815260040161093791815260200190565b60206040518083038186803b15801561094f57600080fd5b505afa158015610963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109879190614d0f565b60405163219e412d60e21b81526001600160a01b038681166004830152602482018590529192509082169063867904b490604401600060405180830381600087803b1580156109d557600080fd5b505af11580156109e9573d6000803e3d6000fd5b505060408051868152602081018690526001600160a01b03881693507f6aef294c17150fbb566095411a70428ebfaac62db39b78dd3a83afc9456c78b692500160405180910390a250505050565b610a3f611df0565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6000806000610aa28585611e64565b50919790965090945092505050565b6000806000610ac08585612204565b9150915080156107de5760405162461bcd60e51b815260206004820152600c60248201526b746f6f20766f6c6174696c6560a01b604482015260640161067f565b6000806000610b0e61186f565b6001600160a01b03166342a28e21856040518263ffffffff1660e01b8152600401610b3b91815260200190565b60006040518083038186803b158015610b5357600080fd5b505afa158015610b67573d6000803e3d6000fd5b50505050610b75858561224b565b9250925092509250925092565b600080610b8d6111e7565b905060005b8151811015610c99576000828281518110610baf57610baf614d2c565b602090810291909101810151600081815260039092526040918290205460025492516321f8a72160e01b8152600481018390529193506001600160a01b039081169216906321f8a7219060240160206040518083038186803b158015610c1457600080fd5b505afa158015610c28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4c9190614d0f565b6001600160a01b0316141580610c7757506000818152600360205260409020546001600160a01b0316155b15610c86576000935050505090565b5080610c9181614d58565b915050610b92565b50600191505090565b816000610cad6118c8565b6001600160a01b03166332608039866040518263ffffffff1660e01b8152600401610cda91815260200190565b60206040518083038186803b158015610cf257600080fd5b505afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190614d0f565b6040516370a0823160e01b81526001600160a01b03888116600483015291909116906370a082319060240160206040518083038186803b158015610d6d57600080fd5b505afa158015610d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da59190614b37565b90508215610dba57610db7828461250f565b91505b80821115610dc6578091505b50949350505050565b600080610dda611889565b6001600160a01b031663cb1ec317846040518263ffffffff1660e01b8152600401610e0791815260200190565b604080518083038186803b158015610e1e57600080fd5b505afa158015610e32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e569190614b5e565b949350505050565b6000806000610e6e87898861256e565b50506040805160608f901b6bffffffffffffffffffffffff191660208201528151601481830301815260348201909252949650929450600093610ec5936005935091508c908c908c90899089908d90605401614dc0565b6040516020818303038152906040529050610edf86612642565b6001600160a01b0316632bb821bd826005886040518463ffffffff1660e01b8152600401610f0f93929190614e15565b60206040518083038186803b158015610f2757600080fd5b505afa158015610f3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5f9190614b37565b9a9950505050505050505050565b6000610f776111e7565b905060005b8151811015610688576000828281518110610f9957610f99614d2c565b602002602001015190506000600260009054906101000a90046001600160a01b03166001600160a01b031663dacb2d01838460405160200161100791907f5265736f6c766572206d697373696e67207461726765743a20000000000000008152601981019190915260390190565b6040516020818303038152906040526040518363ffffffff1660e01b8152600401611033929190614e43565b60206040518083038186803b15801561104b57600080fd5b505afa15801561105f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110839190614d0f565b60008381526003602090815260409182902080546001600160a01b0319166001600160a01b0385169081179091558251868152918201529192507f88a93678a3692f6789d9546fc621bf7234b101ddb7d4fe479455112831b8aa68910160405180910390a1505080806110f590614d58565b915050610f7c565b6001546001600160a01b031633146111755760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b606482015260840161067f565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b606060006111f36126be565b60408051600680825260e08201909252919250600091906020820160c0803683370190505090506b53797374656d53746174757360a01b8160008151811061123d5761123d614d2c565b6020026020010181815250506c45786368616e6765537461746560981b8160018151811061126d5761126d614d2c565b6020026020010181815250506c45786368616e6765526174657360981b8160028151811061129d5761129d614d2c565b6020026020010181815250506c2bb930b83832b229bcb73a343960991b816003815181106112cd576112cd614d2c565b6020026020010181815250506524b9b9bab2b960d11b816004815181106112f6576112f6614d2c565b6020026020010181815250507522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b8160058151811061132f5761132f614d2c565b6020026020010181815250506113458282612715565b9250505090565b6000806113576118ad565b9050336001600160a01b03821614806113f1575060006113756118c8565b6040516316b2213f60e01b81523360048201526001600160a01b0391909116906316b2213f9060240160206040518083038186803b1580156113b657600080fd5b505afa1580156113ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ee9190614b37565b14155b61140d5760405162461bcd60e51b815260040161067f90614b8e565b610dc661141f36869003860186614c48565b84612827565b6000806114328484612cf0565b915091505b9250929050565b600080600061144e86868661256e565b50939a9299509097509095505050505050565b600080600061146e61186f565b6001600160a01b03166342a28e21866040518263ffffffff1660e01b815260040161149b91815260200190565b60006040518083038186803b1580156114b357600080fd5b505afa1580156114c7573d6000803e3d6000fd5b505050506114d361186f565b6001600160a01b03166342a28e21856040518263ffffffff1660e01b815260040161150091815260200190565b60006040518083038186803b15801561151857600080fd5b505afa15801561152c573d6000803e3d6000fd5b50505050600061153a611889565b6001600160a01b031663cb1ec317876040518263ffffffff1660e01b815260040161156791815260200190565b604080518083038186803b15801561157e57600080fd5b505afa158015611592573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b69190614b5e565b91505060006115c3611889565b6001600160a01b031663cb1ec317876040518263ffffffff1660e01b81526004016115f091815260200190565b604080518083038186803b15801561160757600080fd5b505afa15801561161b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163f9190614b5e565b91505081156116905760405162461bcd60e51b815260206004820152601960248201527f736f757263652073796e7468207261746520696e76616c696400000000000000604482015260640161067f565b80156116de5760405162461bcd60e51b815260206004820152601e60248201527f64657374696e6174696f6e2073796e7468207261746520696e76616c69640000604482015260640161067f565b6116e9878988612d6c565b60006116f58888612204565b909450905080156117485760405162461bcd60e51b815260206004820152601b60248201527f65786368616e676520726174657320746f6f20766f6c6174696c650000000000604482015260640161067f565b6000611752612ee1565b60405163414a80b560e11b8152600481018b9052602481018c9052604481018a90526001600160a01b039190911690638295016a9060640160606040518083038186803b1580156117a257600080fd5b505afa1580156117b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117da9190614e5c565b505090506117e88186612efc565b96506117f48188612f88565b95505050505093509350939050565b600061181e6c45786368616e6765537461746560981b612fe6565b905090565b60008061182e61305b565b90508215806118465750611842838261250f565b4210155b156118545750600092915050565b61186842611862858461250f565b90612f88565b9392505050565b600061181e6b53797374656d53746174757360a01b612fe6565b600061181e7522bc31b430b733b2a1b4b931bab4ba213932b0b5b2b960511b612fe6565b600061181e6c2bb930b83832b229bcb73a343960991b612fe6565b600061181e6524b9b9bab2b960d11b612fe6565b60008060008490506118fb816040015182608001518360600151612d6c565b611903612ee1565b6001600160a01b0316638661cc7b82604001516040518263ffffffff1660e01b815260040161193491815260200190565b60206040518083038186803b15801561194c57600080fd5b505afa158015611960573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119849190614cf2565b156119d15760405162461bcd60e51b815260206004820152601c60248201527f5372632073796e74682076616c756520697320766f6c6174696c652e00000000604482015260640161067f565b6119d9612ee1565b6001600160a01b0316638661cc7b82606001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b60206040518083038186803b158015611a2257600080fd5b505afa158015611a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5a9190614cf2565b15611aa75760405162461bcd60e51b815260206004820152601d60248201527f446573742073796e74682076616c756520697320766f6c6174696c652e000000604482015260640161067f565b611abe81608001518260000151836040015161310a565b61010084015260e083015260808201819052611ae1576000809250925050611437565b600080600080611afe85608001518660400151876060015161256e565b60a08b0186905260c08b0185905260408b015160608c0151969d50949b5092985090965094509250611b2f91613143565b15611b4557600080965096505050505050611437565b611b4d611889565b6001600160a01b03166378cb51cb84611b668a8a61250f565b6040516001600160e01b031960e085901b1681526004810192909252602482015260440160206040518083038186803b158015611ba257600080fd5b505afa158015611bb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bda9190614cf2565b15611c275760405162461bcd60e51b815260206004820152601d60248201527f41746f6d6963207261746520646576696174657320746f6f206d756368000000604482015260640161067f565b6000631cd554d160e21b86604001511415611c4757506080890151611c9e565b631cd554d160e21b86606001511415611c61575082611c9e565b600080611c7e8c608001518960400151631cd554d160e21b61256e565b5050505091509150611c99818361250f90919063ffffffff16565b925050505b611ca781613285565b611cb1868a613331565b85602001516001600160a01b031686600001516001600160a01b03167f44dad12059e2c487e34548d3f2853cd851983ebd74f59ae89f55db7f0f5234ea886040015189608001518a606001518d8c60c001518d6101200151604051611d4296959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a385602001516001600160a01b031686600001516001600160a01b03167fa15ae7ed6a9ceafa6788f8020c73234a073e5fd311aca9b3250c93d374c07785886040015189608001518a606001518d8c60c001518d6101200151604051611ddb96959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a35050505050509250929050565b6000546001600160a01b03163314611e625760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b606482015260840161067f565b565b60008060006060611e73611803565b60405163b44e975360e01b81526001600160a01b03888116600483015260248201889052919091169063b44e97539060440160206040518083038186803b158015611ebd57600080fd5b505afa158015611ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef59190614b37565b915060008267ffffffffffffffff811115611f1257611f12614bfd565b604051908082528060200260200182016040528015611f9157816020015b611f7e60405180610100016040528060008019168152602001600081526020016000801916815260200160008152602001600081526020016000815260200160008152602001600081525090565b815260200190600190039081611f305790505b50905060005b838110156121f8576000806000611faf8b8b866136db565b9050600080611fbd83613810565b915091506000611fcb612ee1565b84516020860151604080880151905162d9ccd960e71b815260048101939093526024830191909152604482015260648101859052608481018490526001600160a01b039190911690636ce66c809060a40160606040518083038186803b15801561203457600080fd5b505afa158015612048573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206c9190614e5c565b505090506000612080828660800151612efc565b9050600061208c611889565b6001600160a01b03166378cb51cb8760600151846040518363ffffffff1660e01b81526004016120c6929190918252602082015260400190565b60206040518083038186803b1580156120de57600080fd5b505afa1580156120f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121169190614cf2565b90508061217657818660600151111561214b5760608601516121389083612f88565b97506121448e8961250f565b9d50612176565b8560600151821115612176576060860151612167908390612f88565b96506121738d8861250f565b9c505b6040518061010001604052808760000151815260200187602001518152602001876040015181526020018981526020018881526020018681526020018581526020018760a001518152508a8a815181106121d2576121d2614d2c565b6020026020010181905250505050505050505080806121f090614d58565b915050611f97565b50905092959194509250565b60008060006122246122158561396d565b61221e8761396d565b9061250f565b905060006122328686612cf0565b93509050612240828261250f565b935050509250929050565b600080600061225a85856104bf565b156122b35760405162461bcd60e51b815260206004820152602360248201527f43616e6e6f7420736574746c6520647572696e672077616974696e67207065726044820152621a5bd960ea1b606482015260840161067f565b6000806000806122c38989611e64565b9350935093509350828411156122ef576122dd8484612f88565b96506122ea898989613a51565b61230e565b8383111561230e576123018385612f88565b955061230e898988613b84565b60005b815181101561249457896001600160a01b03167f8e3ad1f68bec55de3b6fa12ae2674a2a683a17c918a4cbf5157ac5d9ddc6e94083838151811061235757612357614d2c565b60200260200101516000015184848151811061237557612375614d2c565b60200260200101516020015185858151811061239357612393614d2c565b6020026020010151604001518686815181106123b1576123b1614d2c565b6020026020010151606001518787815181106123cf576123cf614d2c565b6020026020010151608001518888815181106123ed576123ed614d2c565b602002602001015160a0015189898151811061240b5761240b614d2c565b602002602001015160c001518a8a8151811061242957612429614d2c565b602002602001015160e0015160405161247a989796959493929190978852602088019690965260408701949094526060860192909252608085015260a084015260c083015260e08201526101000190565b60405180910390a28061248c81614d58565b915050612311565b508194506124a0611803565b604051636869eb1560e11b81526001600160a01b038b81166004830152602482018b9052919091169063d0d3d62a90604401600060405180830381600087803b1580156124ec57600080fd5b505af1158015612500573d6000803e3d6000fd5b50505050505050509250925092565b60008061251c8385614e8a565b9050838110156105535760405162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015260640161067f565b6000806000806000806000612581612ee1565b604051622a943760e51b8152600481018b9052602481018c9052604481018a90526001600160a01b03919091169063055286e09060640160806040518083038186803b1580156125d057600080fd5b505afa1580156125e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126089190614ea2565b91965094509250905061261b8989613cae565b94506126278186612efc565b96506126338188612f88565b95505093975093979195509350565b60025460405162c8ac9760e41b8152600481018390526000916001600160a01b031690630c8ac9709060240160206040518083038186803b15801561268657600080fd5b505afa15801561269a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105569190614d0f565b604080516001808252818301909252606091602080830190803683370190505090506e466c657869626c6553746f7261676560881b8160008151811061270657612706614d2c565b60200260200101818152505090565b6060815183516127259190614e8a565b67ffffffffffffffff81111561273d5761273d614bfd565b604051908082528060200260200182016040528015612766578160200160208202803683370190505b50905060005b83518110156127be5783818151811061278757612787614d2c565b60200260200101518282815181106127a1576127a1614d2c565b6020908102919091010152806127b681614d58565b91505061276c565b5060005b8251811015612820578281815181106127dd576127dd614d2c565b6020026020010151828286516127f39190614e8a565b8151811061280357612803614d2c565b60209081029190910101528061281881614d58565b9150506127c2565b5092915050565b600080600084608001511161286c5760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b604482015260640161067f565b6128ac6040518060e00160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6128b4612ee1565b6001600160a01b0316637a018a1e86604001516040518263ffffffff1660e01b81526004016128e591815260200190565b60206040518083038186803b1580156128fd57600080fd5b505afa158015612911573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129359190614b37565b60a0820152612942612ee1565b6001600160a01b0316637a018a1e86606001516040518263ffffffff1660e01b815260040161297391815260200190565b60206040518083038186803b15801561298b57600080fd5b505afa15801561299f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c39190614b37565b60c08201526080850151855160408701516129df92919061310a565b61010088015260e087015260808601819052612a02576000809250925050611437565b612a0a612ee1565b6040808701516080880151606089015160a086015160c0870151945162d9ccd960e71b8152600481019490945260248401929092526044830152606482015260848101919091526001600160a01b039190911690636ce66c809060a40160606040518083038186803b158015612a7f57600080fd5b505afa158015612a93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab79190614e5c565b60208401528252604080830191909152850151606086015160a083015160c0840151612ae593929190613ce2565b612af785604001518660600151613143565b15612b09576000809250925050611437565b6000612b27866040015187606001518460a001518560c00151613e81565b606084019190915290508015612b4557600080935093505050611437565b612b5782604001518360600151612efc565b6040830151909450612b699085612f88565b60a0870185905260c087018190529250612b838686613331565b85602001516001600160a01b031686600001516001600160a01b03167f44dad12059e2c487e34548d3f2853cd851983ebd74f59ae89f55db7f0f5234ea886040015189608001518a60600151898c60c001518d6101200151604051612c1496959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a385602001516001600160a01b031686600001516001600160a01b03167fb5826da57a48b0e5d9160b88cdc4a9c303be1746b0619e42e51afe8fe353bbd9886040015189608001518a60600151898c60c001518d6101200151604051612cad96959493929190958652602086019490945260408501929092526060840152608083015261ffff1660a082015260c00190565b60405180910390a36000612cbf61305b565b1115612ce757612ce78660200151876040015188608001518960600151888760600151613ec6565b50509250929050565b6000806000612cfd6140df565b9050600080612d0c8684614353565b91509150600080612d1d8986614353565b9092509050612d2c848361250f565b6060860151909750871180612d415787612d47565b85606001515b9750878180612d535750845b80612d5b5750825b975097505050505050509250929050565b60008211612daa5760405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b604482015260640161067f565b6040805160028082526060820183526000926020830190803683370190505090508381600081518110612ddf57612ddf614d2c565b6020026020010181815250508181600181518110612dff57612dff614d2c565b602002602001018181525050612e13612ee1565b6001600160a01b0316630a7d36d1826040518263ffffffff1660e01b8152600401612e3e9190614acb565b60206040518083038186803b158015612e5657600080fd5b505afa158015612e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e8e9190614cf2565b15612edb5760405162461bcd60e51b815260206004820152601a60248201527f7372632f646573742076616c756520697320696e76616c69642e000000000000604482015260640161067f565b50505050565b600061181e6c45786368616e6765526174657360981b612fe6565b6000610553612f818373a1e534c7ae316eae135b89a76c7335e78fb1681e63907af6c06040518163ffffffff1660e01b815260040160206040518083038186803b158015612f4957600080fd5b505af4158015612f5d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118629190614b37565b8490614420565b600082821115612fda5760405162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015260640161067f565b6000610e568385614ed8565b600081815260036020908152604080832054905170026b4b9b9b4b7339030b2323932b9b99d1607d1b92810192909252603182018490526001600160a01b03169081151590605101604051602081830303815290604052906128205760405162461bcd60e51b815260040161067f9190614eef565b6000613065614442565b6040516323257c2b60e01b81526d53797374656d53657474696e677360901b60048201527077616974696e67506572696f645365637360781b60248201526001600160a01b0391909116906323257c2b906044015b60206040518083038186803b1580156130d257600080fd5b505afa1580156130e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181e9190614b37565b60008060008061311a868661224b565b8996509194509250905080156131395761313686868985610ca2565b93505b5093509350939050565b6000631cd554d160e21b83146131dd5761315b611889565b6001600160a01b031663a47af19e846040518263ffffffff1660e01b815260040161318891815260200190565b6040805180830381600087803b1580156131a157600080fd5b505af11580156131b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131d99190614b5e565b9150505b631cd554d160e21b82146105565760006131f5611889565b6001600160a01b031663a47af19e846040518263ffffffff1660e01b815260040161322291815260200190565b6040805180830381600087803b15801561323b57600080fd5b505af115801561324f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132739190614b5e565b915082905080610e5657509392505050565b60045460009067ffffffffffffffff1642146132a157816132be565b6004546132be90600160401b90046001600160c01b03168361250f565b90506132c861445f565b8111156133105760405162461bcd60e51b815260206004820152601660248201527514dd5c9c185cdcd959081d9bdb1d5b59481b1a5b5a5d60521b604482015260640161067f565b6001600160c01b0316600160401b0267ffffffffffffffff42161760045550565b6133396118c8565b6001600160a01b0316633260803983604001516040518263ffffffff1660e01b815260040161336a91815260200190565b60206040518083038186803b15801561338257600080fd5b505afa158015613396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ba9190614d0f565b82516080840151604051632770a7eb60e21b81526001600160a01b0392831660048201526024810191909152911690639dc29fac90604401600060405180830381600087803b15801561340c57600080fd5b505af1158015613420573d6000803e3d6000fd5b5050505081610120015161ffff16600014156135335760006134406118c8565b6001600160a01b0316633260803984606001516040518263ffffffff1660e01b815260040161347191815260200190565b60206040518083038186803b15801561348957600080fd5b505afa15801561349d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134c19190614d0f565b602084015160a085015160405163219e412d60e21b81526001600160a01b039283166004820152602481019190915291925082169063867904b490604401600060405180830381600087803b15801561351957600080fd5b505af115801561352d573d6000803e3d6000fd5b50505050505b60008260c001511180156135525750631cd554d160e21b826060015114155b156135fc5761355f612ee1565b606083015160c0840151604051631952982b60e21b815260048101929092526024820152631cd554d160e21b60448201526001600160a01b03919091169063654a60ac9060640160206040518083038186803b1580156135be57600080fd5b505afa1580156135d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135f69190614b37565b60c08301525b61360581612642565b82516040808501516060860151608087015160a088015160e08901516101008a015160c08b01516101208c01516101408d01519851632028bac560e21b81526001600160a01b039a8b166004820152602481019890985260448801969096526064870194909452608486019290925260a485015260c484015260e483015261ffff166101048201529015156101248201529116906380a2eb14903490610144016000604051808303818588803b1580156136be57600080fd5b505af11580156136d2573d6000803e3d6000fd5b50505050505050565b61372960405180610100016040528060008019168152602001600081526020016000801916815260200160008152602001600081526020016000815260200160008152602001600081525090565b60008060008060008060008061373d611803565b604051630acc3f5b60e11b81526001600160a01b038e81166004830152602482018e9052604482018d905291909116906315987eb6906064016101006040518083038186803b15801561378f57600080fd5b505afa1580156137a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c79190614f02565b60408051610100810182529889526020890197909752958701949094526060860192909252608085015260a084015260c083015260e08201529c9b505050505050505050505050565b600080600061381d612ee1565b9050600061382961305b565b855160c087015160a088015160405163084f235160e11b8152600481019390935260248301919091526044820152606481018290529091506001600160a01b0383169063109e46a29060840160206040518083038186803b15801561388d57600080fd5b505afa1580156138a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c59190614b37565b60408681015160e088015160a0890151925163084f235160e11b8152600481019290925260248201526044810191909152606481018390529094506001600160a01b0383169063109e46a29060840160206040518083038186803b15801561392c57600080fd5b505afa158015613940573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139649190614b37565b92505050915091565b6000613977614442565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b6e65786368616e67654665655261746560881b856040516020016139c5929190918252602082015260400190565b604051602081830303815290604052805190602001206040518363ffffffff1660e01b8152600401613a01929190918252602082015260400190565b60206040518083038186803b158015613a1957600080fd5b505afa158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105569190614b37565b613a596118c8565b6001600160a01b03166332608039836040518263ffffffff1660e01b8152600401613a8691815260200190565b60206040518083038186803b158015613a9e57600080fd5b505afa158015613ab2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ad69190614d0f565b604051632770a7eb60e21b81526001600160a01b038581166004830152602482018490529190911690639dc29fac90604401600060405180830381600087803b158015613b2257600080fd5b505af1158015613b36573d6000803e3d6000fd5b505060408051858152602081018590526001600160a01b03871693507f491df6adf9cabe8ca514806effd6b6b6475572dc88fe4b8b58d0a20ecf45e1059250015b60405180910390a2505050565b613b8c6118c8565b6001600160a01b03166332608039836040518263ffffffff1660e01b8152600401613bb991815260200190565b60206040518083038186803b158015613bd157600080fd5b505afa158015613be5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c099190614d0f565b60405163219e412d60e21b81526001600160a01b03858116600483015260248201849052919091169063867904b490604401600060405180830381600087803b158015613c5557600080fd5b505af1158015613c69573d6000803e3d6000fd5b505060408051858152602081018590526001600160a01b03871693507f93751433c6897553c8950f14ccc193ccffb8f539f7421ffde9af83b9b7dae1a8925001613b77565b600080613cc6613cbd846144ce565b61221e866144ce565b90508061055357610e56613cd98461396d565b61221e8661396d565b6040805160028082526060820183526000926020830190803683370190505090508481600081518110613d1757613d17614d2c565b6020026020010181815250508381600181518110613d3757613d37614d2c565b6020908102919091010152604080516002808252606082019092526000918160200160208202803683370190505090508381600081518110613d7b57613d7b614d2c565b6020026020010181815250508281600181518110613d9b57613d9b614d2c565b602002602001018181525050613daf612ee1565b6001600160a01b031663d89ee86183836040518363ffffffff1660e01b8152600401613ddc929190614f5f565b60206040518083038186803b158015613df457600080fd5b505afa158015613e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e2c9190614cf2565b15613e795760405162461bcd60e51b815260206004820152601a60248201527f7372632f646573742076616c756520697320696e76616c69642e000000000000604482015260640161067f565b505050505050565b6000806000613e9b613e928761396d565b61221e8961396d565b90506000613eab8888888861452c565b93509050613eb9828261250f565b9350505094509492505050565b6000613ed0612ee1565b604051633d00c50f60e11b8152600481018890529091506000906001600160a01b03831690637a018a1e9060240160206040518083038186803b158015613f1657600080fd5b505afa158015613f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4e9190614b37565b604051633d00c50f60e11b8152600481018790529091506000906001600160a01b03841690637a018a1e9060240160206040518083038186803b158015613f9457600080fd5b505afa158015613fa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fcc9190614b37565b9050613fd6611803565b604051630f2a761760e21b81526001600160a01b038b81166004830152602482018b9052604482018a9052606482018990526084820188905260a482018790524260c483015260e4820185905261010482018490529190911690633ca9d85c9061012401600060405180830381600087803b15801561405457600080fd5b505af1158015614068573d6000803e3d6000fd5b5050604080518b8152602081018b9052908101899052606081018890526080810187905260a0810185905260c081018490526001600160a01b038c1692507f62e40d554c7abcdd31074960d8347a2225daeb04d93bc748f049ba2ce9462398915060e00160405180910390a2505050505050505050565b61410a6040518060800160405280600081526020016000815260200160008152602001600081525090565b60408051600480825260a08201909252600091602082016080803683370190505090507f65786368616e676544796e616d69634665655468726573686f6c6400000000008160008151811061416157614161614d2c565b6020026020010181815250507f65786368616e676544796e616d69634665655765696768744465636179000000816001815181106141a1576141a1614d2c565b6020026020010181815250507f65786368616e676544796e616d6963466565526f756e64730000000000000000816002815181106141e1576141e1614d2c565b6020026020010181815250507465786368616e67654d617844796e616d696346656560581b8160038151811061421957614219614d2c565b602002602001018181525050600061422f614442565b6001600160a01b031663b67fa7ed6d53797374656d53657474696e677360901b846040518363ffffffff1660e01b815260040161426d929190614fb6565b60006040518083038186803b15801561428557600080fd5b505afa158015614299573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526142c1919081019061506a565b90506040518060800160405280826000815181106142e1576142e1614d2c565b602002602001015181526020018260018151811061430157614301614d2c565b602002602001015181526020018260028151811061432157614321614d2c565b602002602001015181526020018260038151811061434157614341614d2c565b60200260200101518152509250505090565b600080631cd554d160e21b84148061437057506001836040015111155b1561438057506000905080611437565b600061438a612ee1565b6001600160a01b0316637a018a1e866040518263ffffffff1660e01b81526004016143b791815260200190565b60206040518083038186803b1580156143cf57600080fd5b505afa1580156143e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144079190614b37565b90506144148582866145ac565b92509250509250929050565b600061442e6012600a615183565b61443884846146b6565b610553919061518f565b600061181e6e466c657869626c6553746f7261676560881b612fe6565b6000614469614442565b6040516323257c2b60e01b81526d53797374656d53657474696e677360901b60048201527f61746f6d69634d6178566f6c756d65506572426c6f636b00000000000000000060248201526001600160a01b0391909116906323257c2b906044016130ba565b60006144d8614442565b6001600160a01b03166323257c2b6d53797374656d53657474696e677360901b7461746f6d696345786368616e67654665655261746560581b856040516020016139c5929190918252602082015260400190565b60008060006145396140df565b90506000806145498887856145ac565b9150915060008061455b8b8a876145ac565b909250905061456a848361250f565b606086015190975087118061457f5787614585565b85606001515b97508781806145915750845b806145995750825b9750975050505050505094509492505050565b600080631cd554d160e21b8514806145c957506001836040015111155b156145d9575060009050806146ae565b60606145e3612ee1565b6040808601519051630ed7624560e41b8152600481018990526024810191909152604481018790526001600160a01b03919091169063ed7624509060640160006040518083038186803b15801561463957600080fd5b505afa15801561464d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261467591908101906151b1565b508091505061468d8185600001518660200151614735565b60608501519093508311806146a257836146a8565b84606001515b93509150505b935093915050565b6000826146c557506000610556565b60006146d18385615215565b9050826146de858361518f565b146105535760405162461bcd60e51b815260206004820152602160248201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6044820152607760f81b606482015260840161067f565b600083516000141561474957506000611868565b6000806001865161475a9190614ed8565b90505b8015610dc65761476d8285614420565b915060006147b887614780600185614ed8565b8151811061479057614790614d2c565b60200260200101518884815181106147aa576147aa614d2c565b6020026020010151886147da565b90506147c4838261250f565b92505080806147d290615234565b91505061475d565b6000826147e957506000611868565b6000838511614801576147fc8585614ed8565b61480b565b61480b8486614ed8565b90506000614819828661483d565b9050838111614829576000614833565b6148338482614ed8565b9695505050505050565b6000610553826148596148526012600a615183565b86906146b6565b9060008082116148ab5760405162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015260640161067f565b6000610e56838561518f565b6001600160a01b03811681146148cc57600080fd5b50565b80356148da816148b7565b919050565b600080604083850312156148f257600080fd5b82356148fd816148b7565b946020939093013593505050565b60006020828403121561491d57600080fd5b5035919050565b6000610160828403121561493757600080fd5b50919050565b60008060006101a0848603121561495357600080fd5b833592506149648560208601614924565b915061018084013590509250925092565b60008060006060848603121561498a57600080fd5b8335614995816148b7565b95602085013595506040909401359392505050565b6000602082840312156149bc57600080fd5b8135610553816148b7565b600080604083850312156149da57600080fd5b50508035926020909101359150565b600080600080608085870312156149ff57600080fd5b8435614a0a816148b7565b966020860135965060408601359560600135945092505050565b803561ffff811681146148da57600080fd5b60008060008060008060c08789031215614a4f57600080fd5b8635614a5a816148b7565b955060208701359450604087013593506060870135925060808701359150614a8460a08801614a24565b90509295509295509295565b600081518084526020808501945080840160005b83811015614ac057815187529582019590820190600101614aa4565b509495945050505050565b6020815260006105536020830184614a90565b6000806101808385031215614af257600080fd5b614afc8484614924565b94610160939093013593505050565b600080600060608486031215614b2057600080fd5b505081359360208301359350604090920135919050565b600060208284031215614b4957600080fd5b5051919050565b80151581146148cc57600080fd5b60008060408385031215614b7157600080fd5b825191506020830151614b8381614b50565b809150509250929050565b60208082526049908201527f45786368616e6765723a204f6e6c79207772617070656453796e746872206f7260408201527f20612073796e746820636f6e74726163742063616e20706572666f726d20746860608201526834b99030b1ba34b7b760b91b608082015260a00190565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715614c3757614c37614bfd565b60405290565b80356148da81614b50565b60006101608284031215614c5b57600080fd5b614c63614c13565b614c6c836148cf565b8152614c7a602084016148cf565b602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c082015260e083013560e0820152610100808401358183015250610120614cd5818501614a24565b90820152610140614ce7848201614c3d565b908201529392505050565b600060208284031215614d0457600080fd5b815161055381614b50565b600060208284031215614d2157600080fd5b8151610553816148b7565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415614d6c57614d6c614d42565b5060010190565b6000815180845260005b81811015614d9957602081850181015186830182015201614d7d565b81811115614dab576000602083870101525b50601f01601f19169290920160200192915050565b600061010061ffff808c168452816020850152614ddf8285018c614d73565b604085019a909a5260608401989098525050608081019490945260a084019290925260c083015290911660e09091015292915050565b606081526000614e286060830186614d73565b61ffff94851660208401529290931660409091015292915050565b828152604060208201526000610e566040830184614d73565b600080600060608486031215614e7157600080fd5b8351925060208401519150604084015190509250925092565b60008219821115614e9d57614e9d614d42565b500190565b60008060008060808587031215614eb857600080fd5b505082516020840151604085015160609095015191969095509092509050565b600082821015614eea57614eea614d42565b500390565b6020815260006105536020830184614d73565b600080600080600080600080610100898b031215614f1f57600080fd5b505086516020880151604089015160608a015160808b015160a08c015160c08d015160e0909d0151959e949d50929b919a50985090965094509092509050565b604081526000614f726040830185614a90565b82810360208481019190915284518083528582019282019060005b81811015614fa957845183529383019391830191600101614f8d565b5090979650505050505050565b828152604060208201526000610e566040830184614a90565b600082601f830112614fe057600080fd5b8151602067ffffffffffffffff80831115614ffd57614ffd614bfd565b8260051b604051601f19603f8301168101818110848211171561502257615022614bfd565b60405293845285810183019383810192508785111561504057600080fd5b83870191505b8482101561505f57815183529183019190830190615046565b979650505050505050565b60006020828403121561507c57600080fd5b815167ffffffffffffffff81111561509357600080fd5b610e5684828501614fcf565b600181815b808511156150da5781600019048211156150c0576150c0614d42565b808516156150cd57918102915b93841c93908002906150a4565b509250929050565b6000826150f157506001610556565b816150fe57506000610556565b8160018114615114576002811461511e5761513a565b6001915050610556565b60ff84111561512f5761512f614d42565b50506001821b610556565b5060208310610133831016604e8410600b841016171561515d575081810a610556565b615167838361509f565b806000190482111561517b5761517b614d42565b029392505050565b600061055383836150e2565b6000826151ac57634e487b7160e01b600052601260045260246000fd5b500490565b600080604083850312156151c457600080fd5b825167ffffffffffffffff808211156151dc57600080fd5b6151e886838701614fcf565b935060208501519150808211156151fe57600080fd5b5061520b85828601614fcf565b9150509250929050565b600081600019048311821515161561522f5761522f614d42565b500290565b60008161524357615243614d42565b50600019019056fea26469706673582212205e97664359ef89e58d79a3d91702723d13c434c02e8ab4116bfea81d532c41cc64736f6c63430008090033

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

0000000000000000000000006f808ae3445a711ecaa4da5c8330b051541a4de000000000000000000000000014611eee8b9586d4af1f651682dca8e2c9daa234

-----Decoded View---------------
Arg [0] : _owner (address): 0x6F808aE3445A711ecAa4DA5c8330B051541A4dE0
Arg [1] : _resolver (address): 0x14611Eee8b9586d4AF1F651682dCA8e2c9DAA234

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000006f808ae3445a711ecaa4da5c8330b051541a4de0
Arg [1] : 00000000000000000000000014611eee8b9586d4af1f651682dca8e2c9daa234


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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