Contract 0x371bfce7688d29fd23e83f673ec4521b844feb1a

Contract Overview

Balance:
0 BNB
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xaa0c7c886356043606df729b7f7ffb6bdf5d19353f2d2804a0887f5566c8a3b1Approve93168352021-05-31 7:46:38117 days 20 hrs ago0x4bd368aaf89f02a9da0ed80f7cd0b7e0e8bc388b IN  0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB0.00065502
0x32fbcfcf99b75ddcdcd50cd22ed68e345ebce8fd72dcfafd1071ade39f2d7879Approve93166842021-05-31 7:36:52117 days 20 hrs ago0x4bd368aaf89f02a9da0ed80f7cd0b7e0e8bc388b IN  0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB0.0006561
0x32b7baa30f4c911d9cca6520b6437d35fb25bda4cc6f090edcb78dc846d645a7Approve93166752021-05-31 7:36:22117 days 20 hrs ago0x4bd368aaf89f02a9da0ed80f7cd0b7e0e8bc388b IN  0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB0.0006561
0x2e110da66bb5a17e819fdf25bfe39adc7f4a096cf2df8aec0bbfecebde74854aApprove93166632021-05-31 7:35:33117 days 20 hrs ago0x4bd368aaf89f02a9da0ed80f7cd0b7e0e8bc388b IN  0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB0.0006561
0xa26a5ec6f33e7735650e4c3f857cadabd19ec8225a5e2c2b856e308ab4f666580x6080604087371722021-05-11 3:03:27138 days 48 mins ago0x20000b9b01e93a39db9d286e9264eff7f2af16e9 IN  Contract Creation0 BNB0.04624546
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x2983211dca6dd82a776a77a7e9d7c8964824e242eb675928a18dcedea6b6e355116145602021-08-19 12:00:5437 days 15 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0xe83db91c395b39470638fddef129528cb006540616f8104e48c35adf6ceda350116138262021-08-19 11:23:3337 days 16 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x9cb43434482820145e0435fe25ccb693900268b390e5be950f782be3b2f573ae116091992021-08-19 7:26:0737 days 20 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
0x8936cd65c6c7ccb532e295fb74aaa82c7790f808ffb8d27854ee095c165feff2113257122021-08-09 9:13:1447 days 18 hrs ago 0x43bc728d3a57f485d64df21de36a7e3d8dc8f195 0x371bfce7688d29fd23e83f673ec4521b844feb1a0 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Similar Match)
Note: This contract matches the deployed ByteCode of the Source Code for Contract 0xaE98AF4B5B3061Ce933fAB09C1bbDF38153d9b84

Contract Name:
Exchange

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 28 : Exchange.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./interface/IPriceFeed.sol";
import "./interface/IExchange.sol";
import "./interface/ISakePerp.sol";
import "./interface/ISakePerpVault.sol";
import "./interface/IExchangeState.sol";
import "./utils/Decimal.sol";
import "./utils/SignedDecimal.sol";
import "./utils/MixedDecimal.sol";
import "./utils/BlockContext.sol";
import "./utils/Sqrt.sol";
import "./types/ISakePerpVaultTypes.sol";

contract Exchange is IExchange, OwnableUpgradeable, BlockContext {
    using SafeMathUpgradeable for uint256;
    using Decimal for Decimal.decimal;
    using SignedDecimal for SignedDecimal.signedDecimal;
    using MixedDecimal for SignedDecimal.signedDecimal;
    using Sqrt for uint256;

    //
    // CONSTANT
    //
    // because position decimal rounding error,
    // if the position size is less than IGNORABLE_DIGIT_FOR_SHUTDOWN, it's equal size is 0
    uint256 private constant IGNORABLE_DIGIT_FOR_SHUTDOWN = 100;

    // a margin to prevent from rounding when calc liquidity multiplier limit
    uint256 private constant MARGIN_FOR_LIQUIDITY_MIGRATION_ROUNDING = 1e9;

    //
    // EVENTS
    //
    event SwapInput(Dir dir, uint256 quoteAssetAmount, uint256 baseAssetAmount);
    event SwapOutput(Dir dir, uint256 quoteAssetAmount, uint256 baseAssetAmount);
    event FundingRateUpdated(int256 rate, uint256 underlyingPrice);
    event ReserveSnapshotted(uint256 quoteAssetReserve, uint256 baseAssetReserve, uint256 timestamp);
    event LiquidityChanged(uint256 quoteReserve, uint256 baseReserve, int256 cumulativeNotional);
    event CapChanged(uint256 maxHoldingBaseAsset, uint256 openInterestNotionalCap);
    event Shutdown(uint256 settlementPrice);
    event MoveAMMPrice(
        uint256 ammPrice,
        uint256 oraclePrice,
        uint256 adjustPrice,
        int256 MMLiquidity,
        int256 MMPNL,
        bool moved
    );

    //
    // MODIFIERS
    //
    modifier onlyOpen() {
        require(open, "exchange was closed");
        _;
    }

    modifier onlyClose() {
        require(open == false, "exchange was open");
        _;
    }

    modifier onlyCounterParty() {
        require(address(sakePerp) == _msgSender(), "caller is not counterParty");
        _;
    }

    modifier onlyMinter() {
        require(address(sakePerpVault) == _msgSender(), "caller is not minter");
        _;
    }

    //
    // enum and struct
    //
    struct ReserveSnapshot {
        Decimal.decimal quoteAssetReserve;
        Decimal.decimal baseAssetReserve;
        uint256 timestamp;
        uint256 blockNumber;
    }

    // internal usage
    enum QuoteAssetDir {QUOTE_IN, QUOTE_OUT}
    // internal usage
    enum TwapCalcOption {RESERVE_ASSET, INPUT_ASSET}

    // To record current base/quote asset to calculate TWAP

    struct TwapInputAsset {
        Dir dir;
        Decimal.decimal assetAmount;
        QuoteAssetDir inOrOut;
    }

    struct TwapPriceCalcParams {
        TwapCalcOption opt;
        uint256 snapshotIndex;
        TwapInputAsset asset;
    }

    //**********************************************************//
    //    The below state variables can not change the order    //
    //**********************************************************//

    // update during every swap and used when shutting exchange down
    SignedDecimal.signedDecimal public totalPositionSize;

    // latest funding rate = ((twap market price - twap oracle price) / twap oracle price) / 24
    SignedDecimal.signedDecimal public fundingRate;
    SignedDecimal.signedDecimal private cumulativeNotional;
    SignedDecimal.signedDecimal private mmCumulativeNotional;

    Decimal.decimal private settlementPrice;
    Decimal.decimal public override tradeLimitRatio;
    Decimal.decimal public quoteAssetReserve;
    Decimal.decimal public baseAssetReserve;
    Decimal.decimal public override fluctuationLimitRatio;

    // owner can update
    Decimal.decimal public override priceAdjustRatio;

    // snapshot of amm reserve when change liquidity's invariant
    LiquidityChangedSnapshot[] private liquidityChangedSnapshots;

    uint256 public spotPriceTwapInterval;
    uint256 public override fundingPeriod;
    uint256 public fundingBufferPeriod;
    uint256 public nextFundingTime;
    bytes32 public override priceFeedKey;
    ReserveSnapshot[] public reserveSnapshots;

    ISakePerpVault public sakePerpVault;
    IERC20Upgradeable public override quoteAsset;
    IPriceFeed public priceFeed;
    ISakePerp public sakePerp;
    bool public override open;
    uint256 public lastMoveAmmPriceTime;
    Decimal.decimal public oraclePriceSpreadLimit;
    address public mover;
    IExchangeState private exchangeState;

    //**********************************************************//
    //    The above state variables can not change the order    //
    //**********************************************************//

    //◥◤◥◤◥◤◥◤◥◤◥◤◥◤◥◤ add state variables below ◥◤◥◤◥◤◥◤◥◤◥◤◥◤◥◤//

    //◢◣◢◣◢◣◢◣◢◣◢◣◢◣◢◣ add state variables above ◢◣◢◣◢◣◢◣◢◣◢◣◢◣◢◣//
    uint256[50] private __gap;

    //
    // FUNCTIONS
    //
    function initialize(
        uint256 _quoteAssetReserve,
        uint256 _baseAssetReserve,
        uint256 _tradeLimitRatio,
        uint256 _fundingPeriod,
        IPriceFeed _priceFeed,
        ISakePerp _sakePerp,
        ISakePerpVault _sakePerpVault,
        bytes32 _priceFeedKey,
        address _quoteAsset,
        uint256 _fluctuationLimitRatio,
        uint256 _priceAdjustRatio,
        IExchangeState _exchangeState
    ) public initializer {
        require(
            _quoteAssetReserve != 0 &&
                _tradeLimitRatio != 0 &&
                _baseAssetReserve != 0 &&
                _fundingPeriod != 0 &&
                address(_priceFeed) != address(0) &&
                address(_sakePerp) != address(0) &&
                address(_sakePerpVault) != address(0) &&
                address(_exchangeState) != address(0) &&
                _quoteAsset != address(0),
            "invalid input"
        );
        __Ownable_init();

        quoteAssetReserve = Decimal.decimal(_quoteAssetReserve);
        baseAssetReserve = Decimal.decimal(_baseAssetReserve);
        tradeLimitRatio = Decimal.decimal(_tradeLimitRatio);
        priceAdjustRatio = Decimal.decimal(_priceAdjustRatio);
        fluctuationLimitRatio = Decimal.decimal(_fluctuationLimitRatio);
        fundingPeriod = _fundingPeriod;
        fundingBufferPeriod = _fundingPeriod.div(2);
        spotPriceTwapInterval = 1 hours;
        priceFeedKey = _priceFeedKey;
        quoteAsset = IERC20Upgradeable(_quoteAsset);
        priceFeed = _priceFeed;
        sakePerp = _sakePerp;
        sakePerpVault = _sakePerpVault;
        exchangeState = _exchangeState;
        oraclePriceSpreadLimit = Decimal.decimal(3 * 10**17);
        mover = _msgSender();

        sakePerpVault.setRiskLiquidityWeight(address(this), 800, 100);
        sakePerpVault.setMaxLoss(address(this), ISakePerpVaultTypes.Risk.HIGH, 50);
        sakePerpVault.setMaxLoss(address(this), ISakePerpVaultTypes.Risk.LOW, 25);

        liquidityChangedSnapshots.push(
            LiquidityChangedSnapshot({
                cumulativeNotional: SignedDecimal.zero(),
                baseAssetReserve: baseAssetReserve,
                quoteAssetReserve: quoteAssetReserve,
                totalPositionSize: SignedDecimal.zero()
            })
        );
        reserveSnapshots.push(ReserveSnapshot(quoteAssetReserve, baseAssetReserve, _blockTimestamp(), _blockNumber()));
        emit ReserveSnapshotted(quoteAssetReserve.toUint(), baseAssetReserve.toUint(), _blockTimestamp());
    }

    /**
     * @notice mint MLP for MM
     * @dev only sakePerpVault can call this function
     */
    function mint(
        ISakePerpVaultTypes.Risk _risk,
        address account,
        uint256 amount
    ) external override onlyOpen onlyMinter {
        exchangeState.mint(_risk, account, amount);
    }

    /**
     * @notice burn MLP
     * @dev only sakePerpVault can call this function
     */
    function burn(
        ISakePerpVaultTypes.Risk _risk,
        address account,
        uint256 amount
    ) external override onlyMinter {
        exchangeState.burn(_risk, account, amount);
    }

    /**
     * @notice Swap your quote asset to base asset, the impact of the price MUST be less than `fluctuationLimitRatio`
     * @dev Only clearingHouse can call this function
     * @param _dir ADD_TO_AMM for long, REMOVE_FROM_AMM for short
     * @param _quoteAssetAmount quote asset amount
     * @param _baseAssetAmountLimit minimum base asset amount expected to get to prevent front running
     * @return base asset amount
     */
    function swapInput(
        Dir _dir,
        Decimal.decimal calldata _quoteAssetAmount,
        Decimal.decimal calldata _baseAssetAmountLimit
    ) external override onlyOpen onlyCounterParty returns (Decimal.decimal memory) {
        if (_quoteAssetAmount.toUint() == 0) {
            return Decimal.zero();
        }
        if (_dir == Dir.REMOVE_FROM_AMM) {
            require(
                quoteAssetReserve.mulD(tradeLimitRatio).toUint() >= _quoteAssetAmount.toUint(),
                "over trading limit"
            );
        }

        Decimal.decimal memory baseAssetAmount = getInputPrice(_dir, _quoteAssetAmount);
        // If LONG, exchanged base amount should be more than _baseAssetAmountLimit,
        // otherwise(SHORT), exchanged base amount should be less than _baseAssetAmountLimit.
        // In SHORT case, more position means more debt so should not be larger than _baseAssetAmountLimit
        if (_baseAssetAmountLimit.toUint() != 0) {
            if (_dir == Dir.ADD_TO_AMM) {
                require(baseAssetAmount.toUint() >= _baseAssetAmountLimit.toUint(), "Less than minimal base token");
            } else {
                require(baseAssetAmount.toUint() <= _baseAssetAmountLimit.toUint(), "More than maximal base token");
            }
        }

        updateReserve(_dir, _quoteAssetAmount, baseAssetAmount, false);
        emit SwapInput(_dir, _quoteAssetAmount.toUint(), baseAssetAmount.toUint());
        return baseAssetAmount;
    }

    /**
     * @notice swap your base asset to quote asset; the impact of the price can be restricted with fluctuationLimitRatio
     * @dev only clearingHouse can call this function
     * @param _dir ADD_TO_AMM for short, REMOVE_FROM_AMM for long, opposite direction from swapInput
     * @param _baseAssetAmount base asset amount
     * @param _quoteAssetAmountLimit limit of quote asset amount; for slippage protection
     * @param _skipFluctuationCheck false for checking fluctuationLimitRatio; true for no limit, only when closePosition()
     * @return quote asset amount
     */
    function swapOutput(
        Dir _dir,
        Decimal.decimal calldata _baseAssetAmount,
        Decimal.decimal calldata _quoteAssetAmountLimit,
        bool _skipFluctuationCheck
    ) external override onlyOpen onlyCounterParty returns (Decimal.decimal memory) {
        return implSwapOutput(_dir, _baseAssetAmount, _quoteAssetAmountLimit, _skipFluctuationCheck);
    }

    /**
     * @notice update funding rate
     * @dev only allow to update while reaching `nextFundingTime`
     * @return premium fraction of this period in 18 digits
     */
    function settleFunding() external override onlyOpen onlyCounterParty returns (SignedDecimal.signedDecimal memory) {
        require(_blockTimestamp() >= nextFundingTime, "settle funding too early");
        SignedDecimal.signedDecimal memory premiumFraction = SignedDecimal.zero();
        Decimal.decimal memory underlyingPrice = getUnderlyingTwapPrice(spotPriceTwapInterval);

        // if AMM price has been moved before fundingTime, no need to this funding
        if (lastMoveAmmPriceTime.add(fundingPeriod) < nextFundingTime) {
            // premium = twapMarketPrice - twapIndexPrice
            // timeFraction = fundingPeriod(1 hour) / 1 day
            // premiumFraction = premium * timeFraction
            SignedDecimal.signedDecimal memory premium =
                MixedDecimal.fromDecimal(getTwapPrice(spotPriceTwapInterval)).subD(underlyingPrice);
            premiumFraction = premium.mulScalar(fundingPeriod).divScalar(int256(1 days));
        }

        // update funding rate = premiumFraction / twapIndexPrice
        updateFundingRate(premiumFraction, underlyingPrice);

        // in order to prevent multiple funding settlement during very short time after network congestion
        uint256 minNextValidFundingTime = _blockTimestamp().add(fundingBufferPeriod);

        // floor((nextFundingTime + fundingPeriod) / 3600) * 3600
        uint256 nextFundingTimeOnHourStart = nextFundingTime.add(fundingPeriod).div(1 hours).mul(1 hours);

        // max(nextFundingTimeOnHourStart, minNextValidFundingTime)
        nextFundingTime = nextFundingTimeOnHourStart > minNextValidFundingTime
            ? nextFundingTimeOnHourStart
            : minNextValidFundingTime;

        return premiumFraction;
    }

    function migrateLiquidity(
        Decimal.decimal calldata _liquidityMultiplier,
        Decimal.decimal calldata _fluctuationLimitRatio
    ) external override onlyOwner {
        require(_liquidityMultiplier.toUint() != Decimal.one().toUint(), "multiplier can't be 1");

        // check liquidity multiplier limit, have lower bound if position size is positive for now.
        checkLiquidityMultiplierLimit(totalPositionSize, _liquidityMultiplier);

        // #53 fix sandwich attack during liquidity migration
        checkFluctuationLimit(_fluctuationLimitRatio);

        // get current reserve values
        Decimal.decimal memory quoteAssetBeforeAddingLiquidity = quoteAssetReserve;
        Decimal.decimal memory baseAssetBeforeAddingLiquidity = baseAssetReserve;
        SignedDecimal.signedDecimal memory totalPositionSizeBefore = totalPositionSize;

        // migrate liquidity
        quoteAssetReserve = quoteAssetBeforeAddingLiquidity.mulD(_liquidityMultiplier);
        baseAssetReserve = baseAssetBeforeAddingLiquidity.mulD(_liquidityMultiplier);

        totalPositionSize = calcBaseAssetAfterLiquidityMigration(
            totalPositionSizeBefore,
            quoteAssetBeforeAddingLiquidity,
            baseAssetBeforeAddingLiquidity
        );

        // update snapshot
        liquidityChangedSnapshots.push(
            LiquidityChangedSnapshot({
                cumulativeNotional: cumulativeNotional,
                quoteAssetReserve: quoteAssetReserve,
                baseAssetReserve: baseAssetReserve,
                totalPositionSize: totalPositionSize
            })
        );

        emit LiquidityChanged(quoteAssetReserve.toUint(), baseAssetReserve.toUint(), cumulativeNotional.toInt());
    }

    function calcBaseAssetAfterLiquidityMigration(
        SignedDecimal.signedDecimal memory _baseAssetAmount,
        Decimal.decimal memory _fromQuoteReserve,
        Decimal.decimal memory _fromBaseReserve
    ) public view override returns (SignedDecimal.signedDecimal memory) {
        if (_baseAssetAmount.toUint() == 0) {
            return _baseAssetAmount;
        }

        bool isPositiveValue = _baseAssetAmount.toInt() > 0 ? true : false;

        // measure the trader position's notional value on the old curve
        // (by simulating closing the position)
        Decimal.decimal memory posNotional =
            getOutputPriceWithReserves(
                isPositiveValue ? Dir.ADD_TO_AMM : Dir.REMOVE_FROM_AMM,
                _baseAssetAmount.abs(),
                _fromQuoteReserve,
                _fromBaseReserve
            );

        // calculate and apply the required size on the new curve
        SignedDecimal.signedDecimal memory newBaseAsset =
            MixedDecimal.fromDecimal(
                getInputPrice(isPositiveValue ? Dir.REMOVE_FROM_AMM : Dir.ADD_TO_AMM, posNotional)
            );
        return newBaseAsset.mulScalar(isPositiveValue ? 1 : int256(-1));
    }

    /**
     * @notice shutdown exchange
     * @dev only owner can call this function
     */
    function shutdown() external override onlyOwner {
        sakePerpVault.modifyLiquidity();
        LiquidityChangedSnapshot memory latestLiquiditySnapshot = getLatestLiquidityChangedSnapshots();

        // get last liquidity changed history to calc new quote/base reserve
        Decimal.decimal memory previousK =
            latestLiquiditySnapshot.baseAssetReserve.mulD(latestLiquiditySnapshot.quoteAssetReserve);
        SignedDecimal.signedDecimal memory lastInitBaseReserveInNewCurve =
            latestLiquiditySnapshot.totalPositionSize.addD(latestLiquiditySnapshot.baseAssetReserve);
        SignedDecimal.signedDecimal memory lastInitQuoteReserveInNewCurve =
            MixedDecimal.fromDecimal(previousK).divD(lastInitBaseReserveInNewCurve);

        // settlementPrice = SUM(Open Position Notional Value) / SUM(Position Size)
        // `Open Position Notional Value` = init quote reserve - current quote reserve
        // `Position Size` = init base reserve - current base reserve
        SignedDecimal.signedDecimal memory positionNotionalValue =
            lastInitQuoteReserveInNewCurve.subD(quoteAssetReserve);

        // if total position size less than IGNORABLE_DIGIT_FOR_SHUTDOWN, treat it as 0 positions due to rounding error
        if (totalPositionSize.toUint() > IGNORABLE_DIGIT_FOR_SHUTDOWN) {
            settlementPrice = positionNotionalValue.abs().divD(totalPositionSize.abs());
        }

        open = false;
        emit Shutdown(settlementPrice.toUint());
    }

    /**
     * @notice set counter party
     * @dev only owner can call this function
     * @param _counterParty address of counter party
     */
    function setCounterParty(address _counterParty) external onlyOwner {
        sakePerp = ISakePerp(_counterParty);
    }

    /**
     * @notice set minter
     * @dev only owner can call this function
     * @param _minter address of minter
     */
    function setMinter(address _minter) external onlyOwner {
        sakePerpVault = ISakePerpVault(_minter);
    }

    /**
     * @notice set fluctuation limit rate. Default value is `1 / max leverage`
     * @dev only owner can call this function
     * @param _fluctuationLimitRatio fluctuation limit rate in 18 digits, 0 means skip the checking
     */
    function setFluctuationLimitRatio(Decimal.decimal memory _fluctuationLimitRatio) public onlyOwner {
        fluctuationLimitRatio = _fluctuationLimitRatio;
    }

    /**
     * @notice set time interval for twap calculation, default is 1 hour
     * @dev only owner can call this function
     * @param _interval time interval in seconds
     */
    function setSpotPriceTwapInterval(uint256 _interval) external onlyOwner {
        require(_interval != 0, "can not set interval to 0");
        spotPriceTwapInterval = _interval;
    }

    /**
     * @notice set `open` flag. Amm is open to trade if `open` is true. Default is false.
     * @dev only owner can call this function
     * @param _open open to trade is true, otherwise is false.
     */
    function setOpen(bool _open) external onlyOwner {
        if (open == _open) return;

        open = _open;
        if (_open) {
            nextFundingTime = _blockTimestamp().add(fundingPeriod).div(1 hours).mul(1 hours);
        }
    }

    /**
     * @notice set new price adjust ratio
     * @dev only owner can call
     * @param _priceAdjustRatio new price adjust spread in 18 digits
     */
    function setPriceAdjustRatio(Decimal.decimal memory _priceAdjustRatio) public onlyOwner {
        require(_priceAdjustRatio.cmp(Decimal.one()) <= 0, "invalid ratio");
        priceAdjustRatio = _priceAdjustRatio;
    }

    /**
     * @notice set price feed address
     * @param _priceFeed new price feed address
     */
    function setPriceFeed(address _priceFeed) public override onlyOwner {
        require(_priceFeed != address(0), "invalid address");
        priceFeed = IPriceFeed(_priceFeed);
    }

    /**
     * @notice set oracle price spread limitation
     * @param _limit new limitation
     */
    function setOraclePriceSpreadLimit(Decimal.decimal memory _limit) public onlyOwner {
        oraclePriceSpreadLimit = _limit;
    }

    /**
     * @notice set oracle price mover
     * @param _newMover new mover
     */
    function setMover(address _newMover) public onlyOwner {
        require(_newMover != address(0), "invalid address");
        mover = _newMover;
    }

    /**
     * @notice set exchange state address
     * @param _exchangeState new exchange state address
     */
    function setExchangeState(address _exchangeState) public onlyOwner {
        require(_exchangeState != address(0), "invalid address");
        exchangeState = IExchangeState(_exchangeState);
    }

    //
    // VIEW FUNCTIONS
    //

    /**
     * @notice get input twap amount.
     * returns how many base asset you will get with the input quote amount based on twap price.
     * @param _dir ADD_TO_AMM for long, REMOVE_FROM_AMM for short.
     * @param _quoteAssetAmount quote asset amount
     * @return base asset amount
     */
    function getInputTwap(Dir _dir, Decimal.decimal memory _quoteAssetAmount)
        public
        view
        override
        returns (Decimal.decimal memory)
    {
        return implGetInputAssetTwapPrice(_dir, _quoteAssetAmount, QuoteAssetDir.QUOTE_IN, 15 minutes);
    }

    /**
     * @notice get output twap amount.
     * return how many quote asset you will get with the input base amount on twap price.
     * @param _dir ADD_TO_AMM for short, REMOVE_FROM_AMM for long, opposite direction from `getInputTwap`.
     * @param _baseAssetAmount base asset amount
     * @return quote asset amount
     */
    function getOutputTwap(Dir _dir, Decimal.decimal memory _baseAssetAmount)
        public
        view
        override
        returns (Decimal.decimal memory)
    {
        return implGetInputAssetTwapPrice(_dir, _baseAssetAmount, QuoteAssetDir.QUOTE_OUT, 15 minutes);
    }

    /**
     * @notice get input amount. returns how many base asset you will get with the input quote amount.
     * @param _dir ADD_TO_AMM for long, REMOVE_FROM_AMM for short.
     * @param _quoteAssetAmount quote asset amount
     * @return base asset amount
     */
    function getInputPrice(Dir _dir, Decimal.decimal memory _quoteAssetAmount)
        public
        view
        override
        returns (Decimal.decimal memory)
    {
        return getInputPriceWithReserves(_dir, _quoteAssetAmount, quoteAssetReserve, baseAssetReserve);
    }

    /**
     * @notice get output price. return how many quote asset you will get with the input base amount
     * @param _dir ADD_TO_AMM for short, REMOVE_FROM_AMM for long, opposite direction from `getInput`.
     * @param _baseAssetAmount base asset amount
     * @return quote asset amount
     */
    function getOutputPrice(Dir _dir, Decimal.decimal memory _baseAssetAmount)
        public
        view
        override
        returns (Decimal.decimal memory)
    {
        return getOutputPriceWithReserves(_dir, _baseAssetAmount, quoteAssetReserve, baseAssetReserve);
    }

    /**
     * @notice get underlying price provided by oracle
     * @return underlying price
     */
    function getUnderlyingPrice() public view override returns (Decimal.decimal memory) {
        return Decimal.decimal(priceFeed.getPrice(priceFeedKey));
    }

    /**
     * @notice get underlying twap price provided by oracle
     * @return underlying price
     */
    function getUnderlyingTwapPrice(uint256 _intervalInSeconds) public view returns (Decimal.decimal memory) {
        return Decimal.decimal(priceFeed.getTwapPrice(priceFeedKey, _intervalInSeconds));
    }

    /**
     * @notice get spot price based on current quote/base asset reserve.
     * @return spot price
     */
    function getSpotPrice() public view override returns (Decimal.decimal memory) {
        return quoteAssetReserve.divD(baseAssetReserve);
    }

    /**
     * @notice get twap price
     */
    function getTwapPrice(uint256 _intervalInSeconds) public view returns (Decimal.decimal memory) {
        return implGetReserveTwapPrice(_intervalInSeconds);
    }

    /**
     * @notice get current quote/base asset reserve.
     * @return (quote asset reserve, base asset reserve)
     */
    function getReserve() external view override returns (Decimal.decimal memory, Decimal.decimal memory) {
        return (quoteAssetReserve, baseAssetReserve);
    }

    //@audit - no one use this anymore, can be remove (@wraecca).
    // If we remove this, we should make reserveSnapshots private.
    // If we need reserveSnapshots, should keep this. (@Kimi)
    function getSnapshotLen() external view returns (uint256) {
        return reserveSnapshots.length;
    }

    function getLiquidityHistoryLength() external view override returns (uint256) {
        return liquidityChangedSnapshots.length;
    }

    function getCumulativeNotional() external view override returns (SignedDecimal.signedDecimal memory) {
        return cumulativeNotional;
    }

    function getLatestLiquidityChangedSnapshots() public view returns (LiquidityChangedSnapshot memory) {
        return liquidityChangedSnapshots[liquidityChangedSnapshots.length.sub(1)];
    }

    function getLiquidityChangedSnapshots(uint256 i) external view override returns (LiquidityChangedSnapshot memory) {
        require(i < liquidityChangedSnapshots.length, "incorrect index");
        return liquidityChangedSnapshots[i];
    }

    function getSettlementPrice() external view override returns (Decimal.decimal memory) {
        return settlementPrice;
    }

    function getMaxHoldingBaseAsset() external view override returns (Decimal.decimal memory) {
        return exchangeState.getMaxHoldingBaseAsset();
    }

    function getOpenInterestNotionalCap() external view override returns (Decimal.decimal memory) {
        return exchangeState.getOpenInterestNotionalCap();
    }

    function initMarginRatio() external view override returns (Decimal.decimal memory) {
        return exchangeState.initMarginRatio();
    }

    function maintenanceMarginRatio() external view override returns (Decimal.decimal memory) {
        return exchangeState.maintenanceMarginRatio();
    }

    function liquidationFeeRatio() external view override returns (Decimal.decimal memory) {
        return exchangeState.liquidationFeeRatio();
    }

    function maxLiquidationFee() external view override returns (Decimal.decimal memory) {
        return exchangeState.maxLiquidationFee();
    }

    function spreadRatio() external view override returns (Decimal.decimal memory) {
        return exchangeState.spreadRatio();
    }

    function getTotalPositionSize() external view override returns (SignedDecimal.signedDecimal memory) {
        return totalPositionSize;
    }

    function getExchangeState() external view override returns (address) {
        return address(exchangeState);
    }

    function isOverSpreadLimit() external view override returns (bool) {
        Decimal.decimal memory oraclePrice = getUnderlyingPrice();
        require(oraclePrice.toUint() > 0, "underlying price is 0");
        Decimal.decimal memory marketPrice = getSpotPrice();
        Decimal.decimal memory oracleSpreadRatioAbs =
            MixedDecimal.fromDecimal(marketPrice).subD(oraclePrice).divD(oraclePrice).abs();

        return oracleSpreadRatioAbs.toUint() >= exchangeState.maxOracleSpreadRatio().toUint() ? true : false;
    }

    /**
     * @notice calculate spread fee by input quoteAssetAmount
     * @param _quoteAssetAmount quoteAssetAmount
     * @return total tx fee
     */
    function calcFee(Decimal.decimal calldata _quoteAssetAmount)
        external
        view
        override
        returns (Decimal.decimal memory)
    {
        return exchangeState.calcFee(_quoteAssetAmount);
    }

    function getInputPriceWithReserves(
        Dir _dir,
        Decimal.decimal memory _quoteAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) public view override returns (Decimal.decimal memory) {
        return
            exchangeState.getInputPriceWithReserves(
                _dir,
                _quoteAssetAmount,
                _quoteAssetPoolAmount,
                _baseAssetPoolAmount
            );
    }

    function getOutputPriceWithReserves(
        Dir _dir,
        Decimal.decimal memory _baseAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) public view override returns (Decimal.decimal memory) {
        return
            exchangeState.getOutputPriceWithReserves(
                _dir,
                _baseAssetAmount,
                _quoteAssetPoolAmount,
                _baseAssetPoolAmount
            );
    }

    //
    // INTERNAL FUNCTIONS
    //
    // update funding rate = premiumFraction / twapIndexPrice
    function updateFundingRate(
        SignedDecimal.signedDecimal memory _premiumFraction,
        Decimal.decimal memory _underlyingPrice
    ) private {
        fundingRate = _premiumFraction.divD(_underlyingPrice);
        emit FundingRateUpdated(fundingRate.toInt(), _underlyingPrice.toUint());
    }

    function addReserveSnapshot() internal {
        uint256 currentBlock = _blockNumber();
        ReserveSnapshot storage latestSnapshot = reserveSnapshots[reserveSnapshots.length - 1];
        // update values in snapshot if in the same block
        if (currentBlock == latestSnapshot.blockNumber) {
            latestSnapshot.quoteAssetReserve = quoteAssetReserve;
            latestSnapshot.baseAssetReserve = baseAssetReserve;
        } else {
            reserveSnapshots.push(
                ReserveSnapshot(quoteAssetReserve, baseAssetReserve, _blockTimestamp(), currentBlock)
            );
        }
        emit ReserveSnapshotted(quoteAssetReserve.toUint(), baseAssetReserve.toUint(), _blockTimestamp());
    }

    function implSwapOutput(
        Dir _dir,
        Decimal.decimal memory _baseAssetAmount,
        Decimal.decimal memory _quoteAssetAmountLimit,
        bool _skipFluctuationCheck
    ) internal returns (Decimal.decimal memory) {
        if (_baseAssetAmount.toUint() == 0) {
            return Decimal.zero();
        }

        // positionSize may little than real position size because of migtrate liquidity
        if (_dir == Dir.REMOVE_FROM_AMM) {
            require(baseAssetReserve.mulD(tradeLimitRatio).toUint() >= _baseAssetAmount.toUint(), "over trading limit");
        }

        Decimal.decimal memory quoteAssetAmount = getOutputPrice(_dir, _baseAssetAmount);
        // If SHORT, exchanged quote amount should be less than _quoteAssetAmountLimit,
        // otherwise(LONG), exchanged base amount should be more than _quoteAssetAmountLimit.
        // In the SHORT case, more quote assets means more payment so should not be more than _quoteAssetAmountLimit
        if (_quoteAssetAmountLimit.toUint() != 0) {
            if (_dir == Dir.ADD_TO_AMM) {
                // SHORT
                require(quoteAssetAmount.toUint() >= _quoteAssetAmountLimit.toUint(), "Less than minimal quote token");
            } else {
                // LONG
                require(quoteAssetAmount.toUint() <= _quoteAssetAmountLimit.toUint(), "More than maximal quote token");
            }
        }

        // If the price impact of one single tx is larger than priceFluctuation, skip the check
        // only for liquidate()
        if (!_skipFluctuationCheck) {
            _skipFluctuationCheck = isSingleTxOverFluctuation(_dir, quoteAssetAmount, _baseAssetAmount);
        }

        updateReserve(
            _dir == Dir.ADD_TO_AMM ? Dir.REMOVE_FROM_AMM : Dir.ADD_TO_AMM,
            quoteAssetAmount,
            _baseAssetAmount,
            _skipFluctuationCheck
        );

        emit SwapOutput(_dir, quoteAssetAmount.toUint(), _baseAssetAmount.toUint());
        return quoteAssetAmount;
    }

    function updateReserve(
        Dir _dir,
        Decimal.decimal memory _quoteAssetAmount,
        Decimal.decimal memory _baseAssetAmount,
        bool _skipFluctuationCheck
    ) internal {
        if (_dir == Dir.ADD_TO_AMM) {
            quoteAssetReserve = quoteAssetReserve.addD(_quoteAssetAmount);
            baseAssetReserve = baseAssetReserve.subD(_baseAssetAmount);
            totalPositionSize = totalPositionSize.addD(_baseAssetAmount);
            cumulativeNotional = cumulativeNotional.addD(_quoteAssetAmount);
        } else {
            quoteAssetReserve = quoteAssetReserve.subD(_quoteAssetAmount);
            baseAssetReserve = baseAssetReserve.addD(_baseAssetAmount);
            totalPositionSize = totalPositionSize.subD(_baseAssetAmount);
            cumulativeNotional = cumulativeNotional.subD(_quoteAssetAmount);
        }

        // check if it's over fluctuationLimitRatio
        if (!_skipFluctuationCheck) {
            checkFluctuationLimit(fluctuationLimitRatio);
        }

        // addReserveSnapshot must be after checking price fluctuation
        addReserveSnapshot();
    }

    function implGetInputAssetTwapPrice(
        Dir _dir,
        Decimal.decimal memory _assetAmount,
        QuoteAssetDir _inOut,
        uint256 _interval
    ) internal view returns (Decimal.decimal memory) {
        TwapPriceCalcParams memory params;
        params.opt = TwapCalcOption.INPUT_ASSET;
        params.snapshotIndex = reserveSnapshots.length.sub(1);
        params.asset.dir = _dir;
        params.asset.assetAmount = _assetAmount;
        params.asset.inOrOut = _inOut;
        return calcTwap(params, _interval);
    }

    function implGetReserveTwapPrice(uint256 _interval) internal view returns (Decimal.decimal memory) {
        TwapPriceCalcParams memory params;
        params.opt = TwapCalcOption.RESERVE_ASSET;
        params.snapshotIndex = reserveSnapshots.length.sub(1);
        return calcTwap(params, _interval);
    }

    function calcTwap(TwapPriceCalcParams memory _params, uint256 _interval)
        internal
        view
        returns (Decimal.decimal memory)
    {
        Decimal.decimal memory currentPrice = getPriceWithSpecificSnapshot(_params);
        if (_interval == 0) {
            return currentPrice;
        }

        uint256 baseTimestamp = _blockTimestamp().sub(_interval);
        ReserveSnapshot memory currentSnapshot = reserveSnapshots[_params.snapshotIndex];
        // return the latest snapshot price directly
        // if only one snapshot or the timestamp of latest snapshot is earlier than asking for
        if (reserveSnapshots.length == 1 || currentSnapshot.timestamp <= baseTimestamp) {
            return currentPrice;
        }

        uint256 previousTimestamp = currentSnapshot.timestamp;
        uint256 period = _blockTimestamp().sub(previousTimestamp);
        Decimal.decimal memory weightedPrice = currentPrice.mulScalar(period);
        while (true) {
            // if snapshot history is too short
            if (_params.snapshotIndex == 0) {
                return weightedPrice.divScalar(period);
            }

            _params.snapshotIndex = _params.snapshotIndex.sub(1);
            currentSnapshot = reserveSnapshots[_params.snapshotIndex];
            currentPrice = getPriceWithSpecificSnapshot(_params);

            // check if current round timestamp is earlier than target timestamp
            if (currentSnapshot.timestamp <= baseTimestamp) {
                // weighted time period will be (target timestamp - previous timestamp). For example,
                // now is 1000, _interval is 100, then target timestamp is 900. If timestamp of current round is 970,
                // and timestamp of NEXT round is 880, then the weighted time period will be (970 - 900) = 70,
                // instead of (970 - 880)
                weightedPrice = weightedPrice.addD(currentPrice.mulScalar(previousTimestamp.sub(baseTimestamp)));
                break;
            }

            uint256 timeFraction = previousTimestamp.sub(currentSnapshot.timestamp);
            weightedPrice = weightedPrice.addD(currentPrice.mulScalar(timeFraction));
            period = period.add(timeFraction);
            previousTimestamp = currentSnapshot.timestamp;
        }
        return weightedPrice.divScalar(_interval);
    }

    function getPriceWithSpecificSnapshot(TwapPriceCalcParams memory params)
        internal
        view
        virtual
        returns (Decimal.decimal memory)
    {
        ReserveSnapshot memory snapshot = reserveSnapshots[params.snapshotIndex];

        // RESERVE_ASSET means price comes from quoteAssetReserve/baseAssetReserve
        // INPUT_ASSET means getInput/Output price with snapshot's reserve
        if (params.opt == TwapCalcOption.RESERVE_ASSET) {
            return snapshot.quoteAssetReserve.divD(snapshot.baseAssetReserve);
        } else if (params.opt == TwapCalcOption.INPUT_ASSET) {
            if (params.asset.assetAmount.toUint() == 0) {
                return Decimal.zero();
            }
            if (params.asset.inOrOut == QuoteAssetDir.QUOTE_IN) {
                return
                    getInputPriceWithReserves(
                        params.asset.dir,
                        params.asset.assetAmount,
                        snapshot.quoteAssetReserve,
                        snapshot.baseAssetReserve
                    );
            } else if (params.asset.inOrOut == QuoteAssetDir.QUOTE_OUT) {
                return
                    getOutputPriceWithReserves(
                        params.asset.dir,
                        params.asset.assetAmount,
                        snapshot.quoteAssetReserve,
                        snapshot.baseAssetReserve
                    );
            }
        }
        revert("not supported option");
    }

    function isSingleTxOverFluctuation(
        Dir _dir,
        Decimal.decimal memory _quoteAssetAmount,
        Decimal.decimal memory _baseAssetAmount
    ) internal view returns (bool) {
        Decimal.decimal memory priceAfterReserveUpdated =
            (_dir == Dir.ADD_TO_AMM)
                ? quoteAssetReserve.subD(_quoteAssetAmount).divD(baseAssetReserve.addD(_baseAssetAmount))
                : quoteAssetReserve.addD(_quoteAssetAmount).divD(baseAssetReserve.subD(_baseAssetAmount));
        return
            isOverFluctuationLimit(
                priceAfterReserveUpdated,
                fluctuationLimitRatio,
                reserveSnapshots[reserveSnapshots.length.sub(1)]
            );
    }

    function checkFluctuationLimit(Decimal.decimal memory _fluctuationLimitRatio) internal view {
        // Skip the check if the limit is 0
        if (_fluctuationLimitRatio.toUint() > 0) {
            uint256 len = reserveSnapshots.length;
            ReserveSnapshot memory latestSnapshot = reserveSnapshots[len - 1];

            // if the latest snapshot is the same as current block, get the previous one
            if (latestSnapshot.blockNumber == _blockNumber() && len > 1) {
                latestSnapshot = reserveSnapshots[len - 2];
            }

            require(
                !isOverFluctuationLimit(
                    quoteAssetReserve.divD(baseAssetReserve),
                    _fluctuationLimitRatio,
                    latestSnapshot
                ),
                "price is over fluctuation limit"
            );
        }
    }

    function checkLiquidityMultiplierLimit(
        SignedDecimal.signedDecimal memory _positionSize,
        Decimal.decimal memory _liquidityMultiplier
    ) internal view {
        // have lower bound when position size is long
        if (_positionSize.toInt() > 0) {
            Decimal.decimal memory liquidityMultiplierLowerBound =
                _positionSize
                    .addD(Decimal.decimal(MARGIN_FOR_LIQUIDITY_MIGRATION_ROUNDING))
                    .divD(baseAssetReserve)
                    .abs();
            require(_liquidityMultiplier.cmp(liquidityMultiplierLowerBound) >= 0, "illegal liquidity multiplier");
        }
    }

    function isOverFluctuationLimit(
        Decimal.decimal memory _price,
        Decimal.decimal memory _fluctuationLimitRatio,
        ReserveSnapshot memory _snapshot
    ) internal pure returns (bool) {
        Decimal.decimal memory lastPrice = _snapshot.quoteAssetReserve.divD(_snapshot.baseAssetReserve);
        Decimal.decimal memory upperLimit = lastPrice.mulD(Decimal.one().addD(_fluctuationLimitRatio));
        Decimal.decimal memory lowerLimit = lastPrice.mulD(Decimal.one().subD(_fluctuationLimitRatio));

        if (_price.cmp(upperLimit) <= 0 && _price.cmp(lowerLimit) >= 0) {
            return false;
        }
        return true;
    }

    function moveAMMPriceToOracle(uint256 _oraclePrice, bytes32 _priceFeedKey) public override {
        require(mover == _msgSender() || address(priceFeed) == _msgSender(), "illegal operator");
        require(_oraclePrice > 0, "oracle price can't be zero");
        require(priceFeedKey == _priceFeedKey, "illegal price feed key");
        if (!open || priceAdjustRatio.toUint() == 0) return;

        Decimal.decimal memory oraclePrice = Decimal.decimal(_oraclePrice);
        Decimal.decimal memory AMMPrice = quoteAssetReserve.divD(baseAssetReserve);
        require(
            MixedDecimal.fromDecimal(oraclePrice).subD(AMMPrice).abs().cmp(oraclePriceSpreadLimit.mulD(AMMPrice)) <= 0,
            "invalid oracle price"
        );

        Decimal.decimal memory adjustPrice =
            MixedDecimal
                .fromDecimal(AMMPrice)
                .addD(MixedDecimal.fromDecimal(oraclePrice).subD(AMMPrice).mulD(priceAdjustRatio))
                .abs();

        // baseAssetReserve * oraclePrice * baseAssetReserve = invariant
        Decimal.decimal memory invariant = quoteAssetReserve.mulD(baseAssetReserve);
        uint256 basePow = invariant.divD(adjustPrice).toUint().mul(Decimal.one().toUint());
        Decimal.decimal memory _baseAssetReserve = Decimal.decimal(basePow.sqrt());
        Decimal.decimal memory _quoteAssetReserve = invariant.divD(_baseAssetReserve);

        SignedDecimal.signedDecimal memory MMPNL = getMMUnrealizedPNL(_baseAssetReserve, _quoteAssetReserve);
        SignedDecimal.signedDecimal memory MMLiquidity = sakePerpVault.getTotalMMAvailableLiquidity(address(this));
        Decimal.decimal memory MMCachedLiquidity = sakePerpVault.getTotalMMCachedLiquidity(address(this));

        // negative means MM can't pay for this price movement
        if (MMPNL.addD(MMLiquidity).addD(MMCachedLiquidity).isNegative()) {
            emit MoveAMMPrice(
                AMMPrice.toUint(),
                _oraclePrice,
                adjustPrice.toUint(),
                MMLiquidity.toInt(),
                MMPNL.toInt(),
                false
            );
        } else {
            SignedDecimal.signedDecimal memory mmNotional =
                MixedDecimal.fromDecimal(_quoteAssetReserve).subD(quoteAssetReserve);
            mmCumulativeNotional = mmCumulativeNotional.addD(mmNotional);
            cumulativeNotional = cumulativeNotional.addD(mmNotional);
            baseAssetReserve = _baseAssetReserve;
            quoteAssetReserve = _quoteAssetReserve;
            lastMoveAmmPriceTime = _blockTimestamp();

            addReserveSnapshot();
            sakePerpVault.modifyLiquidity();

            emit MoveAMMPrice(
                AMMPrice.toUint(),
                _oraclePrice,
                adjustPrice.toUint(),
                MMLiquidity.addD(MMCachedLiquidity).toInt(),
                MMPNL.toInt(),
                true
            );
        }
    }

    /**
     * @notice get MM unrealized PNL
     */
    function getMMUnrealizedPNL(Decimal.decimal memory _baseAssetReserve, Decimal.decimal memory _quoteAssetReserve)
        public
        view
        override
        returns (SignedDecimal.signedDecimal memory)
    {
        // MMUnrealizedPNL = - (closeLongQuoteAssetAmout - openLongQuoteAssetAmout + openShortQuoteAssetAmout - closeShortQuoteAssetAmout)
        // MMUnrealizedPNL = openLongQuoteAssetAmout - openShortQuoteAssetAmout + closeShortQuoteAssetAmout - closeLongQuoteAssetAmout
        // cumulativeNotional = openLongQuoteAssetAmout - openShortQuoteAssetAmout
        // MMUnrealizedPNL = cumulativeNotional + closeShortQuoteAssetAmout - closeLongQuoteAssetAmout
        SignedDecimal.signedDecimal memory detalCloseAmount;
        if (totalPositionSize.isNegative()) {
            detalCloseAmount = MixedDecimal.fromDecimal(
                getOutputPriceWithReserves(
                    Dir.REMOVE_FROM_AMM,
                    totalPositionSize.abs(),
                    _quoteAssetReserve,
                    _baseAssetReserve
                )
            );
        } else {
            detalCloseAmount = MixedDecimal
                .fromDecimal(
                getOutputPriceWithReserves(
                    Dir.ADD_TO_AMM,
                    totalPositionSize.abs(),
                    _quoteAssetReserve,
                    _baseAssetReserve
                )
            )
                .mulScalar(-1);
        }

        return cumulativeNotional.subD(mmCumulativeNotional).addD(detalCloseAmount);
    }

    function adjustTotalPosition(
        SignedDecimal.signedDecimal memory adjustedPosition,
        SignedDecimal.signedDecimal memory oldAdjustedPosition
    ) public override onlyCounterParty {
        totalPositionSize = totalPositionSize.addD(adjustedPosition).subD(oldAdjustedPosition);
    }
}

File 2 of 28 : MMLPToken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./interface/ISystemSettings.sol";

contract MMLPToken is ERC20Upgradeable, OwnableUpgradeable {
    ISystemSettings public systemSettings;

    constructor(
        string memory _name,
        string memory _symbol,
        address _systemSettings
    ) public {
        systemSettings = ISystemSettings(_systemSettings);
        __ERC20_init(_name, _symbol);
        __Ownable_init();
    }

    function mint(address _account, uint256 _amount) external onlyOwner {
        _mint(_account, _amount);
    }

    function burn(address _account, uint256 _amount) external onlyOwner {
        _burn(_account, _amount);
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        amount;
        require(systemSettings.checkTransfer(from, to), "illegal transfer");
    }
}

File 3 of 28 : IExchange.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "../utils/Decimal.sol";
import "../utils/SignedDecimal.sol";
import "../types/ISakePerpVaultTypes.sol";
import "../types/IExchangeTypes.sol";

interface IExchange is IExchangeTypes {
    function swapInput(
        Dir _dir,
        Decimal.decimal calldata _quoteAssetAmount,
        Decimal.decimal calldata _baseAssetAmountLimit
    ) external returns (Decimal.decimal memory);

    function swapOutput(
        Dir _dir,
        Decimal.decimal calldata _baseAssetAmount,
        Decimal.decimal calldata _quoteAssetAmountLimit,
        bool _skipFluctuationCheck
    ) external returns (Decimal.decimal memory);

    function migrateLiquidity(Decimal.decimal calldata _liquidityMultiplier, Decimal.decimal calldata _priceLimitRatio)
        external;

    function shutdown() external;

    function settleFunding() external returns (SignedDecimal.signedDecimal memory);

    function calcFee(Decimal.decimal calldata _quoteAssetAmount) external view returns (Decimal.decimal memory);

    function calcBaseAssetAfterLiquidityMigration(
        SignedDecimal.signedDecimal memory _baseAssetAmount,
        Decimal.decimal memory _fromQuoteReserve,
        Decimal.decimal memory _fromBaseReserve
    ) external view returns (SignedDecimal.signedDecimal memory);

    function getInputTwap(Dir _dir, Decimal.decimal calldata _quoteAssetAmount)
        external
        view
        returns (Decimal.decimal memory);

    function getOutputTwap(Dir _dir, Decimal.decimal calldata _baseAssetAmount)
        external
        view
        returns (Decimal.decimal memory);

    function getInputPrice(Dir _dir, Decimal.decimal calldata _quoteAssetAmount)
        external
        view
        returns (Decimal.decimal memory);

    function getOutputPrice(Dir _dir, Decimal.decimal calldata _baseAssetAmount)
        external
        view
        returns (Decimal.decimal memory);

    function getInputPriceWithReserves(
        Dir _dir,
        Decimal.decimal memory _quoteAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) external view returns (Decimal.decimal memory);

    function getOutputPriceWithReserves(
        Dir _dir,
        Decimal.decimal memory _baseAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) external view returns (Decimal.decimal memory);

    function getSpotPrice() external view returns (Decimal.decimal memory);

    function getLiquidityHistoryLength() external view returns (uint256);

    // overridden by state variable
    function quoteAsset() external view returns (IERC20Upgradeable);

    function open() external view returns (bool);

    // can not be overridden by state variable due to type `Deciaml.decimal`
    function getSettlementPrice() external view returns (Decimal.decimal memory);

    function getCumulativeNotional() external view returns (SignedDecimal.signedDecimal memory);

    function getMaxHoldingBaseAsset() external view returns (Decimal.decimal memory);

    function getOpenInterestNotionalCap() external view returns (Decimal.decimal memory);

    function getLiquidityChangedSnapshots(uint256 i) external view returns (LiquidityChangedSnapshot memory);

    function mint(
        ISakePerpVaultTypes.Risk _level,
        address account,
        uint256 amount
    ) external;

    function burn(
        ISakePerpVaultTypes.Risk _level,
        address account,
        uint256 amount
    ) external;

    function getMMUnrealizedPNL(Decimal.decimal memory _baseAssetReserve, Decimal.decimal memory _quoteAssetReserve)
        external
        view
        returns (SignedDecimal.signedDecimal memory);

    function moveAMMPriceToOracle(uint256 _oraclePrice, bytes32 _priceFeedKey) external;

    function setPriceFeed(address _priceFeed) external;

    function getReserve() external view returns (Decimal.decimal memory, Decimal.decimal memory);

    function initMarginRatio() external view returns (Decimal.decimal memory);

    function maintenanceMarginRatio() external view returns (Decimal.decimal memory);

    function liquidationFeeRatio() external view returns (Decimal.decimal memory);

    function maxLiquidationFee() external view returns (Decimal.decimal memory);

    function spreadRatio() external view returns (Decimal.decimal memory);

    function priceFeedKey() external view returns (bytes32);

    function tradeLimitRatio() external view returns (uint256);

    function priceAdjustRatio() external view returns (uint256);

    function fluctuationLimitRatio() external view returns (uint256);

    function fundingPeriod() external view returns (uint256);

    function adjustTotalPosition(
        SignedDecimal.signedDecimal memory adjustedPosition,
        SignedDecimal.signedDecimal memory oldAdjustedPosition
    ) external;

    function getTotalPositionSize() external view returns (SignedDecimal.signedDecimal memory);

    function getExchangeState() external view returns (address);

    function getUnderlyingPrice() external view returns (Decimal.decimal memory);

    function isOverSpreadLimit() external view returns (bool);
}

File 4 of 28 : IExchangeState.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../types/IExchangeTypes.sol";
import "../types/ISakePerpVaultTypes.sol";
import "../utils/Decimal.sol";
import "../utils/SignedDecimal.sol";
import "../MMLPToken.sol";

interface IExchangeState {
    function getMaxHoldingBaseAsset() external view returns (Decimal.decimal memory);

    function getOpenInterestNotionalCap() external view returns (Decimal.decimal memory);

    function initMarginRatio() external view returns (Decimal.decimal memory);

    function maintenanceMarginRatio() external view returns (Decimal.decimal memory);

    function liquidationFeeRatio() external view returns (Decimal.decimal memory);

    function maxLiquidationFee() external view returns (Decimal.decimal memory);

    function spreadRatio() external view returns (Decimal.decimal memory);

    function maxOracleSpreadRatio() external view returns (Decimal.decimal memory);

    function getInputPriceWithReserves(
        IExchangeTypes.Dir _dir,
        Decimal.decimal memory _quoteAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) external pure returns (Decimal.decimal memory);

    function getOutputPriceWithReserves(
        IExchangeTypes.Dir _dir,
        Decimal.decimal memory _baseAssetAmount,
        Decimal.decimal memory _quoteAssetPoolAmount,
        Decimal.decimal memory _baseAssetPoolAmount
    ) external pure returns (Decimal.decimal memory);

    function calcFee(Decimal.decimal calldata _quoteAssetAmount) external view returns (Decimal.decimal memory);

    function mint(
        ISakePerpVaultTypes.Risk _level,
        address account,
        uint256 amount
    ) external;

    function burn(
        ISakePerpVaultTypes.Risk _level,
        address account,
        uint256 amount
    ) external;

    function getLPToken(ISakePerpVaultTypes.Risk _level) external view returns (MMLPToken);
}

File 5 of 28 : IInsuranceFund.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../utils/Decimal.sol";
import "./IExchange.sol";

interface IInsuranceFund {
    function withdraw(Decimal.decimal calldata _amount) external returns (Decimal.decimal memory badDebt);

    function setExchange(IExchange _exchange) external;

    function setBeneficiary(address _beneficiary) external;
}

File 6 of 28 : IPriceFeed.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

interface IPriceFeed {
    // get latest price
    function getPrice(bytes32 _priceFeedKey) external view returns (uint256);

    // get twap price depending on _period
    function getTwapPrice(bytes32 _priceFeedKey, uint256 _interval) external view returns (uint256);
}

File 7 of 28 : ISakePerp.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../utils/SignedDecimal.sol";
import "../utils/Decimal.sol";
import "../types/ISakePerpTypes.sol";
import "./IExchange.sol";

interface ISakePerp is ISakePerpTypes {
    function getMMLiquidity(address _exchange) external view returns (SignedDecimal.signedDecimal memory);

    function getLatestCumulativePremiumFraction(IExchange _exchange)
        external
        view
        returns (SignedDecimal.signedDecimal memory);

    function getLatestCumulativeOvernightFeeRate(IExchange _exchange) external view returns (Decimal.decimal memory);

    function getPositionNotionalAndUnrealizedPnl(
        IExchange _exchange,
        address _trader,
        PnlCalcOption _pnlCalcOption
    ) external view returns (Decimal.decimal memory positionNotional, SignedDecimal.signedDecimal memory unrealizedPnl);

    function getPosition(IExchange _exchange, address _trader) external view returns (Position memory);

    function getUnadjustedPosition(IExchange _exchange, address _trader)
        external
        view
        returns (Position memory position);

    function getMarginRatio(IExchange _exchange, address _trader)
        external
        view
        returns (SignedDecimal.signedDecimal memory);
}

File 8 of 28 : ISakePerpVault.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "./IExchange.sol";
import "../utils/Decimal.sol";
import "../utils/SignedDecimal.sol";
import "../types/ISakePerpVaultTypes.sol";

interface ISakePerpVault is ISakePerpVaultTypes {
    function withdraw(
        IExchange _exchange,
        address _receiver,
        Decimal.decimal memory _amount
    ) external;

    function realizeBadDebt(IExchange _exchange, Decimal.decimal memory _badDebt) external;

    function modifyLiquidity() external;

    function getMMLiquidity(address _exchange, Risk _risk) external view returns (SignedDecimal.signedDecimal memory);

    function getAllMMLiquidity(address _exchange)
        external
        view
        returns (SignedDecimal.signedDecimal memory, SignedDecimal.signedDecimal memory);

    function getTotalMMLiquidity(address _exchange) external view returns (SignedDecimal.signedDecimal memory);

    function getTotalMMAvailableLiquidity(address _exchange) external view returns (SignedDecimal.signedDecimal memory);

    function getTotalLpUnrealizedPNL(IExchange _exchange) external view returns (SignedDecimal.signedDecimal memory);

    function addCachedLiquidity(address _exchange, Decimal.decimal memory _DeltalpLiquidity) external;

    function requireMMNotBankrupt(address _exchange) external;

    function getMMCachedLiquidity(address _exchange, Risk _risk) external view returns (Decimal.decimal memory);

    function getTotalMMCachedLiquidity(address _exchange) external view returns (Decimal.decimal memory);

    function setRiskLiquidityWeight(address _exchange, uint256 _highWeight, uint256 _lowWeight) external;

    function setMaxLoss(
        address _exchange,
        Risk _risk,
        uint256 _max
    ) external;
}

File 9 of 28 : ISystemSettings.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "./IExchange.sol";
import "./IInsuranceFund.sol";
import "../utils/Decimal.sol";

interface ISystemSettings {
    function insuranceFundFeeRatio() external view returns (Decimal.decimal memory);

    function lpWithdrawFeeRatio() external view returns (Decimal.decimal memory);

    function overnightFeeRatio() external view returns (Decimal.decimal memory);

    function overnightFeeLpShareRatio() external view returns (Decimal.decimal memory);

    function fundingFeeLpShareRatio() external view returns (Decimal.decimal memory);

    function overnightFeePeriod() external view returns (uint256);

    function isExistedExchange(IExchange _exchange) external view returns (bool);

    function getAllExchanges() external view returns (IExchange[] memory);

    function getInsuranceFund(IExchange _exchange) external view returns (IInsuranceFund);

    function setNextOvernightFeeTime(IExchange _exchange) external;

    function nextOvernightFeeTime(address _exchange) external view returns (uint256);

    function checkTransfer(address _from, address _to) external view returns (bool);
}

File 10 of 28 : IExchangeTypes.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../utils/Decimal.sol";
import "../utils/SignedDecimal.sol";

interface IExchangeTypes {
    /**
     * @notice asset direction, used in getInputPrice, getOutputPrice, swapInput and swapOutput
     * @param ADD_TO_AMM add asset to Amm
     * @param REMOVE_FROM_AMM remove asset from Amm
     */
    enum Dir {ADD_TO_AMM, REMOVE_FROM_AMM}

    struct LiquidityChangedSnapshot {
        SignedDecimal.signedDecimal cumulativeNotional;
        // the base/quote reserve of amm right before liquidity changed
        Decimal.decimal quoteAssetReserve;
        Decimal.decimal baseAssetReserve;
        // total position size owned by amm after last snapshot taken
        // `totalPositionSize` = currentBaseAssetReserve - lastLiquidityChangedHistoryItem.baseAssetReserve + prevTotalPositionSize
        SignedDecimal.signedDecimal totalPositionSize;
    }
}

File 11 of 28 : ISakePerpTypes.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../utils/Decimal.sol";
import "../utils/SignedDecimal.sol";

interface ISakePerpTypes {
    //
    // Struct and Enum
    //
    enum Side {BUY, SELL}
    enum PnlCalcOption { SPOT_PRICE, TWAP, ORACLE }

    /// @notice This struct records personal position information
    /// @param size denominated in amm.baseAsset
    /// @param margin isolated margin
    /// @param openNotional the quoteAsset value of position when opening position. the cost of the position
    /// @param lastUpdatedCumulativePremiumFraction for calculating funding payment, record at the moment every time when trader open/reduce/close position
    /// @param lastUpdatedCumulativeOvernightFeeRate for calculating holding fee, record at the moment every time when trader open/reduce/close position
    /// @param liquidityHistoryIndex
    /// @param blockNumber the block number of the last position
    struct Position {
        SignedDecimal.signedDecimal size;
        Decimal.decimal margin;
        Decimal.decimal openNotional;
        SignedDecimal.signedDecimal lastUpdatedCumulativePremiumFraction;
        Decimal.decimal lastUpdatedCumulativeOvernightFeeRate;
        uint256 liquidityHistoryIndex;
        uint256 blockNumber;
    }
}

File 12 of 28 : ISakePerpVaultTypes.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

interface ISakePerpVaultTypes {
    /**
     * @notice pool types
     * @param HIGH high risk pool
     * @param LOW low risk pool
     */
    enum Risk {HIGH, LOW}
}

File 13 of 28 : BlockContext.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

// wrap block.xxx functions for testing
// only support timestamp and number so far
abstract contract BlockContext {
    //◥◤◥◤◥◤◥◤◥◤◥◤◥◤◥◤ add state variables below ◥◤◥◤◥◤◥◤◥◤◥◤◥◤◥◤//

    //◢◣◢◣◢◣◢◣◢◣◢◣◢◣◢◣ add state variables above ◢◣◢◣◢◣◢◣◢◣◢◣◢◣◢◣//
    uint256[50] private __gap;

    function _blockTimestamp() internal view virtual returns (uint256) {
        return block.timestamp;
    }

    function _blockNumber() internal view virtual returns (uint256) {
        return block.number;
    }
}

File 14 of 28 : Decimal.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "./DecimalMath.sol";

library Decimal {
    using DecimalMath for uint256;
    using SafeMathUpgradeable for uint256;

    struct decimal {
        uint256 d;
    }

    function zero() internal pure returns (decimal memory) {
        return decimal(0);
    }

    function one() internal pure returns (decimal memory) {
        return decimal(DecimalMath.unit(18));
    }

    function toUint(decimal memory x) internal pure returns (uint256) {
        return x.d;
    }

    function modD(decimal memory x, decimal memory y) internal pure returns (decimal memory) {
        return decimal(x.d.mul(DecimalMath.unit(18)) % y.d);
    }

    function cmp(decimal memory x, decimal memory y) internal pure returns (int8) {
        if (x.d > y.d) {
            return 1;
        } else if (x.d < y.d) {
            return -1;
        }
        return 0;
    }

    /// @dev add two decimals
    function addD(decimal memory x, decimal memory y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.add(y.d);
        return t;
    }

    /// @dev subtract two decimals
    function subD(decimal memory x, decimal memory y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.sub(y.d);
        return t;
    }

    /// @dev multiple two decimals
    function mulD(decimal memory x, decimal memory y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.muld(y.d);
        return t;
    }

    /// @dev multiple a decimal by a uint256
    function mulScalar(decimal memory x, uint256 y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.mul(y);
        return t;
    }

    /// @dev divide two decimals
    function divD(decimal memory x, decimal memory y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.divd(y.d);
        return t;
    }

    /// @dev divide a decimal by a uint256
    function divScalar(decimal memory x, uint256 y) internal pure returns (decimal memory) {
        decimal memory t;
        t.d = x.d.div(y);
        return t;
    }
}

File 15 of 28 : DecimalMath.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";

/// @dev Implements simple fixed point math add, sub, mul and div operations.
/// @author Alberto Cuesta Cañada
library DecimalMath {
    using SafeMathUpgradeable for uint256;

    /// @dev Returns 1 in the fixed point representation, with `decimals` decimals.
    function unit(uint8 decimals) internal pure returns (uint256) {
        return 10**uint256(decimals);
    }

    /// @dev Adds x and y, assuming they are both fixed point with 18 decimals.
    function addd(uint256 x, uint256 y) internal pure returns (uint256) {
        return x.add(y);
    }

    /// @dev Subtracts y from x, assuming they are both fixed point with 18 decimals.
    function subd(uint256 x, uint256 y) internal pure returns (uint256) {
        return x.sub(y);
    }

    /// @dev Multiplies x and y, assuming they are both fixed point with 18 digits.
    function muld(uint256 x, uint256 y) internal pure returns (uint256) {
        return muld(x, y, 18);
    }

    /// @dev Multiplies x and y, assuming they are both fixed point with `decimals` digits.
    function muld(
        uint256 x,
        uint256 y,
        uint8 decimals
    ) internal pure returns (uint256) {
        return x.mul(y).div(unit(decimals));
    }

    /// @dev Divides x between y, assuming they are both fixed point with 18 digits.
    function divd(uint256 x, uint256 y) internal pure returns (uint256) {
        return divd(x, y, 18);
    }

    /// @dev Divides x between y, assuming they are both fixed point with `decimals` digits.
    function divd(
        uint256 x,
        uint256 y,
        uint8 decimals
    ) internal pure returns (uint256) {
        return x.mul(unit(decimals)).div(y);
    }
}

File 16 of 28 : MixedDecimal.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "./Decimal.sol";
import "./SignedDecimal.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";

/// @dev To handle a signedDecimal add/sub/mul/div a decimal and provide convert decimal to signedDecimal helper
library MixedDecimal {
    using SignedDecimal for SignedDecimal.signedDecimal;
    using SignedSafeMathUpgradeable for int256;

    uint256 private constant _INT256_MAX = 2**255 - 1;
    string private constant ERROR_NON_CONVERTIBLE = "MixedDecimal: uint value is bigger than _INT256_MAX";

    modifier convertible(Decimal.decimal memory x) {
        require(_INT256_MAX >= x.d, ERROR_NON_CONVERTIBLE);
        _;
    }

    function fromDecimal(Decimal.decimal memory x)
        internal
        pure
        convertible(x)
        returns (SignedDecimal.signedDecimal memory)
    {
        return SignedDecimal.signedDecimal(int256(x.d));
    }

    function toUint(SignedDecimal.signedDecimal memory x) internal pure returns (uint256) {
        return x.abs().d;
    }

    /// @dev add SignedDecimal.signedDecimal and Decimal.decimal, using SignedSafeMath directly
    function addD(SignedDecimal.signedDecimal memory x, Decimal.decimal memory y)
        internal
        pure
        convertible(y)
        returns (SignedDecimal.signedDecimal memory)
    {
        SignedDecimal.signedDecimal memory t;
        t.d = x.d.add(int256(y.d));
        return t;
    }

    /// @dev subtract SignedDecimal.signedDecimal by Decimal.decimal, using SignedSafeMath directly
    function subD(SignedDecimal.signedDecimal memory x, Decimal.decimal memory y)
        internal
        pure
        convertible(y)
        returns (SignedDecimal.signedDecimal memory)
    {
        SignedDecimal.signedDecimal memory t;
        t.d = x.d.sub(int256(y.d));
        return t;
    }

    /// @dev multiple a SignedDecimal.signedDecimal by Decimal.decimal
    function mulD(SignedDecimal.signedDecimal memory x, Decimal.decimal memory y)
        internal
        pure
        convertible(y)
        returns (SignedDecimal.signedDecimal memory)
    {
        SignedDecimal.signedDecimal memory t;
        t = x.mulD(fromDecimal(y));
        return t;
    }

    /// @dev multiple a SignedDecimal.signedDecimal by a uint256
    function mulScalar(SignedDecimal.signedDecimal memory x, uint256 y)
        internal
        pure
        returns (SignedDecimal.signedDecimal memory)
    {
        require(_INT256_MAX >= y, ERROR_NON_CONVERTIBLE);
        SignedDecimal.signedDecimal memory t;
        t = x.mulScalar(int256(y));
        return t;
    }

    /// @dev divide a SignedDecimal.signedDecimal by a Decimal.decimal
    function divD(SignedDecimal.signedDecimal memory x, Decimal.decimal memory y)
        internal
        pure
        convertible(y)
        returns (SignedDecimal.signedDecimal memory)
    {
        SignedDecimal.signedDecimal memory t;
        t = x.divD(fromDecimal(y));
        return t;
    }

    /// @dev divide a SignedDecimal.signedDecimal by a uint256
    function divScalar(SignedDecimal.signedDecimal memory x, uint256 y)
        internal
        pure
        returns (SignedDecimal.signedDecimal memory)
    {
        require(_INT256_MAX >= y, ERROR_NON_CONVERTIBLE);
        SignedDecimal.signedDecimal memory t;
        t = x.divScalar(int256(y));
        return t;
    }
}

File 17 of 28 : SignedDecimal.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "./SignedDecimalMath.sol";
import "./Decimal.sol";

library SignedDecimal {
    using SignedDecimalMath for int256;
    using SignedSafeMathUpgradeable for int256;

    struct signedDecimal {
        int256 d;
    }

    function zero() internal pure returns (signedDecimal memory) {
        return signedDecimal(0);
    }

    function toInt(signedDecimal memory x) internal pure returns (int256) {
        return x.d;
    }

    function isNegative(signedDecimal memory x) internal pure returns (bool) {
        if (x.d < 0) {
            return true;
        }
        return false;
    }

    function abs(signedDecimal memory x) internal pure returns (Decimal.decimal memory) {
        Decimal.decimal memory t;
        if (x.d < 0) {
            t.d = uint256(0 - x.d);
        } else {
            t.d = uint256(x.d);
        }
        return t;
    }

    /// @dev add two decimals
    function addD(signedDecimal memory x, signedDecimal memory y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.add(y.d);
        return t;
    }

    /// @dev subtract two decimals
    function subD(signedDecimal memory x, signedDecimal memory y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.sub(y.d);
        return t;
    }

    /// @dev multiple two decimals
    function mulD(signedDecimal memory x, signedDecimal memory y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.muld(y.d);
        return t;
    }

    /// @dev multiple a signedDecimal by a int256
    function mulScalar(signedDecimal memory x, int256 y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.mul(y);
        return t;
    }

    /// @dev divide two decimals
    function divD(signedDecimal memory x, signedDecimal memory y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.divd(y.d);
        return t;
    }

    /// @dev divide a signedDecimal by a int256
    function divScalar(signedDecimal memory x, int256 y) internal pure returns (signedDecimal memory) {
        signedDecimal memory t;
        t.d = x.d.div(y);
        return t;
    }
}

File 18 of 28 : SignedDecimalMath.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";

/// @dev Implements simple signed fixed point math add, sub, mul and div operations.
library SignedDecimalMath {
    using SignedSafeMathUpgradeable for int256;

    /// @dev Returns 1 in the fixed point representation, with `decimals` decimals.
    function unit(uint8 decimals) internal pure returns (int256) {
        return int256(10**uint256(decimals));
    }

    /// @dev Adds x and y, assuming they are both fixed point with 18 decimals.
    function addd(int256 x, int256 y) internal pure returns (int256) {
        return x.add(y);
    }

    /// @dev Subtracts y from x, assuming they are both fixed point with 18 decimals.
    function subd(int256 x, int256 y) internal pure returns (int256) {
        return x.sub(y);
    }

    /// @dev Multiplies x and y, assuming they are both fixed point with 18 digits.
    function muld(int256 x, int256 y) internal pure returns (int256) {
        return muld(x, y, 18);
    }

    /// @dev Multiplies x and y, assuming they are both fixed point with `decimals` digits.
    function muld(
        int256 x,
        int256 y,
        uint8 decimals
    ) internal pure returns (int256) {
        return x.mul(y).div(unit(decimals));
    }

    /// @dev Divides x between y, assuming they are both fixed point with 18 digits.
    function divd(int256 x, int256 y) internal pure returns (int256) {
        return divd(x, y, 18);
    }

    /// @dev Divides x between y, assuming they are both fixed point with `decimals` digits.
    function divd(
        int256 x,
        int256 y,
        uint8 decimals
    ) internal pure returns (int256) {
        return x.mul(unit(decimals)).div(y);
    }
}

File 19 of 28 : Sqrt.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.6.12;

library Sqrt {
    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint256 y) internal pure returns (uint256) {
        if (y > 3) {
            uint256 z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
            return z;
        } else if (y != 0) {
            return 1;
        } else {
            return 0;
        }
    }
}

File 20 of 28 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}

File 21 of 28 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../GSN/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    uint256[49] private __gap;
}

File 22 of 28 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        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-contracts/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) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        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) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 23 of 28 : SignedSafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @title SignedSafeMath
 * @dev Signed math operations with safety checks that revert on error.
 */
library SignedSafeMathUpgradeable {
    int256 constant private _INT256_MIN = -2**255;

    /**
     * @dev Returns the multiplication of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(int256 a, int256 b) internal pure returns (int256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");

        int256 c = a * b;
        require(c / a == b, "SignedSafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two signed 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(int256 a, int256 b) internal pure returns (int256) {
        require(b != 0, "SignedSafeMath: division by zero");
        require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");

        int256 c = a / b;

        return c;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");

        return c;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");

        return c;
    }
}

File 24 of 28 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;


/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 * 
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 * 
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        // extcodesize checks the size of the code stored in an address, and
        // address returns the current address. Since the code is still not
        // deployed when running a constructor, any checks on its code size will
        // yield zero, making it an effective way to detect if a contract is
        // under construction or not.
        address self = address(this);
        uint256 cs;
        // solhint-disable-next-line no-inline-assembly
        assembly { cs := extcodesize(self) }
        return cs == 0;
    }
}

File 25 of 28 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../GSN/ContextUpgradeable.sol";
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../proxy/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {
    using SafeMathUpgradeable for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint8) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    uint256[44] private __gap;
}

File 26 of 28 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 27 of 28 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20Upgradeable {
    using SafeMathUpgradeable for uint256;
    using AddressUpgradeable for address;

    function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 28 of 28 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

Settings
{
  "remappings": [],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxHoldingBaseAsset","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"openInterestNotionalCap","type":"uint256"}],"name":"CapChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"rate","type":"int256"},{"indexed":false,"internalType":"uint256","name":"underlyingPrice","type":"uint256"}],"name":"FundingRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"quoteReserve","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseReserve","type":"uint256"},{"indexed":false,"internalType":"int256","name":"cumulativeNotional","type":"int256"}],"name":"LiquidityChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ammPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"adjustPrice","type":"uint256"},{"indexed":false,"internalType":"int256","name":"MMLiquidity","type":"int256"},{"indexed":false,"internalType":"int256","name":"MMPNL","type":"int256"},{"indexed":false,"internalType":"bool","name":"moved","type":"bool"}],"name":"MoveAMMPrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"quoteAssetReserve","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseAssetReserve","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"ReserveSnapshotted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"settlementPrice","type":"uint256"}],"name":"Shutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IExchangeTypes.Dir","name":"dir","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"quoteAssetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseAssetAmount","type":"uint256"}],"name":"SwapInput","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IExchangeTypes.Dir","name":"dir","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"quoteAssetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseAssetAmount","type":"uint256"}],"name":"SwapOutput","type":"event"},{"inputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"adjustedPosition","type":"tuple"},{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"oldAdjustedPosition","type":"tuple"}],"name":"adjustTotalPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseAssetReserve","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum ISakePerpVaultTypes.Risk","name":"_risk","type":"uint8"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"_baseAssetAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_fromQuoteReserve","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_fromBaseReserve","type":"tuple"}],"name":"calcBaseAssetAfterLiquidityMigration","outputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmount","type":"tuple"}],"name":"calcFee","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fluctuationLimitRatio","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingBufferPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingRate","outputs":[{"internalType":"int256","name":"d","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCumulativeNotional","outputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeState","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmount","type":"tuple"}],"name":"getInputPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetPoolAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetPoolAmount","type":"tuple"}],"name":"getInputPriceWithReserves","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmount","type":"tuple"}],"name":"getInputTwap","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestLiquidityChangedSnapshots","outputs":[{"components":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"cumulativeNotional","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"quoteAssetReserve","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"baseAssetReserve","type":"tuple"},{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"totalPositionSize","type":"tuple"}],"internalType":"struct IExchangeTypes.LiquidityChangedSnapshot","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"getLiquidityChangedSnapshots","outputs":[{"components":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"cumulativeNotional","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"quoteAssetReserve","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"baseAssetReserve","type":"tuple"},{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"totalPositionSize","type":"tuple"}],"internalType":"struct IExchangeTypes.LiquidityChangedSnapshot","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetReserve","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetReserve","type":"tuple"}],"name":"getMMUnrealizedPNL","outputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxHoldingBaseAsset","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOpenInterestNotionalCap","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetAmount","type":"tuple"}],"name":"getOutputPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetPoolAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetPoolAmount","type":"tuple"}],"name":"getOutputPriceWithReserves","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetAmount","type":"tuple"}],"name":"getOutputTwap","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserve","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSettlementPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSnapshotLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSpotPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalPositionSize","outputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_intervalInSeconds","type":"uint256"}],"name":"getTwapPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnderlyingPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_intervalInSeconds","type":"uint256"}],"name":"getUnderlyingTwapPrice","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initMarginRatio","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_quoteAssetReserve","type":"uint256"},{"internalType":"uint256","name":"_baseAssetReserve","type":"uint256"},{"internalType":"uint256","name":"_tradeLimitRatio","type":"uint256"},{"internalType":"uint256","name":"_fundingPeriod","type":"uint256"},{"internalType":"contract IPriceFeed","name":"_priceFeed","type":"address"},{"internalType":"contract ISakePerp","name":"_sakePerp","type":"address"},{"internalType":"contract ISakePerpVault","name":"_sakePerpVault","type":"address"},{"internalType":"bytes32","name":"_priceFeedKey","type":"bytes32"},{"internalType":"address","name":"_quoteAsset","type":"address"},{"internalType":"uint256","name":"_fluctuationLimitRatio","type":"uint256"},{"internalType":"uint256","name":"_priceAdjustRatio","type":"uint256"},{"internalType":"contract IExchangeState","name":"_exchangeState","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isOverSpreadLimit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastMoveAmmPriceTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationFeeRatio","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maintenanceMarginRatio","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidationFee","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_liquidityMultiplier","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_fluctuationLimitRatio","type":"tuple"}],"name":"migrateLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ISakePerpVaultTypes.Risk","name":"_risk","type":"uint8"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_oraclePrice","type":"uint256"},{"internalType":"bytes32","name":"_priceFeedKey","type":"bytes32"}],"name":"moveAMMPriceToOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mover","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextFundingTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"open","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oraclePriceSpreadLimit","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceAdjustRatio","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeed","outputs":[{"internalType":"contract IPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteAsset","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteAssetReserve","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"reserveSnapshots","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"quoteAssetReserve","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"baseAssetReserve","type":"tuple"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sakePerp","outputs":[{"internalType":"contract ISakePerp","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sakePerpVault","outputs":[{"internalType":"contract ISakePerpVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_counterParty","type":"address"}],"name":"setCounterParty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_exchangeState","type":"address"}],"name":"setExchangeState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_fluctuationLimitRatio","type":"tuple"}],"name":"setFluctuationLimitRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newMover","type":"address"}],"name":"setMover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_open","type":"bool"}],"name":"setOpen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_limit","type":"tuple"}],"name":"setOraclePriceSpreadLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_priceAdjustRatio","type":"tuple"}],"name":"setPriceAdjustRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_priceFeed","type":"address"}],"name":"setPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_interval","type":"uint256"}],"name":"setSpotPriceTwapInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"settleFunding","outputs":[{"components":[{"internalType":"int256","name":"d","type":"int256"}],"internalType":"struct SignedDecimal.signedDecimal","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spotPriceTwapInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"spreadRatio","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetAmountLimit","type":"tuple"}],"name":"swapInput","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IExchangeTypes.Dir","name":"_dir","type":"uint8"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_baseAssetAmount","type":"tuple"},{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"_quoteAssetAmountLimit","type":"tuple"},{"internalType":"bool","name":"_skipFluctuationCheck","type":"bool"}],"name":"swapOutput","outputs":[{"components":[{"internalType":"uint256","name":"d","type":"uint256"}],"internalType":"struct Decimal.decimal","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalPositionSize","outputs":[{"internalType":"int256","name":"d","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradeLimitRatio","outputs":[{"internalType":"uint256","name":"d","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506152a7806100206000396000f3fe608060405234801561001057600080fd5b506004361061043e5760003560e01c806383acb48a11610236578063dc3184a01161013b578063eebab8ef116100c3578063fc0e74d111610087578063fc0e74d11461082a578063fca3b5aa14610832578063fcfff16f14610845578063fdf262b71461084d578063ffbf688e146108555761043e565b8063eebab8ef146107d6578063eebd72e0146107e9578063f1b5df86146107fc578063f1e286501461080f578063f2fde38b146108175761043e565b8063e4bc2eb91161010a578063e4bc2eb9146107ae578063e57d5636146107b6578063ec2c0e63146107be578063ed83d79c146107c6578063eeb016f2146107ce5761043e565b8063dc3184a014610778578063dc76fabc1461078b578063e0037a6c14610793578063e1f1027f1461079b5761043e565b8063a8f8be4e116101be578063c2de442f1161018d578063c2de442f1461073a578063c9566fcc14610742578063cfe711031461074a578063d835e52314610752578063da0c5927146107655761043e565b8063a8f8be4e146106f9578063ae09a4571461070c578063ba0c476314610714578063bea69f74146107275761043e565b80638f40d932116102055780638f40d932146106a35780639bf5d1d4146106ab5780639e010362146106be5780639ece77c8146106d35780639ffca62f146106e65761043e565b806383acb48a146106785780638da5cb5b146106805780638e483328146106885780638eb86e8b146106905761043e565b806350799c811161034757806362e7a176116102cf5780637155c361116102935780637155c3611461062f578063724e78da14610642578063741bef1a1461065557806374d7c62b1461065d57806377481024146106655761043e565b806362e7a176146105e65780636fa42ede146105f95780636fdca5e01461060c5780636febdd501461061f578063715018a6146106275761043e565b806358a4c3dc1161031657806358a4c3dc146105a557806359bf5d39146105ad5780635f1ba1fd146105c357806360d98201146105cb57806362267955146105d35761043e565b806350799c8114610562578063517587c614610575578063525454101461058a5780635834db90146105925761043e565b806334953249116103ca578063429f24bc11610399578063429f24bc1461052f57806342b3198b14610537578063468f02d21461053f5780634894d183146105475780634cb876f21461055a5761043e565b806334953249146104ec57806340098b75146104ff57806340d71cd91461051457806341d3c84c146105275761043e565b80631d3acb44116104115780631d3acb441461049357806320855f051461049b57806321e00985146104ae578063237f17ee146104d157806329f9b17b146104e45761043e565b80630d451c8f146104435780630f999b151461046157806311377394146104695780631b584d6c1461047e575b600080fd5b61044b61085d565b604051610458919061496e565b60405180910390f35b61044b610863565b610471610869565b6040516104589190615168565b6104866108fc565b604051610458919061512f565b61044b610984565b6104716104a93660046147a5565b61098a565b6104c16104bc3660046147fd565b610a69565b6040516104589493929190615182565b6104866104df3660046147fd565b610ab7565b610471610b60565b6104716104fa3660046147fd565b610b7c565b61051261050d36600461457b565b610c1b565b005b6104716105223660046147c1565b610c98565b61044b610d49565b61044b610d4f565b61044b610d55565b610471610d5b565b610471610555366004614652565b610df8565b61044b610e0f565b610471610570366004614687565b610e15565b61057d610eae565b6040516104589190614904565b610471610ebd565b6105126105a036600461473a565b610f13565b61044b6111e7565b6105b56111ed565b604051610458929190615172565b61044b61121f565b61057d611225565b6104716105e136600461471f565b611234565b6105126105f43660046147fd565b6112bc565b610471610607366004614652565b611313565b61051261061a366004614597565b61132a565b6104716113d2565b610512611428565b61051261063d36600461457b565b6114a7565b61051261065036600461457b565b611524565b61057d6115a1565b61044b6115b0565b610512610673366004614765565b6115b6565b610471611626565b61057d61167c565b61057d61168b565b61051261069e3660046146df565b61169a565b61044b61173a565b6104716106b9366004614687565b611740565b6106c661177e565b6040516104589190614963565b6105126106e136600461457b565b6118a5565b6104716106f43660046145b3565b6118fc565b610512610707366004614765565b611b2b565b61057d611b66565b6105126107223660046147a5565b611b75565b61051261073536600461484e565b611bd8565b61044b6120c7565b61044b6120cd565b6104716120d3565b610512610760366004614765565b612129565b610471610773366004614652565b612164565b6104716107863660046145f9565b612196565b610471612224565b61044b612253565b6104716107a9366004614652565b612259565b61044b61228b565b610471612291565b6104716122e7565b610471612303565b610471612479565b6105126107e43660046146df565b6124cf565b6105126107f736600461482d565b612561565b61047161080a3660046147fd565b612aa7565b61044b612ab8565b61051261082536600461457b565b612abe565b610512612b75565b61051261084036600461457b565b612d49565b6106c6612da0565b61057d612db0565b610471612dbf565b60a75490565b60ad5481565b610871614473565b60af60009054906101000a90046001600160a01b03166001600160a01b031663113773946040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b505afa1580156108d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f79190614780565b905090565b610904614486565b60a18054610913906001612ddb565b8154811061091d57fe5b60009182526020918290206040805160a0810182526004909302909101805460808401908152835281518085018352600182015481528385015281518085018352600282015481528383015281519384019091526003015482526060810191909152905090565b609f5481565b610992614473565b61099a614473565b604080516020810190915260975481526109b390612e1d565b156109ef57604080516020810190915260975481526109e8906109e3906001906109dc90612e3b565b8688611740565b612e69565b9050610a2c565b610a29600019610a236109e36000610a1c6097604051806020016040529081600082015481525050612e3b565b888a611740565b90612ecd565b90505b6040805160208082018352609a54825282519081019092526099548252610a5f918391610a599190612ef2565b90612f0f565b9150505b92915050565b60a78181548110610a7657fe5b600091825260209182902060408051808501825260049093029091018054835281519384019091526001810154835260028101546003909101549193509084565b610abf614486565b60a1548210610ae95760405162461bcd60e51b8152600401610ae090614c61565b60405180910390fd5b60a18281548110610af657fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018201548152838501528151808501835260028201548152838301528151938401909152600301548252606081019190915290505b919050565b610b68614473565b506040805160208101909152609954815290565b610b84614473565b60408051602081019182905260aa5460a654632885cf7d60e21b909352909182916001600160a01b03169063a2173df490610bc3908760248601614977565b60206040518083038186803b158015610bdb57600080fd5b505afa158015610bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c139190614815565b905292915050565b610c23612f2c565b6033546001600160a01b03908116911614610c505760405162461bcd60e51b8152600401610ae090614ebc565b6001600160a01b038116610c765760405162461bcd60e51b8152600401610ae090614cb9565b60ae80546001600160a01b0319166001600160a01b0392909216919091179055565b610ca0614473565b610ca984612f30565b610cb4575082610d42565b600080610cc086612f42565b13610ccc576000610ccf565b60015b9050610cd9614473565b610cfb82610ce8576001610ceb565b60005b610cf488612e3b565b8787611740565b9050610d05614473565b610d206109e384610d17576000610d1a565b60015b84612259565b9050610d3c83610d3257600019610d35565b60015b8290612ecd565b93505050505b9392505050565b60985481565b60ac5481565b60a15490565b610d63614473565b60408051602081019182905260aa5460a6546331d98b3f60e01b909352909182916001600160a01b0316906331d98b3f90610da1906024850161496e565b60206040518083038186803b158015610db957600080fd5b505afa158015610dcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df19190614815565b9052905090565b610e00614473565b610d4283836000610384612f46565b60975481565b610e1d614473565b60af546040516350799c8160e01b81526001600160a01b03909116906350799c8190610e53908890889088908890600401614985565b60206040518083038186803b158015610e6b57600080fd5b505afa158015610e7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea39190614780565b90505b949350505050565b60a8546001600160a01b031681565b610ec5614473565b60af60009054906101000a90046001600160a01b03166001600160a01b031663525454106040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b610f1b612f2c565b6033546001600160a01b03908116911614610f485760405162461bcd60e51b8152600401610ae090614ebc565b610f58610f53612fcc565b612f42565b610f6a610f5336859003850185614765565b1415610f885760405162461bcd60e51b8152600401610ae090614a4b565b60408051602081019091526097548152610fb090610fab36859003850185614765565b612fe9565b610fc7610fc236839003830183614765565b613069565b610fcf614473565b506040805160208101909152609d548152610fe8614473565b506040805160208101909152609e548152611001614473565b506040805160208101909152609754815261102b61102436879003870187614765565b84906131b9565b51609d5561104861104136879003870187614765565b83906131b9565b51609e55611057818484610c98565b5160978190556040805160a081018252609954608082019081528152815160208082018452609d8054835281840192835284518083018652609e548152848601908152855180840187529687526060850196875260a180546001810182556000919091529451517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f6498786004909602958601559251517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f6498798501559151517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f64987a8401559351517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f64987b9092019190915581519283019091525481527feba648fc83514e32ba75e27696ee5fa3610ebc7a77460a3ac3e8724ffa1be4cc9061119790612f42565b6040805160208101909152609e5481526111b090612f42565b604080516020810190915260995481526111c990612f42565b6040516111d89392919061519d565b60405180910390a15050505050565b60a65481565b6111f5614473565b6111fd614473565b50506040805160208082018352609d5482528251908101909252609e54825291565b60a25481565b60af546001600160a01b031690565b61123c614473565b60af54604051636226795560e01b81526001600160a01b039091169063622679559061126c90859060040161515e565b60206040518083038186803b15801561128457600080fd5b505afa158015611298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a639190614780565b6112c4612f2c565b6033546001600160a01b039081169116146112f15760405162461bcd60e51b8152600401610ae090614ebc565b8061130e5760405162461bcd60e51b8152600401610ae090614b4f565b60a255565b61131b614473565b610d4283836001610384612f46565b611332612f2c565b6033546001600160a01b0390811691161461135f5760405162461bcd60e51b8152600401610ae090614ebc565b60ab5460ff600160a01b909104161515811515141561137d576113cf565b60ab805482158015600160a01b0260ff60a01b19909216919091179091556113cf576113cb610e106113c5610e106113bf60a3546113b96131d6565b906131da565b906131ff565b90613241565b60a5555b50565b6113da614473565b60af60009054906101000a90046001600160a01b03166001600160a01b0316636febdd506040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b611430612f2c565b6033546001600160a01b0390811691161461145d5760405162461bcd60e51b8152600401610ae090614ebc565b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b6114af612f2c565b6033546001600160a01b039081169116146114dc5760405162461bcd60e51b8152600401610ae090614ebc565b6001600160a01b0381166115025760405162461bcd60e51b8152600401610ae090614cb9565b60af80546001600160a01b0319166001600160a01b0392909216919091179055565b61152c612f2c565b6033546001600160a01b039081169116146115595760405162461bcd60e51b8152600401610ae090614ebc565b6001600160a01b03811661157f5760405162461bcd60e51b8152600401610ae090614cb9565b60aa80546001600160a01b0319166001600160a01b0392909216919091179055565b60aa546001600160a01b031681565b60a35481565b6115be612f2c565b6033546001600160a01b039081169116146115eb5760405162461bcd60e51b8152600401610ae090614ebc565b60006115ff6115f8612fcc565b839061327b565b60000b13156116205760405162461bcd60e51b8152600401610ae090614f38565b5160a055565b61162e614473565b60af60009054906101000a90046001600160a01b03166001600160a01b03166383acb48a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b6033546001600160a01b031690565b60ab546001600160a01b031681565b6116a2612f2c565b60a8546001600160a01b039081169116146116cf5760405162461bcd60e51b8152600401610ae090614ddf565b60af54604051638eb86e8b60e01b81526001600160a01b0390911690638eb86e8b90611703908690869086906004016149ce565b600060405180830381600087803b15801561171d57600080fd5b505af1158015611731573d6000803e3d6000fd5b50505050505050565b609c5481565b611748614473565b60af546040516326fd747560e21b81526001600160a01b0390911690639bf5d1d490610e53908890889088908890600401614985565b6000611788614473565b611790610d5b565b9050600061179d82612f42565b116117ba5760405162461bcd60e51b8152600401610ae090614c8a565b6117c2614473565b6117ca612224565b90506117d4614473565b6117f96117f4846117ee866117e887612e69565b906132ac565b9061331b565b612e3b565b905061188460af60009054906101000a90046001600160a01b03166001600160a01b03166337292b606040518163ffffffff1660e01b815260040160206040518083038186803b15801561184c57600080fd5b505afa158015611860573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f539190614780565b61188d82612f42565b101561189a57600061189d565b60015b935050505090565b6118ad612f2c565b6033546001600160a01b039081169116146118da5760405162461bcd60e51b8152600401610ae090614ebc565b60ab80546001600160a01b0319166001600160a01b0392909216919091179055565b611904614473565b60ab54600160a01b900460ff1661192d5760405162461bcd60e51b8152600401610ae090614f5f565b611935612f2c565b60ab546001600160a01b039081169116146119625760405162461bcd60e51b8152600401610ae090614b86565b611974610f5336859003850185614765565b6119875761198061338f565b9050610d42565b600184600181111561199557fe5b14156119f5576119ad610f5336859003850185614765565b6040805160208082018352609c5482528251908101909252609d5482526119d791610f53916131b9565b10156119f55760405162461bcd60e51b8152600401610ae090614bfe565b6119fd614473565b611a10856107a936879003870187614765565b9050611a24610f5336859003850185614765565b15611ab4576000856001811115611a3757fe5b1415611a7b57611a4f610f5336859003850185614765565b611a5882612f42565b1015611a765760405162461bcd60e51b8152600401610ae090614e85565b611ab4565b611a8d610f5336859003850185614765565b611a9682612f42565b1115611ab45760405162461bcd60e51b8152600401610ae090614fc3565b611acf85611ac736879003870187614765565b8360006133aa565b7fae6a2b946841d9afc0e1e19a94ae4af26f01125b87b5095bbfb177a9741a2ede85611b03610f5336889003880188614765565b611b0c84612f42565b604051611b1b939291906149ad565b60405180910390a1949350505050565b611b33612f2c565b6033546001600160a01b03908116911614611b605760405162461bcd60e51b8152600401610ae090614ebc565b51609f55565b60ae546001600160a01b031681565b611b7d612f2c565b60ab546001600160a01b03908116911614611baa5760405162461bcd60e51b8152600401610ae090614b86565b60408051602081019091526097548152611bd0908290611bca9085612f0f565b90612ef2565b516097555050565b600054610100900460ff1680611bf15750611bf16134e0565b80611bff575060005460ff16155b611c1b5760405162461bcd60e51b8152600401610ae090614d50565b600054610100900460ff16158015611c46576000805460ff1961ff0019909116610100171660011790555b8c15801590611c5457508a15155b8015611c5f57508b15155b8015611c6a57508915155b8015611c7e57506001600160a01b03891615155b8015611c9257506001600160a01b03881615155b8015611ca657506001600160a01b03871615155b8015611cba57506001600160a01b03821615155b8015611cce57506001600160a01b03851615155b611cea5760405162461bcd60e51b8152600401610ae090615061565b611cf26134e6565b6040805160208082018352908f9052609d8f9055815180820183528e9052609e8e9055815180820183528d9052609c8d90558151808201835285905260a08590558151908101909152849052609f84905560a38a9055611d538a60026131ff565b60a455610e1060a25560a686905560a980546001600160a01b03199081166001600160a01b038881169190911790925560aa805482168c841617905560ab805482168b841617905560a8805482168a841617905560af80549091169184169190911790556040805160208101909152670429d069189e00009081905260ad55611dda612f2c565b60ae80546001600160a01b0319166001600160a01b0392831617905560a854604051632acd9be960e11b815291169063559b37d290611e2490309061032090606490600401614942565b600060405180830381600087803b158015611e3e57600080fd5b505af1158015611e52573d6000803e3d6000fd5b505060a8546040516386be9e8b60e01b81526001600160a01b0390911692506386be9e8b9150611e8c903090600090603290600401614918565b600060405180830381600087803b158015611ea657600080fd5b505af1158015611eba573d6000803e3d6000fd5b505060a8546040516386be9e8b60e01b81526001600160a01b0390911692506386be9e8b9150611ef4903090600190601990600401614918565b600060405180830381600087803b158015611f0e57600080fd5b505af1158015611f22573d6000803e3d6000fd5b5050505060a16040518060800160405280611f3b61338f565b81526040805160208181018352609d5482528084019190915281519081018252609e54815290820152606001611f6f61338f565b9052815460018181018455600093845260209384902083515160049093020191825583830151519082015560408083015151600283015560609092015151600390910155805160a081018252609d5460808201908152815281518084018352609e5481529281019290925260a791908101611fe86131d6565b8152602001611ff5613578565b90528154600181810184556000938452602093849020835151600490930201918255838301515190820155604080830151600283015560609092015160039091015580519182019052609d5481527f3a3348362552c3897fd1f06a3233519ebd8bd76ad6e99a418a9741155fe905159061206e90612f42565b6040805160208101909152609e54815261208790612f42565b61208f6131d6565b60405161209e9392919061519d565b60405180910390a180156120b8576000805461ff00191690555b50505050505050505050505050565b60a45481565b609e5481565b6120db614473565b60af60009054906101000a90046001600160a01b03166001600160a01b031663cfe711036040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b612131612f2c565b6033546001600160a01b0390811691161461215e5760405162461bcd60e51b8152600401610ae090614ebc565b5160ad55565b61216c614473565b6040805160208082018352609d5482528251908101909252609e548252610d429185918591611740565b61219e614473565b60ab54600160a01b900460ff166121c75760405162461bcd60e51b8152600401610ae090614f5f565b6121cf612f2c565b60ab546001600160a01b039081169116146121fc5760405162461bcd60e51b8152600401610ae090614b86565b610ea38561220f36879003870187614765565b61221e36879003870187614765565b8561357c565b61222c614473565b6040805160208082018352609e5482528251908101909252609d5482526108f7919061372f565b60a55481565b612261614473565b6040805160208082018352609d5482528251908101909252609e548252610d429185918591610e15565b609d5481565b612299614473565b60af60009054906101000a90046001600160a01b03166001600160a01b031663e57d56366040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b6122ef614473565b506040805160208101909152609b54815290565b61230b614473565b60ab54600160a01b900460ff166123345760405162461bcd60e51b8152600401610ae090614f5f565b61233c612f2c565b60ab546001600160a01b039081169116146123695760405162461bcd60e51b8152600401610ae090614b86565b60a5546123746131d6565b10156123925760405162461bcd60e51b8152600401610ae090614ffa565b61239a614473565b6123a261338f565b90506123ac614473565b6123b760a254610b7c565b905060a5546123d360a35460ac546131da90919063ffffffff16565b101561241b576123e1614473565b6123f3826117e86109e360a254612aa7565b90506124176201518061241160a3548461374c90919063ffffffff16565b906137a9565b9250505b61242582826137c5565b600061243560a4546113b96131d6565b9050600061245c610e106113c5610e106113bf60a35460a5546131da90919063ffffffff16565b905081811161246b578161246d565b805b60a55550919250505090565b612481614473565b60af60009054906101000a90046001600160a01b03166001600160a01b031663eeb016f26040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b60ab54600160a01b900460ff166124f85760405162461bcd60e51b8152600401610ae090614f5f565b612500612f2c565b60a8546001600160a01b0390811691161461252d5760405162461bcd60e51b8152600401610ae090614ddf565b60af5460405163eebab8ef60e01b81526001600160a01b039091169063eebab8ef90611703908690869086906004016149ce565b612569612f2c565b60ae546001600160a01b039081169116148061259a5750612588612f2c565b60aa546001600160a01b039081169116145b6125b65760405162461bcd60e51b8152600401610ae090614a7a565b600082116125d65760405162461bcd60e51b8152600401610ae090614ce2565b8060a654146125f75760405162461bcd60e51b8152600401610ae090615031565b60ab54600160a01b900460ff1615806126265750604080516020810190915260a054815261262490612f42565b155b1561263057612aa3565b612638614473565b50604080516020810190915282815261264f614473565b6040805160208082018352609e5482528251908101909252609d548252612676919061372f565b604080516020810190915260ad5481529091506000906126b09061269a90846131b9565b6126aa6117f4856117e888612e69565b9061327b565b60000b13156126d15760405162461bcd60e51b8152600401610ae0906150cc565b6126d9614473565b604080516020810190915260a0548152612711906117f49061270890612702866117e889612e69565b90613831565b610a5985612e69565b905061271b614473565b6040805160208082018352609e5482528251908101909252609d54825261274291906131b9565b90506000612761612754610f53612fcc565b6113c5610f53858761372f565b905061276b614473565b604051806020016040528061277f8461389c565b9052905061278b614473565b612795848361372f565b905061279f614473565b6127a9838361098a565b90506127b3614473565b60a85460405163180c91f160e01b81526001600160a01b039091169063180c91f1906127e3903090600401614904565b60206040518083038186803b1580156127fb57600080fd5b505afa15801561280f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128339190614780565b905061283d614473565b60a85460405163633ceadb60e01b81526001600160a01b039091169063633ceadb9061286d903090600401614904565b60206040518083038186803b15801561288557600080fd5b505afa158015612899573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128bd9190614780565b90506128db6128d6826128d08686612f0f565b906138f6565b612e1d565b15612947577f2ad23a1b94bd43bf2d22d82564f2f357d4428e8146810a57fbaef7b88ddc6fba61290a8a612f42565b8d6129148b612f42565b61291d86612f42565b61292688612f42565b600060405161293a969594939291906151b3565b60405180910390a1612a98565b61294f614473565b6040805160208101909152609d54815261296c906117e887612e69565b6040805160208101909152609a5481529091506129899082612f0f565b51609a55604080516020810190915260995481526129a79082612f0f565b516099558551609e558451609d556129bd6131d6565b60ac556129c861395b565b60a860009054906101000a90046001600160a01b03166001600160a01b0316638df3fab36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612a1857600080fd5b505af1158015612a2c573d6000803e3d6000fd5b505050507f2ad23a1b94bd43bf2d22d82564f2f357d4428e8146810a57fbaef7b88ddc6fba612a5a8b612f42565b8e612a648c612f42565b612a71610f5388886138f6565b612a7a89612f42565b6001604051612a8e969594939291906151b3565b60405180910390a1505b505050505050505050505b5050565b612aaf614473565b610a6382613a93565b60a05481565b612ac6612f2c565b6033546001600160a01b03908116911614612af35760405162461bcd60e51b8152600401610ae090614ebc565b6001600160a01b038116612b195760405162461bcd60e51b8152600401610ae090614ad2565b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b612b7d612f2c565b6033546001600160a01b03908116911614612baa5760405162461bcd60e51b8152600401610ae090614ebc565b60a860009054906101000a90046001600160a01b03166001600160a01b0316638df3fab36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612bfa57600080fd5b505af1158015612c0e573d6000803e3d6000fd5b50505050612c1a614486565b612c226108fc565b9050612c2c614473565b60208201516040830151612c3f916131b9565b9050612c49614473565b60408301516060840151612c5c916138f6565b9050612c66614473565b612c7982612c7385612e69565b90613ac4565b9050612c83614473565b6040805160208101909152609d548152612c9e9083906132ac565b60408051602081019091526097548152909150606490612cbd90612f30565b1115612cf45760408051602081019091526097548152612cef90612ce090612e3b565b612ce983612e3b565b9061372f565b51609b555b60ab805460ff60a01b191690556040805160208101909152609b5481527f574214b195bf5273a95bb4498e35cf1fde0ce327c727a95ec2ab359f7ba4e11a90612d3c90612f42565b6040516111d8919061496e565b612d51612f2c565b6033546001600160a01b03908116911614612d7e5760405162461bcd60e51b8152600401610ae090614ebc565b60a880546001600160a01b0319166001600160a01b0392909216919091179055565b60ab54600160a01b900460ff1681565b60a9546001600160a01b031681565b612dc7614473565b506040805160208101909152609754815290565b6000610d4283836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613ae1565b60008082600001511215612e3357506001610b5b565b506000919050565b612e43614473565b612e4b614473565b825160001315612e615782516000038152610a63565b915182525090565b612e71614473565b8180600001516001600160ff1b03101560405180606001604052806033815260200161523f6033913990612eb85760405162461bcd60e51b8152600401610ae091906149f8565b50506040805160208101909152915182525090565b612ed5614473565b612edd614473565b8351612ee99084613b0d565b81529392505050565b612efa614473565b612f02614473565b82518451612ee991613b78565b612f17614473565b612f1f614473565b82518451612ee991613bbe565b3390565b6000612f3b82612e3b565b5192915050565b5190565b612f4e614473565b612f566144c5565b600180825260a754612f6791612ddb565b60208201526040810151866001811115612f7d57fe5b90816001811115612f8a57fe5b905250604080820180516020018790525101846001811115612fa857fe5b90816001811115612fb557fe5b905250612fc28184613c04565b9695505050505050565b612fd4614473565b6040518060200160405280610df16012613e2f565b6000612ff483612f42565b1315612aa357613002614473565b6040805160208082018352609e5482528251908101909252633b9aca008252613035916117f491906117ee9087906138f6565b90506000613043838361327b565b60000b12156130645760405162461bcd60e51b8152600401610ae090614c2a565b505050565b600061307482612f42565b11156113cf5760a7546130856144e4565b60a7600183038154811061309557fe5b60009182526020918290206040805160a08101825260049093029091018054608084019081528352815180850183526001820154815293830193909352600283015490820152600390910154606082015290506130f0613578565b81606001511480156131025750600182115b1561316b5760a7600283038154811061311757fe5b60009182526020918290206040805160a08101825260049093029091018054608084019081528352815180850183526001820154815293830193909352600283015490820152600390910154606082015290505b6040805160208082018352609e5482528251908101909252609d54825261319c916131959161372f565b8483613e38565b156130645760405162461bcd60e51b8152600401610ae090614d19565b6131c1614473565b6131c9614473565b82518451612ee991613eda565b4290565b600082820183811015610d425760405162461bcd60e51b8152600401610ae090614b18565b6000610d4283836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613ee8565b60008261325057506000610a63565b8282028284828161325d57fe5b0414610d425760405162461bcd60e51b8152600401610ae090614e44565b80518251600091101561329057506001610a63565b8151835110156132a35750600019610a63565b50600092915050565b6132b4614473565b8180600001516001600160ff1b03101560405180606001604052806033815260200161523f60339139906132fb5760405162461bcd60e51b8152600401610ae091906149f8565b50613304614473565b8351855161331191613b78565b8152949350505050565b613323614473565b8180600001516001600160ff1b03101560405180606001604052806033815260200161523f603391399061336a5760405162461bcd60e51b8152600401610ae091906149f8565b50613373614473565b61338661337f85612e69565b8690613ac4565b95945050505050565b613397614473565b5060408051602081019091526000815290565b60008460018111156133b857fe5b141561343b576040805160208101909152609d5481526133d89084613f1f565b51609d556040805160208101909152609e5481526133f69083613f3c565b51609e556040805160208101909152609754815261341490836138f6565b516097556040805160208101909152609954815261343290846138f6565b516099556134b4565b6040805160208101909152609d5481526134559084613f3c565b51609d556040805160208101909152609e5481526134739083613f1f565b51609e556040805160208101909152609754815261349190836132ac565b51609755604080516020810190915260995481526134af90846132ac565b516099555b806134d2576040805160208101909152609f5481526134d290613069565b6134da61395b565b50505050565b303b1590565b600054610100900460ff16806134ff57506134ff6134e0565b8061350d575060005460ff16155b6135295760405162461bcd60e51b8152600401610ae090614d50565b600054610100900460ff16158015613554576000805460ff1961ff0019909116610100171660011790555b61355c613f59565b613564613fda565b80156113cf576000805461ff001916905550565b4390565b613584614473565b61358d84612f42565b6135a05761359961338f565b9050610ea6565b60018560018111156135ae57fe5b1415613605576135bd84612f42565b6040805160208082018352609c5482528251908101909252609e5482526135e791610f53916131b9565b10156136055760405162461bcd60e51b8152600401610ae090614bfe565b61360d614473565b6136178686612164565b905061362284612f42565b156136a057600086600181111561363557fe5b14156136705761364484612f42565b61364d82612f42565b101561366b5760405162461bcd60e51b8152600401610ae090614e0d565b6136a0565b61367984612f42565b61368282612f42565b11156136a05760405162461bcd60e51b8152600401610ae090614f8c565b826136b3576136b08682876140b4565b92505b6136db60008760018111156136c457fe5b146136d05760006136d3565b60015b8287866133aa565b7f0dd4066b1a6ce97fb670c3e4201e908c644193f38cbdaffd0229d7e26da3e5338661370683612f42565b61370f88612f42565b60405161371e939291906149ad565b60405180910390a195945050505050565b613737614473565b61373f614473565b82518451612ee9916141d0565b613754614473565b816001600160ff1b03101560405180606001604052806033815260200161523f60339139906137965760405162461bcd60e51b8152600401610ae091906149f8565b5061379f614473565b610a5f8484612ecd565b6137b1614473565b6137b9614473565b8351612ee990846141de565b6137cf828261331b565b51609881905560408051602081019091529081527fd2805fe76d30598332a67c1061cee82e2e102b0f59f5457b1729bce028a054a09061380e90612f42565b61381783612f42565b604051613825929190614977565b60405180910390a15050565b613839614473565b8180600001516001600160ff1b03101560405180606001604052806033815260200161523f60339139906138805760405162461bcd60e51b8152600401610ae091906149f8565b50613889614473565b61338661389585612e69565b8690614242565b600060038211156138e05781600160028204015b818110156138d8578091506002818286816138c757fe5b0401816138d057fe5b0490506138b0565b509050610b5b565b81156138ee57506001610b5b565b506000610b5b565b6138fe614473565b8180600001516001600160ff1b03101560405180606001604052806033815260200161523f60339139906139455760405162461bcd60e51b8152600401610ae091906149f8565b5061394e614473565b8351855161331191613bbe565b6000613965613578565b60a78054919250600091600019810190811061397d57fe5b9060005260206000209060040201905080600301548214156139ab57609d548155609e546001820155613a28565b6040805160a081018252609d54608082019081528152815160208181018452609e54825282015260a79181016139df6131d6565b8152602090810185905282546001818101855560009485529382902083515160049092020190815590820151519281019290925560408101516002830155606001516003909101555b6040805160208101909152609d5481527f3a3348362552c3897fd1f06a3233519ebd8bd76ad6e99a418a9741155fe9051590613a6390612f42565b6040805160208101909152609e548152613a7c90612f42565b613a846131d6565b6040516138259392919061519d565b613a9b614473565b613aa36144c5565b6000815260a754613ab5906001612ddb565b6020820152610d428184613c04565b613acc614473565b613ad4614473565b82518451612ee99161425f565b60008184841115613b055760405162461bcd60e51b8152600401610ae091906149f8565b505050900390565b600082613b1c57506000610a63565b82600019148015613b305750600160ff1b82145b15613b4d5760405162461bcd60e51b8152600401610ae090614ef1565b82820282848281613b5a57fe5b0514610d425760405162461bcd60e51b8152600401610ae090614ef1565b6000818303818312801590613b8d5750838113155b80613ba25750600083128015613ba257508381135b610d425760405162461bcd60e51b8152600401610ae090615088565b6000828201818312801590613bd35750838112155b80613be85750600083128015613be857508381125b610d425760405162461bcd60e51b8152600401610ae090614bbd565b613c0c614473565b613c14614473565b613c1d8461426d565b905082613c2b579050610a63565b6000613c3f84613c396131d6565b90612ddb565b9050613c496144e4565b60a7866020015181548110613c5a57fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018083015482529484015260028101549183019190915260030154606082015260a7549092501480613cc0575081816040015111155b15613cd057829350505050610a63565b60408101516000613ce382613c396131d6565b9050613ced614473565b613cf786836143c5565b90505b6020890151613d1a57613d0d81836143e1565b9650505050505050610a63565b6020890151613d2a906001612ddb565b60208a0181905260a780549091908110613d4057fe5b60009182526020918290206040805160a0810182526004909302909101805460808401908152835281518085018352600182015481529383019390935260028301549082015260039091015460608201529350613d9c8961426d565b955084846040015111613dce57613dc7613dc0613db98588612ddb565b88906143c5565b8290613f1f565b9050613e18565b6000613de7856040015185612ddb90919063ffffffff16565b9050613dfd613df688836143c5565b8390613f1f565b9150613e0983826131da565b92508460400151935050613cfa565b613e2281896143e1565b9998505050505050505050565b60ff16600a0a90565b6000613e42614473565b60208301518351613e529161372f565b9050613e5c614473565b613e7161104186613e6b612fcc565b90613f1f565b9050613e7b614473565b613e9061102487613e8a612fcc565b90613f3c565b90506000613e9e888461327b565b60000b13158015613ebc57506000613eb6888361327b565b60000b12155b15613ecd5760009350505050610d42565b5060019695505050505050565b6000610d42838360126143fd565b60008183613f095760405162461bcd60e51b8152600401610ae091906149f8565b506000838581613f1557fe5b0495945050505050565b613f27614473565b613f2f614473565b82518451612ee9916131da565b613f44614473565b613f4c614473565b82518451612ee991612ddb565b600054610100900460ff1680613f725750613f726134e0565b80613f80575060005460ff16155b613f9c5760405162461bcd60e51b8152600401610ae090614d50565b600054610100900460ff16158015613564576000805460ff1961ff00199091166101001716600117905580156113cf576000805461ff001916905550565b600054610100900460ff1680613ff35750613ff36134e0565b80614001575060005460ff16155b61401d5760405162461bcd60e51b8152600401610ae090614d50565b600054610100900460ff16158015614048576000805460ff1961ff0019909116610100171660011790555b6000614052612f2c565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35080156113cf576000805461ff001916905550565b60006140be614473565b60008560018111156140cc57fe5b1461410e576040805160208101909152609e548152614109906140ef9085613f3c565b6040805160208101909152609d548152612ce99087613f1f565b614146565b6040805160208101909152609e5481526141469061412c9085613f1f565b6040805160208101909152609d548152612ce99087613f3c565b6040805160208101909152609f54815260a780549293506133869284929190614170906001612ddb565b8154811061417a57fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018201548152938301939093526002830154908201526003909101546060820152613e38565b6000610d4283836012614415565b6000816141fd5760405162461bcd60e51b8152600401610ae0906150fa565b816000191480156142115750600160ff1b83145b1561422e5760405162461bcd60e51b8152600401610ae090614d9e565b600082848161423957fe5b05949350505050565b61424a614473565b614252614473565b82518451612ee99161442e565b6000610d428383601261443c565b614275614473565b61427d6144e4565b60a783602001518154811061428e57fe5b600091825260208083206040805160a081018252600490940290910180546080850190815284528151808401835260018201548152928401929092526002820154908301526003015460608201529150835160018111156142eb57fe5b141561430957602081015181516143019161372f565b915050610b5b565b60018351600181111561431857fe5b14156143ad5761432f836040015160200151612f42565b61433b5761430161338f565b6000836040015160400151600181111561435157fe5b141561437457604083015180516020918201518351928401516143019390610e15565b6001836040015160400151600181111561438a57fe5b14156143ad57604083015180516020918201518351928401516143019390611740565b60405162461bcd60e51b8152600401610ae090614aa4565b6143cd614473565b6143d5614473565b8351612ee99084613241565b6143e9614473565b6143f1614473565b8351612ee990846131ff565b6000610ea661440b83613e2f565b6113bf8686613241565b6000610ea6836113bf61442785613e2f565b8790613241565b6000610d428383601261445b565b6000610ea68361445561444e85613e2f565b8790613b0d565b906141de565b6000610ea661446983613e2f565b6144558686613b0d565b6040518060200160405280600081525090565b6040518060800160405280614499614473565b81526020016144a6614473565b81526020016144b3614473565b81526020016144c0614473565b905290565b60408051606081018252600080825260208201529081016144c0614518565b60405180608001604052806144f7614473565b8152602001614504614473565b815260200160008152602001600081525090565b60408051606081019091528060008152602001614533614473565b815260200160006144c0565b600060208284031215614550578081fd5b50919050565b600060208284031215614567578081fd5b61457160206151dd565b9135825250919050565b60006020828403121561458c578081fd5b8135610d428161520e565b6000602082840312156145a8578081fd5b8135610d4281615223565b6000806000606084860312156145c7578182fd5b83356145d281615231565b92506145e1856020860161453f565b91506145f0856040860161453f565b90509250925092565b6000806000806080858703121561460e578081fd5b843561461981615231565b9350614628866020870161453f565b9250614637866040870161453f565b9150606085013561464781615223565b939692955090935050565b60008060408385031215614664578182fd5b823561466f81615231565b915061467e8460208501614556565b90509250929050565b6000806000806080858703121561469c578384fd5b84356146a781615231565b93506146b68660208701614556565b92506146c58660408701614556565b91506146d48660608701614556565b905092959194509250565b6000806000606084860312156146f3578283fd5b83356146fe81615231565b9250602084013561470e8161520e565b929592945050506040919091013590565b600060208284031215614730578081fd5b610d42838361453f565b6000806040838503121561474c578182fd5b614756848461453f565b915061467e846020850161453f565b600060208284031215614776578081fd5b610d428383614556565b600060208284031215614791578081fd5b61479b60206151dd565b9151825250919050565b600080604083850312156147b7578182fd5b61466f8484614556565b6000806000606084860312156147d5578081fd5b6147df8585614556565b92506147ee8560208601614556565b91506145f08560408601614556565b60006020828403121561480e578081fd5b5035919050565b600060208284031215614826578081fd5b5051919050565b6000806040838503121561483f578182fd5b50508035926020909101359150565b6000806000806000806000806000806000806101808d8f03121561487057898afd5b8c359b5060208d01359a5060408d0135995060608d0135985060808d01356148978161520e565b975060a08d01356148a78161520e565b965060c08d01356148b78161520e565b955060e08d013594506101008d01356148cf8161520e565b93506101208d013592506101408d013591506101608d01356148f08161520e565b809150509295989b509295989b509295989b565b6001600160a01b0391909116815260200190565b6001600160a01b03841681526060810161493184615204565b602082019390935260400152919050565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b90815260200190565b918252602082015260400190565b6080810161499286615204565b94815292516020840152905160408301525160609091015290565b606081016149ba85615204565b938152602081019290925260409091015290565b606081016149db85615204565b9381526001600160a01b0392909216602083015260409091015290565b6000602080835283518082850152825b81811015614a2457858101830151858201604001528201614a08565b81811115614a355783604083870101525b50601f01601f1916929092016040019392505050565b6020808252601590820152746d756c7469706c6965722063616e2774206265203160581b604082015260600190565b60208082526010908201526f34b63632b3b0b61037b832b930ba37b960811b604082015260600190565b6020808252601490820152733737ba1039bab83837b93a32b21037b83a34b7b760611b604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526019908201527f63616e206e6f742073657420696e74657276616c20746f203000000000000000604082015260600190565b6020808252601a908201527f63616c6c6572206973206e6f7420636f756e7465725061727479000000000000604082015260600190565b60208082526021908201527f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252601290820152711bdd995c881d1c98591a5b99c81b1a5b5a5d60721b604082015260600190565b6020808252601c908201527f696c6c6567616c206c6971756964697479206d756c7469706c69657200000000604082015260600190565b6020808252600f908201526e0d2dcc6dee4e4cac6e840d2dcc8caf608b1b604082015260600190565b6020808252601590820152740756e6465726c79696e67207072696365206973203605c1b604082015260600190565b6020808252600f908201526e696e76616c6964206164647265737360881b604082015260600190565b6020808252601a908201527f6f7261636c652070726963652063616e2774206265207a65726f000000000000604082015260600190565b6020808252601f908201527f7072696365206973206f76657220666c756374756174696f6e206c696d697400604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60208082526021908201527f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526014908201527331b0b63632b91034b9903737ba1036b4b73a32b960611b604082015260600190565b6020808252601d908201527f4c657373207468616e206d696e696d616c2071756f746520746f6b656e000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252601c908201527f4c657373207468616e206d696e696d616c206261736520746f6b656e00000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526027908201527f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f604082015266766572666c6f7760c81b606082015260800190565b6020808252600d908201526c696e76616c696420726174696f60981b604082015260600190565b602080825260139082015272195e18da185b99d9481dd85cc818db1bdcd959606a1b604082015260600190565b6020808252601d908201527f4d6f7265207468616e206d6178696d616c2071756f746520746f6b656e000000604082015260600190565b6020808252601c908201527f4d6f7265207468616e206d6178696d616c206261736520746f6b656e00000000604082015260600190565b60208082526018908201527f736574746c652066756e64696e6720746f6f206561726c790000000000000000604082015260600190565b602080825260169082015275696c6c6567616c2070726963652066656564206b657960501b604082015260600190565b6020808252600d908201526c1a5b9d985b1a59081a5b9c1d5d609a1b604082015260600190565b60208082526024908201527f5369676e6564536166654d6174683a207375627472616374696f6e206f766572604082015263666c6f7760e01b606082015260800190565b602080825260149082015273696e76616c6964206f7261636c6520707269636560601b604082015260600190565b6020808252818101527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604082015260600190565b815151815260208083015151908201526040808301515190820152606091820151519181019190915260800190565b9035815260200190565b9051815260200190565b9151825251602082015260400190565b93518452915160208401526040830152606082015260800190565b9283526020830191909152604082015260600190565b9586526020860194909452604085019290925260608401526080830152151560a082015260c00190565b60405181810167ffffffffffffffff811182821017156151fc57600080fd5b604052919050565b600281106113cf57fe5b6001600160a01b03811681146113cf57600080fd5b80151581146113cf57600080fd5b600281106113cf57600080fdfe4d69786564446563696d616c3a2075696e742076616c756520697320626967676572207468616e205f494e543235365f4d4158a2646970667358221220f2c6f975eb6b89bd4083300e1889bc024bd1db83ce24267008b9c924726eaa5964736f6c634300060c0033

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