Contract 0x905f36be526a0b09fa05948225d142b89aebed0f

Contract Overview

Balance:
0 BNB
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x22e2f6adce75a11ffe04f5f8e3c233fe1a6cd00e0d6e57ad91733545f8118d4c0x6080604080223652021-04-16 7:14:27154 days 12 mins ago0x20000b9b01e93a39db9d286e9264eff7f2af16e9 IN  Contract Creation0 BNB0.04547844
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0xd83aba8fa4361fe72d04736fc2918e63d5e69280297d86e9d4695fab1d6853a783976932021-04-29 8:01:10140 days 23 hrs ago 0xbdcac02c00f7412959724d19490ecbe22e515728 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0x70ee148651aee92bd4a5fc4691f2b8624c127ad58ac96775c96dde9f73245fe783976852021-04-29 8:00:46140 days 23 hrs ago 0xce41a9f4b2b0327e1a7df4e2437157362efa2eba 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0x70ee148651aee92bd4a5fc4691f2b8624c127ad58ac96775c96dde9f73245fe783976852021-04-29 8:00:46140 days 23 hrs ago 0xce41a9f4b2b0327e1a7df4e2437157362efa2eba 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0x70ee148651aee92bd4a5fc4691f2b8624c127ad58ac96775c96dde9f73245fe783976852021-04-29 8:00:46140 days 23 hrs ago 0xce41a9f4b2b0327e1a7df4e2437157362efa2eba 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0x70ee148651aee92bd4a5fc4691f2b8624c127ad58ac96775c96dde9f73245fe783976852021-04-29 8:00:46140 days 23 hrs ago 0xce41a9f4b2b0327e1a7df4e2437157362efa2eba 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
0x70ee148651aee92bd4a5fc4691f2b8624c127ad58ac96775c96dde9f73245fe783976852021-04-29 8:00:46140 days 23 hrs ago 0xce41a9f4b2b0327e1a7df4e2437157362efa2eba 0x905f36be526a0b09fa05948225d142b89aebed0f0 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

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 26 : 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 public override 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.setHighRiskLiquidityWeight(address(this), 400);
        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 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;
    }

    /**
     * @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 26 : 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";

contract MMLPToken is ERC20Upgradeable, OwnableUpgradeable {
    constructor(string memory _name, string memory _symbol) public {
        __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);
    }
}

File 3 of 26 : 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";
import "./IExchangeState.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 exchangeState() external view returns (IExchangeState);
}

File 4 of 26 : 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 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 26 : 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 6 of 26 : 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 7 of 26 : 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 setHighRiskLiquidityWeight(address _exchange, uint256 _weight) external;

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

File 8 of 26 : 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 9 of 26 : 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}

    /// @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 10 of 26 : 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 11 of 26 : 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 12 of 26 : 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 13 of 26 : 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 14 of 26 : 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 15 of 26 : 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 16 of 26 : 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 17 of 26 : 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 18 of 26 : 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 19 of 26 : 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 20 of 26 : 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 21 of 26 : 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 22 of 26 : 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 23 of 26 : 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 24 of 26 : 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 25 of 26 : 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 26 of 26 : 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":"exchangeState","outputs":[{"internalType":"contract IExchangeState","name":"","type":"address"}],"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":[{"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":"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"}]

608060405234801561001057600080fd5b50615144806100206000396000f3fe608060405234801561001057600080fd5b50600436106104335760003560e01c80637748102411610236578063dc3184a01161013b578063eebab8ef116100c3578063fc0e74d111610087578063fc0e74d11461080a578063fca3b5aa14610812578063fcfff16f14610825578063fdf262b71461083a578063ffbf688e1461084257610433565b8063eebab8ef146107b6578063eebd72e0146107c9578063f1b5df86146107dc578063f1e28650146107ef578063f2fde38b146107f757610433565b8063e4bc2eb91161010a578063e4bc2eb91461078e578063e57d563614610796578063ec2c0e631461079e578063ed83d79c146107a6578063eeb016f2146107ae57610433565b8063dc3184a014610758578063dc76fabc1461076b578063e0037a6c14610773578063e1f1027f1461077b57610433565b8063a8f8be4e116101be578063c2de442f1161018d578063c2de442f1461071a578063c9566fcc14610722578063cfe711031461072a578063d835e52314610732578063da0c59271461074557610433565b8063a8f8be4e146106d9578063ae09a457146106ec578063ba0c4763146106f4578063bea69f741461070757610433565b80638eb86e8b116102055780638eb86e8b146106855780638f40d932146106985780639bf5d1d4146106a05780639ece77c8146106b35780639ffca62f146106c657610433565b8063774810241461065a57806383acb48a1461066d5780638da5cb5b146106755780638e4833281461067d57610433565b80634cb876f21161033c57806362e7a176116102c4578063715018a611610293578063715018a61461061c5780637155c36114610624578063724e78da14610637578063741bef1a1461064a57806374d7c62b1461065257610433565b806362e7a176146105db5780636fa42ede146105ee5780636fdca5e0146106015780636febdd501461061457610433565b80635834db901161030b5780635834db901461058f57806358a4c3dc146105a257806359bf5d39146105aa5780635f1ba1fd146105c057806362267955146105c857610433565b80634cb876f21461056457806350799c811461056c578063517587c61461057f578063525454101461058757610433565b806329f9b17b116103bf57806341d3c84c1161038e57806341d3c84c14610531578063429f24bc1461053957806342b3198b14610541578063468f02d2146105495780634894d1831461055157610433565b806329f9b17b146104ee57806334953249146104f657806340098b751461050957806340d71cd91461051e57610433565b80631b584d6c116104065780631b584d6c146104885780631d3acb441461049d57806320855f05146104a557806321e00985146104b8578063237f17ee146104db57610433565b80630d451c8f146104385780630dd27120146104565780630f999b151461046b5780631137739414610473575b600080fd5b61044061084a565b60405161044d919061483a565b60405180910390f35b61045e610850565b60405161044d91906147d8565b61044061085f565b61047b610865565b60405161044d9190615005565b6104906108f8565b60405161044d9190614fcc565b610440610980565b61047b6104b3366004614679565b610986565b6104cb6104c63660046146d1565b610a65565b60405161044d949392919061501f565b6104906104e93660046146d1565b610ab3565b61047b610b5c565b61047b6105043660046146d1565b610b78565b61051c61051736600461444f565b610c17565b005b61047b61052c366004614695565b610c94565b610440610d45565b610440610d4b565b610440610d51565b61047b610d57565b61047b61055f366004614526565b610df4565b610440610e0b565b61047b61057a36600461455b565b610e11565b61045e610eaa565b61047b610eb9565b61051c61059d36600461460e565b610f0f565b6104406111e3565b6105b26111e9565b60405161044d92919061500f565b61044061121b565b61047b6105d63660046145f3565b611221565b61051c6105e93660046146d1565b6112a9565b61047b6105fc366004614526565b611300565b61051c61060f36600461446b565b611317565b61047b6113bf565b61051c611415565b61051c61063236600461444f565b611494565b61051c61064536600461444f565b611511565b61045e61158e565b61044061159d565b61051c610668366004614639565b6115a3565b61047b611613565b61045e611669565b61045e611678565b61051c6106933660046145b3565b611687565b610440611727565b61047b6106ae36600461455b565b61172d565b61051c6106c136600461444f565b61176b565b61047b6106d4366004614487565b6117c2565b61051c6106e7366004614639565b6119f1565b61045e611a2c565b61051c610702366004614679565b611a3b565b61051c610715366004614722565b611a9e565b610440611f8a565b610440611f90565b61047b611f96565b61051c610740366004614639565b611fec565b61047b610753366004614526565b612027565b61047b6107663660046144cd565b612059565b61047b6120e7565b610440612116565b61047b610789366004614526565b61211c565b61044061214e565b61047b612154565b61047b6121aa565b61047b6121c6565b61047b612342565b61051c6107c43660046145b3565b612398565b61051c6107d7366004614701565b61242a565b61047b6107ea3660046146d1565b612975565b610440612986565b61051c61080536600461444f565b61298c565b61051c612a43565b61051c61082036600461444f565b612c17565b61082d612c6e565b60405161044d919061482f565b61045e612c7e565b61047b612c8d565b60a75490565b60af546001600160a01b031681565b60ad5481565b61086d614347565b60af60009054906101000a90046001600160a01b03166001600160a01b031663113773946040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b505afa1580156108cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f39190614654565b905090565b61090061435a565b60a1805461090f906001612ca9565b8154811061091957fe5b60009182526020918290206040805160a0810182526004909302909101805460808401908152835281518085018352600182015481528385015281518085018352600282015481528383015281519384019091526003015482526060810191909152905090565b609f5481565b61098e614347565b610996614347565b604080516020810190915260975481526109af90612ceb565b156109eb57604080516020810190915260975481526109e4906109df906001906109d890612d09565b868861172d565b612d37565b9050610a28565b610a25600019610a1f6109df6000610a186097604051806020016040529081600082015481525050612d09565b888a61172d565b90612d9b565b90505b6040805160208082018352609a54825282519081019092526099548252610a5b918391610a559190612dc0565b90612ddd565b9150505b92915050565b60a78181548110610a7257fe5b600091825260209182902060408051808501825260049093029091018054835281519384019091526001810154835260028101546003909101549193509084565b610abb61435a565b60a1548210610ae55760405162461bcd60e51b8152600401610adc90614b2d565b60405180910390fd5b60a18281548110610af257fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018201548152838501528151808501835260028201548152838301528151938401909152600301548252606081019190915290505b919050565b610b64614347565b506040805160208101909152609954815290565b610b80614347565b60408051602081019182905260aa5460a654632885cf7d60e21b909352909182916001600160a01b03169063a2173df490610bbf908760248601614843565b60206040518083038186803b158015610bd757600080fd5b505afa158015610beb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0f91906146e9565b905292915050565b610c1f612dfa565b6033546001600160a01b03908116911614610c4c5760405162461bcd60e51b8152600401610adc90614d59565b6001600160a01b038116610c725760405162461bcd60e51b8152600401610adc90614b56565b60ae80546001600160a01b0319166001600160a01b0392909216919091179055565b610c9c614347565b610ca584612dfe565b610cb0575082610d3e565b600080610cbc86612e10565b13610cc8576000610ccb565b60015b9050610cd5614347565b610cf782610ce4576001610ce7565b60005b610cf088612d09565b878761172d565b9050610d01614347565b610d1c6109df84610d13576000610d16565b60015b8461211c565b9050610d3883610d2e57600019610d31565b60015b8290612d9b565b93505050505b9392505050565b60985481565b60ac5481565b60a15490565b610d5f614347565b60408051602081019182905260aa5460a6546331d98b3f60e01b909352909182916001600160a01b0316906331d98b3f90610d9d906024850161483a565b60206040518083038186803b158015610db557600080fd5b505afa158015610dc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ded91906146e9565b9052905090565b610dfc614347565b610d3e83836000610384612e14565b60975481565b610e19614347565b60af546040516350799c8160e01b81526001600160a01b03909116906350799c8190610e4f908890889088908890600401614851565b60206040518083038186803b158015610e6757600080fd5b505afa158015610e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9f9190614654565b90505b949350505050565b60a8546001600160a01b031681565b610ec1614347565b60af60009054906101000a90046001600160a01b03166001600160a01b031663525454106040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b610f17612dfa565b6033546001600160a01b03908116911614610f445760405162461bcd60e51b8152600401610adc90614d59565b610f54610f4f612e9a565b612e10565b610f66610f4f36859003850185614639565b1415610f845760405162461bcd60e51b8152600401610adc90614917565b60408051602081019091526097548152610fac90610fa736859003850185614639565b612eb7565b610fc3610fbe36839003830183614639565b612f3d565b610fcb614347565b506040805160208101909152609d548152610fe4614347565b506040805160208101909152609e548152610ffd614347565b506040805160208101909152609754815261102761102036879003870187614639565b849061308d565b51609d5561104461103d36879003870187614639565b839061308d565b51609e55611053818484610c94565b5160978190556040805160a081018252609954608082019081528152815160208082018452609d8054835281840192835284518083018652609e548152848601908152855180840187529687526060850196875260a180546001810182556000919091529451517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f6498786004909602958601559251517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f6498798501559151517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f64987a8401559351517faadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f64987b9092019190915581519283019091525481527feba648fc83514e32ba75e27696ee5fa3610ebc7a77460a3ac3e8724ffa1be4cc9061119390612e10565b6040805160208101909152609e5481526111ac90612e10565b604080516020810190915260995481526111c590612e10565b6040516111d49392919061503a565b60405180910390a15050505050565b60a65481565b6111f1614347565b6111f9614347565b50506040805160208082018352609d5482528251908101909252609e54825291565b60a25481565b611229614347565b60af54604051636226795560e01b81526001600160a01b0390911690636226795590611259908590600401614ffb565b60206040518083038186803b15801561127157600080fd5b505afa158015611285573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5f9190614654565b6112b1612dfa565b6033546001600160a01b039081169116146112de5760405162461bcd60e51b8152600401610adc90614d59565b806112fb5760405162461bcd60e51b8152600401610adc90614a1b565b60a255565b611308614347565b610d3e83836001610384612e14565b61131f612dfa565b6033546001600160a01b0390811691161461134c5760405162461bcd60e51b8152600401610adc90614d59565b60ab5460ff600160a01b909104161515811515141561136a576113bc565b60ab805482158015600160a01b0260ff60a01b19909216919091179091556113bc576113b8610e106113b2610e106113ac60a3546113a66130aa565b906130ae565b906130d3565b90613115565b60a5555b50565b6113c7614347565b60af60009054906101000a90046001600160a01b03166001600160a01b0316636febdd506040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b61141d612dfa565b6033546001600160a01b0390811691161461144a5760405162461bcd60e51b8152600401610adc90614d59565b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b61149c612dfa565b6033546001600160a01b039081169116146114c95760405162461bcd60e51b8152600401610adc90614d59565b6001600160a01b0381166114ef5760405162461bcd60e51b8152600401610adc90614b56565b60af80546001600160a01b0319166001600160a01b0392909216919091179055565b611519612dfa565b6033546001600160a01b039081169116146115465760405162461bcd60e51b8152600401610adc90614d59565b6001600160a01b03811661156c5760405162461bcd60e51b8152600401610adc90614b56565b60aa80546001600160a01b0319166001600160a01b0392909216919091179055565b60aa546001600160a01b031681565b60a35481565b6115ab612dfa565b6033546001600160a01b039081169116146115d85760405162461bcd60e51b8152600401610adc90614d59565b60006115ec6115e5612e9a565b839061314f565b60000b131561160d5760405162461bcd60e51b8152600401610adc90614dd5565b5160a055565b61161b614347565b60af60009054906101000a90046001600160a01b03166001600160a01b03166383acb48a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b6033546001600160a01b031690565b60ab546001600160a01b031681565b61168f612dfa565b60a8546001600160a01b039081169116146116bc5760405162461bcd60e51b8152600401610adc90614c7c565b60af54604051638eb86e8b60e01b81526001600160a01b0390911690638eb86e8b906116f09086908690869060040161489a565b600060405180830381600087803b15801561170a57600080fd5b505af115801561171e573d6000803e3d6000fd5b50505050505050565b609c5481565b611735614347565b60af546040516326fd747560e21b81526001600160a01b0390911690639bf5d1d490610e4f908890889088908890600401614851565b611773612dfa565b6033546001600160a01b039081169116146117a05760405162461bcd60e51b8152600401610adc90614d59565b60ab80546001600160a01b0319166001600160a01b0392909216919091179055565b6117ca614347565b60ab54600160a01b900460ff166117f35760405162461bcd60e51b8152600401610adc90614dfc565b6117fb612dfa565b60ab546001600160a01b039081169116146118285760405162461bcd60e51b8152600401610adc90614a52565b61183a610f4f36859003850185614639565b61184d57611846613180565b9050610d3e565b600184600181111561185b57fe5b14156118bb57611873610f4f36859003850185614639565b6040805160208082018352609c5482528251908101909252609d54825261189d91610f4f9161308d565b10156118bb5760405162461bcd60e51b8152600401610adc90614aca565b6118c3614347565b6118d68561078936879003870187614639565b90506118ea610f4f36859003850185614639565b1561197a5760008560018111156118fd57fe5b141561194157611915610f4f36859003850185614639565b61191e82612e10565b101561193c5760405162461bcd60e51b8152600401610adc90614d22565b61197a565b611953610f4f36859003850185614639565b61195c82612e10565b111561197a5760405162461bcd60e51b8152600401610adc90614e60565b6119958561198d36879003870187614639565b83600061319b565b7fae6a2b946841d9afc0e1e19a94ae4af26f01125b87b5095bbfb177a9741a2ede856119c9610f4f36889003880188614639565b6119d284612e10565b6040516119e193929190614879565b60405180910390a1949350505050565b6119f9612dfa565b6033546001600160a01b03908116911614611a265760405162461bcd60e51b8152600401610adc90614d59565b51609f55565b60ae546001600160a01b031681565b611a43612dfa565b60ab546001600160a01b03908116911614611a705760405162461bcd60e51b8152600401610adc90614a52565b60408051602081019091526097548152611a96908290611a909085612ddd565b90612dc0565b516097555050565b600054610100900460ff1680611ab75750611ab76132d1565b80611ac5575060005460ff16155b611ae15760405162461bcd60e51b8152600401610adc90614bed565b600054610100900460ff16158015611b0c576000805460ff1961ff0019909116610100171660011790555b8c15801590611b1a57508a15155b8015611b2557508b15155b8015611b3057508915155b8015611b4457506001600160a01b03891615155b8015611b5857506001600160a01b03881615155b8015611b6c57506001600160a01b03871615155b8015611b8057506001600160a01b03821615155b8015611b9457506001600160a01b03851615155b611bb05760405162461bcd60e51b8152600401610adc90614efe565b611bb86132d7565b6040805160208082018352908f9052609d8f9055815180820183528e9052609e8e9055815180820183528d9052609c8d90558151808201835285905260a08590558151908101909152849052609f84905560a38a9055611c198a60026130d3565b60a455610e1060a25560a686905560a980546001600160a01b03199081166001600160a01b038881169190911790925560aa805482168c841617905560ab805482168b841617905560a8805482168a841617905560af80549091169184169190911790556040805160208101909152670429d069189e00009081905260ad55611ca0612dfa565b60ae80546001600160a01b0319166001600160a01b0392831617905560a85460405163025c4d3560e51b8152911690634b89a6a090611ce790309061019090600401614816565b600060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505060a8546040516386be9e8b60e01b81526001600160a01b0390911692506386be9e8b9150611d4f9030906000906032906004016147ec565b600060405180830381600087803b158015611d6957600080fd5b505af1158015611d7d573d6000803e3d6000fd5b505060a8546040516386be9e8b60e01b81526001600160a01b0390911692506386be9e8b9150611db79030906001906019906004016147ec565b600060405180830381600087803b158015611dd157600080fd5b505af1158015611de5573d6000803e3d6000fd5b5050505060a16040518060800160405280611dfe613180565b81526040805160208181018352609d5482528084019190915281519081018252609e54815290820152606001611e32613180565b9052815460018181018455600093845260209384902083515160049093020191825583830151519082015560408083015151600283015560609092015151600390910155805160a081018252609d5460808201908152815281518084018352609e5481529281019290925260a791908101611eab6130aa565b8152602001611eb8613369565b90528154600181810184556000938452602093849020835151600490930201918255838301515190820155604080830151600283015560609092015160039091015580519182019052609d5481527f3a3348362552c3897fd1f06a3233519ebd8bd76ad6e99a418a9741155fe9051590611f3190612e10565b6040805160208101909152609e548152611f4a90612e10565b611f526130aa565b604051611f619392919061503a565b60405180910390a18015611f7b576000805461ff00191690555b50505050505050505050505050565b60a45481565b609e5481565b611f9e614347565b60af60009054906101000a90046001600160a01b03166001600160a01b031663cfe711036040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b611ff4612dfa565b6033546001600160a01b039081169116146120215760405162461bcd60e51b8152600401610adc90614d59565b5160ad55565b61202f614347565b6040805160208082018352609d5482528251908101909252609e548252610d3e918591859161172d565b612061614347565b60ab54600160a01b900460ff1661208a5760405162461bcd60e51b8152600401610adc90614dfc565b612092612dfa565b60ab546001600160a01b039081169116146120bf5760405162461bcd60e51b8152600401610adc90614a52565b610e9f856120d236879003870187614639565b6120e136879003870187614639565b8561336d565b6120ef614347565b6040805160208082018352609e5482528251908101909252609d5482526108f39190613520565b60a55481565b612124614347565b6040805160208082018352609d5482528251908101909252609e548252610d3e9185918591610e11565b609d5481565b61215c614347565b60af60009054906101000a90046001600160a01b03166001600160a01b031663e57d56366040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b6121b2614347565b506040805160208101909152609b54815290565b6121ce614347565b60ab54600160a01b900460ff166121f75760405162461bcd60e51b8152600401610adc90614dfc565b6121ff612dfa565b60ab546001600160a01b0390811691161461222c5760405162461bcd60e51b8152600401610adc90614a52565b60a5546122376130aa565b10156122555760405162461bcd60e51b8152600401610adc90614e97565b61225d614347565b612265613180565b905061226f614347565b61227a60a254610b78565b905060a55461229660a35460ac546130ae90919063ffffffff16565b10156122e4576122a4614347565b6122bc826122b66109df60a254612975565b9061353d565b90506122e0620151806122da60a354846135ac90919063ffffffff16565b90613609565b9250505b6122ee8282613625565b60006122fe60a4546113a66130aa565b90506000612325610e106113b2610e106113ac60a35460a5546130ae90919063ffffffff16565b90508181116123345781612336565b805b60a55550919250505090565b61234a614347565b60af60009054906101000a90046001600160a01b03166001600160a01b031663eeb016f26040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bb57600080fd5b60ab54600160a01b900460ff166123c15760405162461bcd60e51b8152600401610adc90614dfc565b6123c9612dfa565b60a8546001600160a01b039081169116146123f65760405162461bcd60e51b8152600401610adc90614c7c565b60af5460405163eebab8ef60e01b81526001600160a01b039091169063eebab8ef906116f09086908690869060040161489a565b612432612dfa565b60ae546001600160a01b03908116911614806124635750612451612dfa565b60aa546001600160a01b039081169116145b61247f5760405162461bcd60e51b8152600401610adc90614946565b6000821161249f5760405162461bcd60e51b8152600401610adc90614b7f565b8060a654146124c05760405162461bcd60e51b8152600401610adc90614ece565b60ab54600160a01b900460ff1615806124ef5750604080516020810190915260a05481526124ed90612e10565b155b156124f957612971565b612501614347565b506040805160208101909152828152612518614347565b6040805160208082018352609e5482528251908101909252609d54825261253f9190613520565b604080516020810190915260ad54815290915060009061257e90612563908461308d565b612578612573856122b688612d37565b612d09565b9061314f565b60000b131561259f5760405162461bcd60e51b8152600401610adc90614f69565b6125a7614347565b604080516020810190915260a05481526125df90612573906125d6906125d0866122b689612d37565b90613691565b610a5585612d37565b90506125e9614347565b6040805160208082018352609e5482528251908101909252609d548252612610919061308d565b9050600061262f612622610f4f612e9a565b6113b2610f4f8587613520565b9050612639614347565b604051806020016040528061264d84613705565b90529050612659614347565b6126638483613520565b905061266d614347565b6126778383610986565b9050612681614347565b60a85460405163180c91f160e01b81526001600160a01b039091169063180c91f1906126b19030906004016147d8565b60206040518083038186803b1580156126c957600080fd5b505afa1580156126dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127019190614654565b905061270b614347565b60a85460405163633ceadb60e01b81526001600160a01b039091169063633ceadb9061273b9030906004016147d8565b60206040518083038186803b15801561275357600080fd5b505afa158015612767573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061278b9190614654565b90506127a96127a48261279e8686612ddd565b9061375f565b612ceb565b15612815577f2ad23a1b94bd43bf2d22d82564f2f357d4428e8146810a57fbaef7b88ddc6fba6127d88a612e10565b8d6127e28b612e10565b6127eb86612e10565b6127f488612e10565b600060405161280896959493929190615050565b60405180910390a1612966565b61281d614347565b6040805160208101909152609d54815261283a906122b687612d37565b6040805160208101909152609a5481529091506128579082612ddd565b51609a55604080516020810190915260995481526128759082612ddd565b516099558551609e558451609d5561288b6130aa565b60ac556128966137c4565b60a860009054906101000a90046001600160a01b03166001600160a01b0316638df3fab36040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156128e657600080fd5b505af11580156128fa573d6000803e3d6000fd5b505050507f2ad23a1b94bd43bf2d22d82564f2f357d4428e8146810a57fbaef7b88ddc6fba6129288b612e10565b8e6129328c612e10565b61293f610f4f888861375f565b61294889612e10565b600160405161295c96959493929190615050565b60405180910390a1505b505050505050505050505b5050565b61297d614347565b610a5f826138fc565b60a05481565b612994612dfa565b6033546001600160a01b039081169116146129c15760405162461bcd60e51b8152600401610adc90614d59565b6001600160a01b0381166129e75760405162461bcd60e51b8152600401610adc9061499e565b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b612a4b612dfa565b6033546001600160a01b03908116911614612a785760405162461bcd60e51b8152600401610adc90614d59565b60a860009054906101000a90046001600160a01b03166001600160a01b0316638df3fab36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612ac857600080fd5b505af1158015612adc573d6000803e3d6000fd5b50505050612ae861435a565b612af06108f8565b9050612afa614347565b60208201516040830151612b0d9161308d565b9050612b17614347565b60408301516060840151612b2a9161375f565b9050612b34614347565b612b4782612b4185612d37565b9061392d565b9050612b51614347565b6040805160208101909152609d548152612b6c90839061353d565b60408051602081019091526097548152909150606490612b8b90612dfe565b1115612bc25760408051602081019091526097548152612bbd90612bae90612d09565b612bb783612d09565b90613520565b51609b555b60ab805460ff60a01b191690556040805160208101909152609b5481527f574214b195bf5273a95bb4498e35cf1fde0ce327c727a95ec2ab359f7ba4e11a90612c0a90612e10565b6040516111d4919061483a565b612c1f612dfa565b6033546001600160a01b03908116911614612c4c5760405162461bcd60e51b8152600401610adc90614d59565b60a880546001600160a01b0319166001600160a01b0392909216919091179055565b60ab54600160a01b900460ff1681565b60a9546001600160a01b031681565b612c95614347565b506040805160208101909152609754815290565b6000610d3e83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061394a565b60008082600001511215612d0157506001610b57565b506000919050565b612d11614347565b612d19614347565b825160001315612d2f5782516000038152610a5f565b915182525090565b612d3f614347565b8180600001516001600160ff1b0310156040518060600160405280603381526020016150dc6033913990612d865760405162461bcd60e51b8152600401610adc91906148c4565b50506040805160208101909152915182525090565b612da3614347565b612dab614347565b8351612db79084613976565b81529392505050565b612dc8614347565b612dd0614347565b82518451612db7916139e1565b612de5614347565b612ded614347565b82518451612db791613a27565b3390565b6000612e0982612d09565b5192915050565b5190565b612e1c614347565b612e24614399565b600180825260a754612e3591612ca9565b60208201526040810151866001811115612e4b57fe5b90816001811115612e5857fe5b905250604080820180516020018790525101846001811115612e7657fe5b90816001811115612e8357fe5b905250612e908184613a6d565b9695505050505050565b612ea2614347565b6040518060200160405280610ded6012613c98565b6000612ec283612e10565b131561297157612ed0614347565b6040805160208082018352609e5482528251908101909252633b9aca008252612f09916125739190612f0390879061375f565b90613ca1565b90506000612f17838361314f565b60000b1215612f385760405162461bcd60e51b8152600401610adc90614af6565b505050565b6000612f4882612e10565b11156113bc5760a754612f596143b8565b60a76001830381548110612f6957fe5b60009182526020918290206040805160a0810182526004909302909101805460808401908152835281518085018352600182015481529383019390935260028301549082015260039091015460608201529050612fc4613369565b8160600151148015612fd65750600182115b1561303f5760a76002830381548110612feb57fe5b60009182526020918290206040805160a08101825260049093029091018054608084019081528352815180850183526001820154815293830193909352600283015490820152600390910154606082015290505b6040805160208082018352609e5482528251908101909252609d5482526130709161306991613520565b8483613d0c565b15612f385760405162461bcd60e51b8152600401610adc90614bb6565b613095614347565b61309d614347565b82518451612db791613dae565b4290565b600082820183811015610d3e5760405162461bcd60e51b8152600401610adc906149e4565b6000610d3e83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613dbc565b60008261312457506000610a5f565b8282028284828161313157fe5b0414610d3e5760405162461bcd60e51b8152600401610adc90614ce1565b80518251600091101561316457506001610a5f565b8151835110156131775750600019610a5f565b50600092915050565b613188614347565b5060408051602081019091526000815290565b60008460018111156131a957fe5b141561322c576040805160208101909152609d5481526131c99084613df3565b51609d556040805160208101909152609e5481526131e79083613e10565b51609e5560408051602081019091526097548152613205908361375f565b5160975560408051602081019091526099548152613223908461375f565b516099556132a5565b6040805160208101909152609d5481526132469084613e10565b51609d556040805160208101909152609e5481526132649083613df3565b51609e5560408051602081019091526097548152613282908361353d565b51609755604080516020810190915260995481526132a0908461353d565b516099555b806132c3576040805160208101909152609f5481526132c390612f3d565b6132cb6137c4565b50505050565b303b1590565b600054610100900460ff16806132f057506132f06132d1565b806132fe575060005460ff16155b61331a5760405162461bcd60e51b8152600401610adc90614bed565b600054610100900460ff16158015613345576000805460ff1961ff0019909116610100171660011790555b61334d613e2d565b613355613eae565b80156113bc576000805461ff001916905550565b4390565b613375614347565b61337e84612e10565b6133915761338a613180565b9050610ea2565b600185600181111561339f57fe5b14156133f6576133ae84612e10565b6040805160208082018352609c5482528251908101909252609e5482526133d891610f4f9161308d565b10156133f65760405162461bcd60e51b8152600401610adc90614aca565b6133fe614347565b6134088686612027565b905061341384612e10565b1561349157600086600181111561342657fe5b14156134615761343584612e10565b61343e82612e10565b101561345c5760405162461bcd60e51b8152600401610adc90614caa565b613491565b61346a84612e10565b61347382612e10565b11156134915760405162461bcd60e51b8152600401610adc90614e29565b826134a4576134a1868287613f88565b92505b6134cc60008760018111156134b557fe5b146134c15760006134c4565b60015b82878661319b565b7f0dd4066b1a6ce97fb670c3e4201e908c644193f38cbdaffd0229d7e26da3e533866134f783612e10565b61350088612e10565b60405161350f93929190614879565b60405180910390a195945050505050565b613528614347565b613530614347565b82518451612db7916140a4565b613545614347565b8180600001516001600160ff1b0310156040518060600160405280603381526020016150dc603391399061358c5760405162461bcd60e51b8152600401610adc91906148c4565b50613595614347565b835185516135a2916139e1565b8152949350505050565b6135b4614347565b816001600160ff1b0310156040518060600160405280603381526020016150dc60339139906135f65760405162461bcd60e51b8152600401610adc91906148c4565b506135ff614347565b610a5b8484612d9b565b613611614347565b613619614347565b8351612db790846140b2565b61362f8282613ca1565b51609881905560408051602081019091529081527fd2805fe76d30598332a67c1061cee82e2e102b0f59f5457b1729bce028a054a09061366e90612e10565b61367783612e10565b604051613685929190614843565b60405180910390a15050565b613699614347565b8180600001516001600160ff1b0310156040518060600160405280603381526020016150dc60339139906136e05760405162461bcd60e51b8152600401610adc91906148c4565b506136e9614347565b6136fc6136f585612d37565b8690614116565b95945050505050565b600060038211156137495781600160028204015b818110156137415780915060028182868161373057fe5b04018161373957fe5b049050613719565b509050610b57565b811561375757506001610b57565b506000610b57565b613767614347565b8180600001516001600160ff1b0310156040518060600160405280603381526020016150dc60339139906137ae5760405162461bcd60e51b8152600401610adc91906148c4565b506137b7614347565b835185516135a291613a27565b60006137ce613369565b60a7805491925060009160001981019081106137e657fe5b90600052602060002090600402019050806003015482141561381457609d548155609e546001820155613891565b6040805160a081018252609d54608082019081528152815160208181018452609e54825282015260a79181016138486130aa565b8152602090810185905282546001818101855560009485529382902083515160049092020190815590820151519281019290925560408101516002830155606001516003909101555b6040805160208101909152609d5481527f3a3348362552c3897fd1f06a3233519ebd8bd76ad6e99a418a9741155fe90515906138cc90612e10565b6040805160208101909152609e5481526138e590612e10565b6138ed6130aa565b6040516136859392919061503a565b613904614347565b61390c614399565b6000815260a75461391e906001612ca9565b6020820152610d3e8184613a6d565b613935614347565b61393d614347565b82518451612db791614133565b6000818484111561396e5760405162461bcd60e51b8152600401610adc91906148c4565b505050900390565b60008261398557506000610a5f565b826000191480156139995750600160ff1b82145b156139b65760405162461bcd60e51b8152600401610adc90614d8e565b828202828482816139c357fe5b0514610d3e5760405162461bcd60e51b8152600401610adc90614d8e565b60008183038183128015906139f65750838113155b80613a0b5750600083128015613a0b57508381135b610d3e5760405162461bcd60e51b8152600401610adc90614f25565b6000828201818312801590613a3c5750838112155b80613a515750600083128015613a5157508381125b610d3e5760405162461bcd60e51b8152600401610adc90614a89565b613a75614347565b613a7d614347565b613a8684614141565b905082613a94579050610a5f565b6000613aa884613aa26130aa565b90612ca9565b9050613ab26143b8565b60a7866020015181548110613ac357fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018083015482529484015260028101549183019190915260030154606082015260a7549092501480613b29575081816040015111155b15613b3957829350505050610a5f565b60408101516000613b4c82613aa26130aa565b9050613b56614347565b613b608683614299565b90505b6020890151613b8357613b7681836142b5565b9650505050505050610a5f565b6020890151613b93906001612ca9565b60208a0181905260a780549091908110613ba957fe5b60009182526020918290206040805160a0810182526004909302909101805460808401908152835281518085018352600182015481529383019390935260028301549082015260039091015460608201529350613c0589614141565b955084846040015111613c3757613c30613c29613c228588612ca9565b8890614299565b8290613df3565b9050613c81565b6000613c50856040015185612ca990919063ffffffff16565b9050613c66613c5f8883614299565b8390613df3565b9150613c7283826130ae565b92508460400151935050613b63565b613c8b81896142b5565b9998505050505050505050565b60ff16600a0a90565b613ca9614347565b8180600001516001600160ff1b0310156040518060600160405280603381526020016150dc6033913990613cf05760405162461bcd60e51b8152600401610adc91906148c4565b50613cf9614347565b6136fc613d0585612d37565b869061392d565b6000613d16614347565b60208301518351613d2691613520565b9050613d30614347565b613d4561103d86613d3f612e9a565b90613df3565b9050613d4f614347565b613d6461102087613d5e612e9a565b90613e10565b90506000613d72888461314f565b60000b13158015613d9057506000613d8a888361314f565b60000b12155b15613da15760009350505050610d3e565b5060019695505050505050565b6000610d3e838360126142d1565b60008183613ddd5760405162461bcd60e51b8152600401610adc91906148c4565b506000838581613de957fe5b0495945050505050565b613dfb614347565b613e03614347565b82518451612db7916130ae565b613e18614347565b613e20614347565b82518451612db791612ca9565b600054610100900460ff1680613e465750613e466132d1565b80613e54575060005460ff16155b613e705760405162461bcd60e51b8152600401610adc90614bed565b600054610100900460ff16158015613355576000805460ff1961ff00199091166101001716600117905580156113bc576000805461ff001916905550565b600054610100900460ff1680613ec75750613ec76132d1565b80613ed5575060005460ff16155b613ef15760405162461bcd60e51b8152600401610adc90614bed565b600054610100900460ff16158015613f1c576000805460ff1961ff0019909116610100171660011790555b6000613f26612dfa565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35080156113bc576000805461ff001916905550565b6000613f92614347565b6000856001811115613fa057fe5b14613fe2576040805160208101909152609e548152613fdd90613fc39085613e10565b6040805160208101909152609d548152612bb79087613df3565b61401a565b6040805160208101909152609e54815261401a906140009085613df3565b6040805160208101909152609d548152612bb79087613e10565b6040805160208101909152609f54815260a780549293506136fc9284929190614044906001612ca9565b8154811061404e57fe5b60009182526020918290206040805160a081018252600490930290910180546080840190815283528151808501835260018201548152938301939093526002830154908201526003909101546060820152613d0c565b6000610d3e838360126142e9565b6000816140d15760405162461bcd60e51b8152600401610adc90614f97565b816000191480156140e55750600160ff1b83145b156141025760405162461bcd60e51b8152600401610adc90614c3b565b600082848161410d57fe5b05949350505050565b61411e614347565b614126614347565b82518451612db791614302565b6000610d3e83836012614310565b614149614347565b6141516143b8565b60a783602001518154811061416257fe5b600091825260208083206040805160a081018252600490940290910180546080850190815284528151808401835260018201548152928401929092526002820154908301526003015460608201529150835160018111156141bf57fe5b14156141dd57602081015181516141d591613520565b915050610b57565b6001835160018111156141ec57fe5b141561428157614203836040015160200151612e10565b61420f576141d5613180565b6000836040015160400151600181111561422557fe5b141561424857604083015180516020918201518351928401516141d59390610e11565b6001836040015160400151600181111561425e57fe5b141561428157604083015180516020918201518351928401516141d5939061172d565b60405162461bcd60e51b8152600401610adc90614970565b6142a1614347565b6142a9614347565b8351612db79084613115565b6142bd614347565b6142c5614347565b8351612db790846130d3565b6000610ea26142df83613c98565b6113ac8686613115565b6000610ea2836113ac6142fb85613c98565b8790613115565b6000610d3e8383601261432f565b6000610ea28361432961432285613c98565b8790613976565b906140b2565b6000610ea261433d83613c98565b6143298686613976565b6040518060200160405280600081525090565b604051806080016040528061436d614347565b815260200161437a614347565b8152602001614387614347565b8152602001614394614347565b905290565b60408051606081018252600080825260208201529081016143946143ec565b60405180608001604052806143cb614347565b81526020016143d8614347565b815260200160008152602001600081525090565b60408051606081019091528060008152602001614407614347565b81526020016000614394565b600060208284031215614424578081fd5b50919050565b60006020828403121561443b578081fd5b614445602061507a565b9135825250919050565b600060208284031215614460578081fd5b8135610d3e816150ab565b60006020828403121561447c578081fd5b8135610d3e816150c0565b60008060006060848603121561449b578182fd5b83356144a6816150ce565b92506144b58560208601614413565b91506144c48560408601614413565b90509250925092565b600080600080608085870312156144e2578081fd5b84356144ed816150ce565b93506144fc8660208701614413565b925061450b8660408701614413565b9150606085013561451b816150c0565b939692955090935050565b60008060408385031215614538578182fd5b8235614543816150ce565b9150614552846020850161442a565b90509250929050565b60008060008060808587031215614570578384fd5b843561457b816150ce565b935061458a866020870161442a565b9250614599866040870161442a565b91506145a8866060870161442a565b905092959194509250565b6000806000606084860312156145c7578283fd5b83356145d2816150ce565b925060208401356145e2816150ab565b929592945050506040919091013590565b600060208284031215614604578081fd5b610d3e8383614413565b60008060408385031215614620578182fd5b61462a8484614413565b91506145528460208501614413565b60006020828403121561464a578081fd5b610d3e838361442a565b600060208284031215614665578081fd5b61466f602061507a565b9151825250919050565b6000806040838503121561468b578182fd5b614543848461442a565b6000806000606084860312156146a9578081fd5b6146b3858561442a565b92506146c2856020860161442a565b91506144c4856040860161442a565b6000602082840312156146e2578081fd5b5035919050565b6000602082840312156146fa578081fd5b5051919050565b60008060408385031215614713578182fd5b50508035926020909101359150565b6000806000806000806000806000806000806101808d8f03121561474457898afd5b8c359b5060208d01359a5060408d0135995060608d0135985060808d013561476b816150ab565b975060a08d013561477b816150ab565b965060c08d013561478b816150ab565b955060e08d013594506101008d01356147a3816150ab565b93506101208d013592506101408d013591506101608d01356147c4816150ab565b809150509295989b509295989b509295989b565b6001600160a01b0391909116815260200190565b6001600160a01b038416815260608101614805846150a1565b602082019390935260400152919050565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b918252602082015260400190565b6080810161485e866150a1565b94815292516020840152905160408301525160609091015290565b60608101614886856150a1565b938152602081019290925260409091015290565b606081016148a7856150a1565b9381526001600160a01b0392909216602083015260409091015290565b6000602080835283518082850152825b818110156148f0578581018301518582016040015282016148d4565b818111156149015783604083870101525b50601f01601f1916929092016040019392505050565b6020808252601590820152746d756c7469706c6965722063616e2774206265203160581b604082015260600190565b60208082526010908201526f34b63632b3b0b61037b832b930ba37b960811b604082015260600190565b6020808252601490820152733737ba1039bab83837b93a32b21037b83a34b7b760611b604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526019908201527f63616e206e6f742073657420696e74657276616c20746f203000000000000000604082015260600190565b6020808252601a908201527f63616c6c6572206973206e6f7420636f756e7465725061727479000000000000604082015260600190565b60208082526021908201527f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252601290820152711bdd995c881d1c98591a5b99c81b1a5b5a5d60721b604082015260600190565b6020808252601c908201527f696c6c6567616c206c6971756964697479206d756c7469706c69657200000000604082015260600190565b6020808252600f908201526e0d2dcc6dee4e4cac6e840d2dcc8caf608b1b604082015260600190565b6020808252600f908201526e696e76616c6964206164647265737360881b604082015260600190565b6020808252601a908201527f6f7261636c652070726963652063616e2774206265207a65726f000000000000604082015260600190565b6020808252601f908201527f7072696365206973206f76657220666c756374756174696f6e206c696d697400604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60208082526021908201527f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526014908201527331b0b63632b91034b9903737ba1036b4b73a32b960611b604082015260600190565b6020808252601d908201527f4c657373207468616e206d696e696d616c2071756f746520746f6b656e000000604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252601c908201527f4c657373207468616e206d696e696d616c206261736520746f6b656e00000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526027908201527f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f604082015266766572666c6f7760c81b606082015260800190565b6020808252600d908201526c696e76616c696420726174696f60981b604082015260600190565b602080825260139082015272195e18da185b99d9481dd85cc818db1bdcd959606a1b604082015260600190565b6020808252601d908201527f4d6f7265207468616e206d6178696d616c2071756f746520746f6b656e000000604082015260600190565b6020808252601c908201527f4d6f7265207468616e206d6178696d616c206261736520746f6b656e00000000604082015260600190565b60208082526018908201527f736574746c652066756e64696e6720746f6f206561726c790000000000000000604082015260600190565b602080825260169082015275696c6c6567616c2070726963652066656564206b657960501b604082015260600190565b6020808252600d908201526c1a5b9d985b1a59081a5b9c1d5d609a1b604082015260600190565b60208082526024908201527f5369676e6564536166654d6174683a207375627472616374696f6e206f766572604082015263666c6f7760e01b606082015260800190565b602080825260149082015273696e76616c6964206f7261636c6520707269636560601b604082015260600190565b6020808252818101527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604082015260600190565b815151815260208083015151908201526040808301515190820152606091820151519181019190915260800190565b9035815260200190565b9051815260200190565b9151825251602082015260400190565b93518452915160208401526040830152606082015260800190565b9283526020830191909152604082015260600190565b9586526020860194909452604085019290925260608401526080830152151560a082015260c00190565b60405181810167ffffffffffffffff8111828210171561509957600080fd5b604052919050565b600281106113bc57fe5b6001600160a01b03811681146113bc57600080fd5b80151581146113bc57600080fd5b600281106113bc57600080fdfe4d69786564446563696d616c3a2075696e742076616c756520697320626967676572207468616e205f494e543235365f4d4158a264697066735822122005ec6794b389a5b88b28bcef0f2ae00d8cc1f84a444a82ff70868bda1244785164736f6c634300060c0033

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