Contract 0xf1791E48B9d7f7b39036d5aE9601B45C9694539e 1

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xd62a442735cf0bb7f9f9c6acf8f5a12c7501fd8881c8ed19b67da5e081b89f5a0x6080604029017392021-11-08 8:07:1128 days 12 hrs ago0x904b5993fc92979eeedc19ccc58bed6b7216667c IN  Contract Creation0 ETH0.244300879777 ETH
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LiquidityPool

Compiler Version
v0.7.4+commit.3f05b770

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 49 : LiquidityPool.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";

import "./interface/ILiquidityPool.sol";

import "./module/AMMModule.sol";
import "./module/LiquidityPoolModule.sol";
import "./module/PerpetualModule.sol";

import "./Getter.sol";
import "./Governance.sol";
import "./LibraryEvents.sol";
import "./Perpetual.sol";
import "./Storage.sol";
import "./Type.sol";

contract LiquidityPool is Storage, Perpetual, Getter, Governance, LibraryEvents, ILiquidityPool {
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set;
    using SafeCastUpgradeable for uint256;
    using PerpetualModule for PerpetualStorage;
    using LiquidityPoolModule for LiquidityPoolStorage;
    using AMMModule for LiquidityPoolStorage;

    receive() external payable {
        revert("contract does not accept ether");
    }

    /**
     * @notice  Initialize the liquidity pool and set up its configuration
     *
     * @param   operator                The address of operator which should be current pool creator.
     * @param   collateral              The address of collateral token.
     * @param   collateralDecimals      The decimals of collateral token, to support token without decimals interface.
     * @param   governor                The address of governor, who is able to call governance methods.
     * @param   initData                A bytes array contains data to initialize new created liquidity pool.
     */
    function initialize(
        address operator,
        address collateral,
        uint256 collateralDecimals,
        address governor,
        bytes calldata initData
    ) external override initializer {
        _liquidityPool.initialize(
            _msgSender(),
            collateral,
            collateralDecimals,
            operator,
            governor,
            initData
        );
    }

    /**
     * @notice  Create new perpetual of the liquidity pool.
     *          The operator can create perpetual only when the pool is not running or isFastCreationEnabled is true.
     *          Otherwise a perpetual can only be create by governor (say, through voting).
     *
     * @param   oracle              The oracle's address of the perpetual.
     * @param   baseParams          The base parameters of the perpetual, see whitepaper for details.
     * @param   riskParams          The risk parameters of the perpetual,
     *                              Must be within range [minRiskParamValues, maxRiskParamValues].
     * @param   minRiskParamValues  The minimum values of risk parameters.
     * @param   maxRiskParamValues  The maximum values of risk parameters.
     */
    function createPerpetual(
        address oracle,
        int256[9] calldata baseParams,
        int256[9] calldata riskParams,
        int256[9] calldata minRiskParamValues,
        int256[9] calldata maxRiskParamValues
    ) external onlyNotUniverseSettled {
        if (!_liquidityPool.isRunning || _liquidityPool.isFastCreationEnabled) {
            require(
                _msgSender() == _liquidityPool.getOperator(),
                "only operator can create perpetual"
            );
        } else {
            require(_msgSender() == _liquidityPool.governor, "only governor can create perpetual");
        }
        _liquidityPool.createPerpetual(
            oracle,
            baseParams,
            riskParams,
            minRiskParamValues,
            maxRiskParamValues
        );
    }

    /**
     * @notice  Set the liquidity pool to running state. Can be call only once by operater.m n
     */
    function runLiquidityPool() external override onlyOperator {
        require(!_liquidityPool.isRunning, "already running");
        _liquidityPool.runLiquidityPool();
    }

    /**
     * @notice  If you want to get the real-time data, call this function first
     */
    function forceToSyncState() public override syncState(false) {}

    /**
     * @notice  Add liquidity to the liquidity pool.
     *          Liquidity provider deposits collaterals then gets share tokens back.
     *          The ratio of added cash to share token is determined by current liquidity.
     *          Can only called when the pool is running.
     *
     * @param   cashToAdd   The amount of cash to add. always use decimals 18.
     */
    function addLiquidity(int256 cashToAdd)
        external
        override
        onlyNotUniverseSettled
        syncState(false)
        nonReentrant
    {
        require(_liquidityPool.isRunning, "pool is not running");
        _liquidityPool.addLiquidity(_msgSender(), cashToAdd);
    }

    /**
     * @notice  Remove liquidity from the liquidity pool.
     *          Liquidity providers redeems share token then gets collateral back.
     *          The amount of collateral retrieved may differ from the amount when adding liquidity,
     *          The index price, trading fee and positions holding by amm will affect the profitability of providers.
     *          Can only called when the pool is running.
     *
     * @param   shareToRemove   The amount of share token to remove. The amount always use decimals 18.
     * @param   cashToReturn    The amount of cash(collateral) to return. The amount always use decimals 18.
     */
    function removeLiquidity(int256 shareToRemove, int256 cashToReturn)
        external
        override
        nonReentrant
        syncState(false)
    {
        require(_liquidityPool.isRunning, "pool is not running");
        if (IPoolCreatorFull(_liquidityPool.creator).isUniverseSettled()) {
            require(
                _liquidityPool.isAllPerpetualIn(PerpetualState.CLEARED),
                "all perpetual must be cleared"
            );
        }
        _liquidityPool.removeLiquidity(_msgSender(), shareToRemove, cashToReturn);
    }

    /**
     * @notice  Donate collateral to the insurance fund of the pool.
     *          Can only called when the pool is running.
     *          Donated collateral is not withdrawable but can be used to improve security.
     *          Unexpected loss (bankrupt) will be deducted from insurance fund then donated insurance fund.
     *          Until donated insurance fund is drained, the perpetual will not enter emergency state and shutdown.
     *
     * @param   amount          The amount of collateral to donate. The amount always use decimals 18.
     */
    function donateInsuranceFund(int256 amount) external nonReentrant {
        require(_liquidityPool.isRunning, "pool is not running");
        _liquidityPool.donateInsuranceFund(_msgSender(), amount);
    }

    /**
     * @notice  Add liquidity to the liquidity pool without getting shares.
     *
     * @param   cashToAdd   The amount of cash to add. The amount always use decimals 18.
     */
    function donateLiquidity(int256 cashToAdd) external nonReentrant {
        require(_liquidityPool.isRunning, "pool is not running");
        _liquidityPool.donateLiquidity(_msgSender(), cashToAdd);
    }

    bytes32[50] private __gap;
}

File 2 of 49 : EnumerableSetUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 3 of 49 : SafeCastUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;


/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such 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.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 4 of 49 : ILiquidityPool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "../Type.sol";

interface ILiquidityPool {
    /**
     * @notice Initialize the liquidity pool and set up its configuration.
     *
     * @param operator              The operator's address of the liquidity pool.
     * @param collateral            The collateral's address of the liquidity pool.
     * @param collateralDecimals    The collateral's decimals of the liquidity pool.
     * @param governor              The governor's address of the liquidity pool.
     * @param initData              A bytes array contains data to initialize new created liquidity pool.
     */
    function initialize(
        address operator,
        address collateral,
        uint256 collateralDecimals,
        address governor,
        bytes calldata initData
    ) external;

    /**
     * @notice  Set the liquidity pool to running state. Can be call only once by operater.m n
     */
    function runLiquidityPool() external;

    /**
     * @notice  If you want to get the real-time data, call this function first
     */
    function forceToSyncState() external;

    /**
     * @notice  Add liquidity to the liquidity pool.
     *          Liquidity provider deposits collaterals then gets share tokens back.
     *          The ratio of added cash to share token is determined by current liquidity.
     *          Can only called when the pool is running.
     *
     * @param   cashToAdd   The amount of cash to add. always use decimals 18.
     */
    function addLiquidity(int256 cashToAdd) external;

    /**
     * @notice  Remove liquidity from the liquidity pool.
     *          Liquidity providers redeems share token then gets collateral back.
     *          The amount of collateral retrieved may differ from the amount when adding liquidity,
     *          The index price, trading fee and positions holding by amm will affect the profitability of providers.
     *          Can only called when the pool is running.
     *
     * @param   shareToRemove   The amount of share token to remove. The amount always use decimals 18.
     * @param   cashToReturn    The amount of cash(collateral) to return. The amount always use decimals 18.
     */
    function removeLiquidity(int256 shareToRemove, int256 cashToReturn) external;
}

File 5 of 49 : AMMModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

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

import "../libraries/Constant.sol";
import "../libraries/Math.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";

import "../module/MarginAccountModule.sol";
import "../module/PerpetualModule.sol";

import "../Type.sol";

library AMMModule {
    using Math for int256;
    using SafeMathExt for int256;
    using SignedSafeMathUpgradeable for int256;
    using SafeCastUpgradeable for uint256;

    using MarginAccountModule for PerpetualStorage;
    using PerpetualModule for PerpetualStorage;

    struct Context {
        int256 indexPrice;
        int256 position;
        int256 positionValue;
        // squareValue is 10^36, others are 10^18
        int256 squareValue;
        int256 positionMargin;
        int256 availableCash;
    }

    /**
     * @dev     Get the trading result when trader trades with AMM, divided into two parts:
     *            - AMM closes its position
     *            - AMM opens its position.
     *
     * @param   liquidityPool   The liquidity pool object of AMM.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool to trade.
     * @param   tradeAmount     The trading amount of position, positive if AMM longs, negative if AMM shorts.
     * @param   partialFill     Whether to allow partially trading. Set to true when liquidation trading,
     *                          set to false when normal trading.
     * @return  deltaCash       The update cash(collateral) of AMM after the trade.
     * @return  deltaPosition   The update position of AMM after the trade.
     */
    function queryTradeWithAMM(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256 tradeAmount,
        bool partialFill
    ) public view returns (int256 deltaCash, int256 deltaPosition) {
        require(tradeAmount != 0, "trading amount is zero");
        Context memory context = prepareContext(liquidityPool, perpetualIndex);
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        (int256 closePosition, int256 openPosition) = Utils.splitAmount(
            context.position,
            tradeAmount
        );
        // AMM close position
        int256 closeBestPrice;
        (deltaCash, closeBestPrice) = ammClosePosition(context, perpetual, closePosition);
        context.availableCash = context.availableCash.add(deltaCash);
        context.position = context.position.add(closePosition);
        // AMM open position
        (int256 openDeltaCash, int256 openDeltaPosition, int256 openBestPrice) = ammOpenPosition(
            context,
            perpetual,
            openPosition,
            partialFill
        );
        deltaCash = deltaCash.add(openDeltaCash);
        deltaPosition = closePosition.add(openDeltaPosition);
        int256 bestPrice = closePosition != 0 ? closeBestPrice : openBestPrice;
        // If price is better(for trader) than best price, change price to best price
        deltaCash = deltaCash.max(bestPrice.wmul(deltaPosition).neg());
    }

    /**
     * @dev     Calculate the amount of share token to mint when liquidity provider adds liquidity to the liquidity pool.
     *          If adding liquidity at first time, which means total supply of share token is zero,
     *          the amount of share token to mint equals to the pool margin after adding liquidity.
     *
     * @param   liquidityPool       The liquidity pool object of AMM.
     * @param   shareTotalSupply    The total supply of the share token before adding liquidity.
     * @param   cashToAdd           The amount of cash(collateral) added to the liquidity pool.
     * @return  shareToMint         The amount of share token to mint.
     * @return  addedPoolMargin     The added amount of pool margin after adding liquidity.
     */
    function getShareToMint(
        LiquidityPoolStorage storage liquidityPool,
        int256 shareTotalSupply,
        int256 cashToAdd
    ) public view returns (int256 shareToMint, int256 addedPoolMargin) {
        Context memory context = prepareContext(liquidityPool);
        (int256 poolMargin, ) = getPoolMargin(context);
        context.availableCash = context.availableCash.add(cashToAdd);
        (int256 newPoolMargin, ) = getPoolMargin(context);
        require(
            liquidityPool.liquidityCap == 0 ||
                newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
            "liquidity reaches cap"
        );
        addedPoolMargin = newPoolMargin.sub(poolMargin);
        if (shareTotalSupply == 0) {
            // first time, if there is pool margin left in pool, it belongs to the first person who adds liquidity
            shareToMint = newPoolMargin;
        } else {
            // If share token's total supply is not zero and there is no money in pool,
            // these share tokens have no value. This case should be avoided.
            require(poolMargin > 0, "share token has no value");
            shareToMint = newPoolMargin.sub(poolMargin).wfrac(shareTotalSupply, poolMargin);
        }
    }

    /**
     * @dev     Calculate the amount of cash to add when liquidity provider adds liquidity to the liquidity pool.
     *          If adding liquidity at first time, which means total supply of share token is zero,
     *          the amount of cash to add equals to the share amount to mint minus pool margin before adding liquidity.
     *
     * @param   liquidityPool       The liquidity pool object of AMM.
     * @param   shareTotalSupply    The total supply of the share token before adding liquidity.
     * @param   shareToMint         The amount of share token to mint.
     * @return  cashToAdd           The amount of cash(collateral) to add to the liquidity pool.
     */
    function getCashToAdd(
        LiquidityPoolStorage storage liquidityPool,
        int256 shareTotalSupply,
        int256 shareToMint
    ) public view returns (int256 cashToAdd) {
        Context memory context = prepareContext(liquidityPool);
        (int256 poolMargin, ) = getPoolMargin(context);
        if (shareTotalSupply == 0) {
            // first time, if there is pool margin left in pool, it belongs to the first person who adds liquidity
            cashToAdd = shareToMint.sub(poolMargin).max(0);
            int256 newPoolMargin = cashToAdd.add(poolMargin);
            require(
                liquidityPool.liquidityCap == 0 ||
                    newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
                "liquidity reaches cap"
            );
        } else {
            // If share token's total supply is not zero and there is no money in pool,
            // these share tokens have no value. This case should be avoided.
            require(poolMargin > 0, "share token has no value");
            int256 newPoolMargin = shareTotalSupply.add(shareToMint).wfrac(
                poolMargin,
                shareTotalSupply
            );
            require(
                liquidityPool.liquidityCap == 0 ||
                    newPoolMargin <= liquidityPool.liquidityCap.toInt256(),
                "liquidity reaches cap"
            );
            int256 minPoolMargin = context.squareValue.div(2).sqrt();
            int256 newCash;
            if (newPoolMargin <= minPoolMargin) {
                // pool is still unsafe after adding liquidity
                newCash = newPoolMargin.mul(2).sub(context.positionValue);
            } else {
                // context.squareValue is 10^36, so use div instead of wdiv
                newCash = context.squareValue.div(newPoolMargin).div(2).add(newPoolMargin).sub(
                    context.positionValue
                );
            }
            cashToAdd = newCash.sub(context.availableCash);
        }
    }

    /**
     * @dev     Calculate the amount of cash(collateral) to return when liquidity provider removes liquidity from the liquidity pool.
     *          Removing liquidity is forbidden at several cases:
     *            1. AMM is unsafe before removing liquidity
     *            2. AMM is unsafe after removing liquidity
     *            3. AMM will offer negative price at any perpetual after removing liquidity
     *            4. AMM will exceed maximum leverage at any perpetual after removing liquidity
     *
     * @param   liquidityPool                The liquidity pool object of AMM.
     * @param   shareTotalSupply             The total supply of the share token before removing liquidity.
     * @param   shareToRemove                The amount of share token to redeem.
     * @return  cashToReturn                 The amount of cash(collateral) to return.
     * @return  removedInsuranceFund         The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedDonatedInsuranceFund  The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedPoolMargin            The removed amount of pool margin after removing liquidity.
     */
    function getCashToReturn(
        LiquidityPoolStorage storage liquidityPool,
        int256 shareTotalSupply,
        int256 shareToRemove
    )
        public
        view
        returns (
            int256 cashToReturn,
            int256 removedInsuranceFund,
            int256 removedDonatedInsuranceFund,
            int256 removedPoolMargin
        )
    {
        require(
            shareTotalSupply > 0,
            "total supply of share token is zero when removing liquidity"
        );
        Context memory context = prepareContext(liquidityPool);
        require(isAMMSafe(context, 0), "AMM is unsafe before removing liquidity");
        removedPoolMargin = calculatePoolMarginWhenSafe(context, 0);
        require(removedPoolMargin > 0, "pool margin must be positive");
        int256 poolMargin = shareTotalSupply.sub(shareToRemove).wfrac(
            removedPoolMargin,
            shareTotalSupply
        );
        removedPoolMargin = removedPoolMargin.sub(poolMargin);
        {
            int256 minPoolMargin = context.squareValue.div(2).sqrt();
            require(poolMargin >= minPoolMargin, "AMM is unsafe after removing liquidity");
        }
        cashToReturn = calculateCashToReturn(context, poolMargin);
        require(cashToReturn >= 0, "received margin is negative");
        uint256 length = liquidityPool.perpetualCount;
        bool allCleared = true;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.CLEARED) {
                allCleared = false;
            }
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            // prevent AMM offering negative price
            require(
                perpetual.getPosition(address(this)) <=
                    poolMargin.wdiv(perpetual.openSlippageFactor.value).wdiv(
                        perpetual.getIndexPrice()
                    ),
                "AMM is unsafe after removing liquidity"
            );
        }
        // prevent AMM exceeding max leverage
        require(
            context.availableCash.add(context.positionValue).sub(cashToReturn) >=
                context.positionMargin,
            "AMM exceeds max leverage after removing liquidity"
        );
        if (allCleared) {
            // get insurance fund proportionally
            removedInsuranceFund = liquidityPool.insuranceFund.wfrac(
                shareToRemove,
                shareTotalSupply,
                Round.FLOOR
            );
            removedDonatedInsuranceFund = liquidityPool.donatedInsuranceFund.wfrac(
                shareToRemove,
                shareTotalSupply,
                Round.FLOOR
            );
            cashToReturn = cashToReturn.add(removedInsuranceFund).add(removedDonatedInsuranceFund);
        }
    }

    /**
     * @dev     Calculate the amount of share token to redeem when liquidity provider removes liquidity from the liquidity pool.
     *          Removing liquidity is forbidden at several cases:
     *            1. AMM is unsafe before removing liquidity
     *            2. AMM is unsafe after removing liquidity
     *            3. AMM will offer negative price at any perpetual after removing liquidity
     *            4. AMM will exceed maximum leverage at any perpetual after removing liquidity
     *
     * @param   liquidityPool                The liquidity pool object of AMM.
     * @param   shareTotalSupply             The total supply of the share token before removing liquidity.
     * @param   cashToReturn                 The cash(collateral) to return.
     * @return  shareToRemove                The amount of share token to redeem.
     * @return  removedInsuranceFund         The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedDonatedInsuranceFund  The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedPoolMargin            The removed amount of pool margin after removing liquidity.
     */
    function getShareToRemove(
        LiquidityPoolStorage storage liquidityPool,
        int256 shareTotalSupply,
        int256 cashToReturn
    )
        public
        view
        returns (
            int256 shareToRemove,
            int256 removedInsuranceFund,
            int256 removedDonatedInsuranceFund,
            int256 removedPoolMargin
        )
    {
        require(
            shareTotalSupply > 0,
            "total supply of share token is zero when removing liquidity"
        );
        Context memory context = prepareContext(liquidityPool);
        require(isAMMSafe(context, 0), "AMM is unsafe before removing liquidity");
        int256 poolMargin = calculatePoolMarginWhenSafe(context, 0);
        context.availableCash = context.availableCash.sub(cashToReturn);
        require(isAMMSafe(context, 0), "AMM is unsafe after removing liquidity");
        int256 newPoolMargin = calculatePoolMarginWhenSafe(context, 0);
        removedPoolMargin = poolMargin.sub(newPoolMargin);
        shareToRemove = poolMargin.sub(newPoolMargin).wfrac(shareTotalSupply, poolMargin);
        uint256 length = liquidityPool.perpetualCount;
        bool allCleared = true;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.CLEARED) {
                allCleared = false;
            }
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            // prevent AMM offering negative price
            require(
                perpetual.getPosition(address(this)) <=
                    newPoolMargin.wdiv(perpetual.openSlippageFactor.value).wdiv(
                        perpetual.getIndexPrice()
                    ),
                "AMM is unsafe after removing liquidity"
            );
        }
        // prevent AMM exceeding max leverage
        require(
            context.availableCash.add(context.positionValue) >= context.positionMargin,
            "AMM exceeds max leverage after removing liquidity"
        );
        if (allCleared) {
            // get insurance fund proportionally
            (
                shareToRemove,
                removedInsuranceFund,
                removedDonatedInsuranceFund,
                removedPoolMargin
            ) = getShareToRemoveWhenAllCleared(
                liquidityPool,
                cashToReturn,
                poolMargin,
                shareTotalSupply
            );
        }
    }

    /**
     * @dev     Calculate the amount of share token to redeem when liquidity provider removes liquidity from the liquidity pool.
     *          Only called when all perpetuals in the liquidity pool are in CLEARED state.
     *
     * @param   liquidityPool                The liquidity pool object of AMM.
     * @param   cashToReturn                 The cash(collateral) to return.
     * @param   poolMargin                   The pool margin before removing liquidity.
     * @param   shareTotalSupply             The total supply of the share token before removing liquidity.
     * @return  shareToRemove                The amount of share token to redeem.
     * @return  removedInsuranceFund         The part of insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedDonatedInsuranceFund  The part of donated insurance fund returned to LP if all perpetuals are in CLEARED state.
     * @return  removedPoolMargin            The part of pool margin returned to LP if all perpetuals are in CLEARED state.
     */
    function getShareToRemoveWhenAllCleared(
        LiquidityPoolStorage storage liquidityPool,
        int256 cashToReturn,
        int256 poolMargin,
        int256 shareTotalSupply
    )
        public
        view
        returns (
            int256 shareToRemove,
            int256 removedInsuranceFund,
            int256 removedDonatedInsuranceFund,
            int256 removedPoolMargin
        )
    {
        // get insurance fund proportionally
        require(
            poolMargin.add(liquidityPool.insuranceFund).add(liquidityPool.donatedInsuranceFund) > 0,
            "all cleared, insufficient liquidity"
        );
        shareToRemove = shareTotalSupply.wfrac(
            cashToReturn,
            poolMargin.add(liquidityPool.insuranceFund).add(liquidityPool.donatedInsuranceFund)
        );
        removedInsuranceFund = liquidityPool.insuranceFund.wfrac(
            shareToRemove,
            shareTotalSupply,
            Round.FLOOR
        );
        removedDonatedInsuranceFund = liquidityPool.donatedInsuranceFund.wfrac(
            shareToRemove,
            shareTotalSupply,
            Round.FLOOR
        );
        removedPoolMargin = poolMargin.wfrac(shareToRemove, shareTotalSupply, Round.FLOOR);
    }

    /**
     * @dev     Calculate the pool margin of AMM when AMM is safe.
     *          Pool margin is how much collateral of the pool considering the AMM's positions of perpetuals.
     *
     * @param   context         Context object of AMM, but current perpetual is not included.
     * @param   slippageFactor  The slippage factor of current perpetual.
     * @return  poolMargin      The pool margin of AMM.
     */
    function calculatePoolMarginWhenSafe(Context memory context, int256 slippageFactor)
        internal
        pure
        returns (int256 poolMargin)
    {
        // The context doesn't include the current perpetual, add them.
        int256 positionValue = context.indexPrice.wmul(context.position);
        int256 margin = positionValue.add(context.positionValue).add(context.availableCash);
        // 10^36, the same as context.squareValue
        int256 tmp = positionValue.wmul(positionValue).mul(slippageFactor).add(context.squareValue);
        int256 beforeSqrt = margin.mul(margin).sub(tmp.mul(2));
        require(beforeSqrt >= 0, "AMM is unsafe when calculating pool margin");
        poolMargin = beforeSqrt.sqrt().add(margin).div(2);
        require(poolMargin >= 0, "pool margin is negative when calculating pool margin");
    }

    /**
     * @dev     Check if AMM is safe
     * @param   context         Context object of AMM, but current perpetual is not included.
     * @param   slippageFactor  The slippage factor of current perpetual.
     * @return  bool            True if AMM is safe.
     */
    function isAMMSafe(Context memory context, int256 slippageFactor) internal pure returns (bool) {
        int256 positionValue = context.indexPrice.wmul(context.position);
        // 10^36, the same as context.squareValue
        int256 minAvailableCash = positionValue.wmul(positionValue).mul(slippageFactor);
        minAvailableCash = minAvailableCash.add(context.squareValue).mul(2).sqrt().sub(
            context.positionValue.add(positionValue)
        );
        return context.availableCash >= minAvailableCash;
    }

    /**
     * @dev     Get the trading result when AMM closes its position.
     *          If the AMM is unsafe, the trading price is the best price.
     *          If trading price is too bad, it will be limited to index price * (1 +/- max close price discount)
     *
     * @param   context     Context object of AMM, but current perpetual is not included.
     * @param   perpetual   The perpetual object to trade.
     * @param   tradeAmount The amount of position to trade.
     *                      Positive for long and negative for short from AMM's perspective.
     * @return  deltaCash   The update cash(collateral) of AMM after the trade.
     * @return  bestPrice   The best price, is used for clipping to spread price if needed outside.
     *                      If AMM is safe, best price = middle price * (1 +/- half spread).
     *                      If AMM is unsafe and normal case, best price = index price.
     */
    function ammClosePosition(
        Context memory context,
        PerpetualStorage storage perpetual,
        int256 tradeAmount
    ) internal view returns (int256 deltaCash, int256 bestPrice) {
        if (tradeAmount == 0) {
            return (0, 0);
        }
        int256 positionBefore = context.position;
        int256 indexPrice = context.indexPrice;
        int256 slippageFactor = perpetual.closeSlippageFactor.value;
        int256 maxClosePriceDiscount = perpetual.maxClosePriceDiscount.value;
        int256 halfSpread = tradeAmount < 0
            ? perpetual.halfSpread.value
            : perpetual.halfSpread.value.neg();
        if (isAMMSafe(context, slippageFactor)) {
            int256 poolMargin = calculatePoolMarginWhenSafe(context, slippageFactor);
            require(poolMargin > 0, "pool margin must be positive");
            bestPrice = getMidPrice(poolMargin, indexPrice, positionBefore, slippageFactor).wmul(
                halfSpread.add(Constant.SIGNED_ONE)
            );
            deltaCash = getDeltaCash(
                poolMargin,
                positionBefore,
                positionBefore.add(tradeAmount),
                indexPrice,
                slippageFactor
            );
        } else {
            bestPrice = indexPrice;
            deltaCash = bestPrice.wmul(tradeAmount).neg();
        }
        int256 priceLimit = tradeAmount > 0
            ? Constant.SIGNED_ONE.add(maxClosePriceDiscount)
            : Constant.SIGNED_ONE.sub(maxClosePriceDiscount);
        // prevent too bad price
        deltaCash = deltaCash.max(indexPrice.wmul(priceLimit).wmul(tradeAmount).neg());
        // prevent negative price
        require(
            !Utils.hasTheSameSign(deltaCash, tradeAmount),
            "price is negative when AMM closes position"
        );
    }

    /**
     * @dev     Get the trading result when AMM opens its position.
     *          AMM can't open position when unsafe and can't open position to exceed the maximum position
     *
     * @param   context     Context object of AMM, but current perpetual is not included.
     * @param   perpetual   The perpetual object to trade
     * @param   tradeAmount The trading amount of position, positive if AMM longs, negative if AMM shorts
     * @param   partialFill Whether to allow partially trading. Set to true when liquidation trading,
     *                      set to false when normal trading
     * @return  deltaCash       The update cash(collateral) of AMM after the trade
     * @return  deltaPosition   The update position of AMM after the trade
     * @return  bestPrice       The best price, is used for clipping to spread price if needed outside.
     *                          Equal to middle price * (1 +/- half spread)
     */
    function ammOpenPosition(
        Context memory context,
        PerpetualStorage storage perpetual,
        int256 tradeAmount,
        bool partialFill
    )
        internal
        view
        returns (
            int256 deltaCash,
            int256 deltaPosition,
            int256 bestPrice
        )
    {
        if (tradeAmount == 0) {
            return (0, 0, 0);
        }
        int256 slippageFactor = perpetual.openSlippageFactor.value;
        if (!isAMMSafe(context, slippageFactor)) {
            require(partialFill, "AMM is unsafe when open");
            return (0, 0, 0);
        }
        int256 poolMargin = calculatePoolMarginWhenSafe(context, slippageFactor);
        require(poolMargin > 0, "pool margin must be positive");
        int256 indexPrice = context.indexPrice;
        int256 positionBefore = context.position;
        int256 positionAfter = positionBefore.add(tradeAmount);
        int256 maxPosition = getMaxPosition(
            context,
            poolMargin,
            perpetual.ammMaxLeverage.value,
            slippageFactor,
            positionAfter > 0
        );
        if (positionAfter.abs() > maxPosition.abs()) {
            require(partialFill, "trade amount exceeds max amount");
            // trade to max position if partialFill
            deltaPosition = maxPosition.sub(positionBefore);
            // current position already exeeds max position before trade, can't open
            if (Utils.hasTheSameSign(deltaPosition, tradeAmount.neg())) {
                return (0, 0, 0);
            }
            positionAfter = maxPosition;
        } else {
            deltaPosition = tradeAmount;
        }
        deltaCash = getDeltaCash(
            poolMargin,
            positionBefore,
            positionAfter,
            indexPrice,
            slippageFactor
        );
        // prevent negative price
        require(
            !Utils.hasTheSameSign(deltaCash, deltaPosition),
            "price is negative when AMM opens position"
        );
        int256 halfSpread = tradeAmount < 0
            ? perpetual.halfSpread.value
            : perpetual.halfSpread.value.neg();
        bestPrice = getMidPrice(poolMargin, indexPrice, positionBefore, slippageFactor).wmul(
            halfSpread.add(Constant.SIGNED_ONE)
        );
    }

    /**
     * @dev     Calculate the status of AMM
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @return  context         Context object of AMM, but current perpetual is not included.
     */
    function prepareContext(LiquidityPoolStorage storage liquidityPool)
        internal
        view
        returns (Context memory context)
    {
        context = prepareContext(liquidityPool, liquidityPool.perpetualCount);
    }

    /**
     * @dev     Calculate the status of AMM, but specified perpetual index is not included.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool to distinguish,
     *                          set to liquidityPool.perpetualCount to skip distinguishing.
     * @return  context         Context object of AMM.
     */
    function prepareContext(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
        internal
        view
        returns (Context memory context)
    {
        int256 maintenanceMargin;
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            // only involve normal market
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            int256 position = perpetual.getPosition(address(this));
            int256 indexPrice = perpetual.getIndexPrice();
            require(indexPrice > 0, "index price must be positive");
            context.availableCash = context.availableCash.add(
                perpetual.getAvailableCash(address(this))
            );
            maintenanceMargin = maintenanceMargin.add(
                indexPrice.wmul(position).wmul(perpetual.maintenanceMarginRate).abs()
            );
            if (i == perpetualIndex) {
                context.indexPrice = indexPrice;
                context.position = position;
            } else {
                // To avoid returning more cash than pool has because of precision error,
                // cashToReturn should be smaller, which means positionValue should be smaller, squareValue should be bigger
                context.positionValue = context.positionValue.add(
                    indexPrice.wmul(position, Round.FLOOR)
                );
                // 10^36
                context.squareValue = context.squareValue.add(
                    position
                        .wmul(position, Round.CEIL)
                        .wmul(indexPrice, Round.CEIL)
                        .wmul(indexPrice, Round.CEIL)
                        .mul(perpetual.openSlippageFactor.value)
                );
                context.positionMargin = context.positionMargin.add(
                    indexPrice.wmul(position).abs().wdiv(perpetual.ammMaxLeverage.value)
                );
            }
        }
        context.availableCash = context.availableCash.add(liquidityPool.poolCash);
        // prevent margin balance < maintenance margin.
        // call setEmergencyState(SET_ALL_PERPETUALS_TO_EMERGENCY_STATE) when AMM is maintenance margin unsafe
        require(
            context.availableCash.add(context.positionValue).add(
                context.indexPrice.wmul(context.position)
            ) >= maintenanceMargin,
            "AMM is mm unsafe"
        );
    }

    /**
     * @dev     Calculate the cash(collateral) to return when removing liquidity.
     *
     * @param   context         Context object of AMM, but current perpetual is not included.
     * @param   poolMargin      The pool margin of AMM before removing liquidity.
     * @return  cashToReturn    The cash(collateral) to return.
     */
    function calculateCashToReturn(Context memory context, int256 poolMargin)
        public
        pure
        returns (int256 cashToReturn)
    {
        if (poolMargin == 0) {
            // remove all
            return context.availableCash;
        }
        require(poolMargin > 0, "pool margin must be positive when removing liquidity");
        // context.squareValue is 10^36, so use div instead of wdiv
        cashToReturn = context.squareValue.div(poolMargin).div(2).add(poolMargin).sub(
            context.positionValue
        );
        cashToReturn = context.availableCash.sub(cashToReturn);
    }

    /**
     * @dev     Get the middle price offered by AMM
     *
     * @param   poolMargin      The pool margin of AMM.
     * @param   indexPrice      The index price of the perpetual.
     * @param   position        The position of AMM in the perpetual.
     * @param   slippageFactor  The slippage factor of AMM in the perpetual.
     * @return  midPrice        A middle price offered by AMM.
     */
    function getMidPrice(
        int256 poolMargin,
        int256 indexPrice,
        int256 position,
        int256 slippageFactor
    ) internal pure returns (int256 midPrice) {
        midPrice = Constant
            .SIGNED_ONE
            .sub(indexPrice.wmul(position).wfrac(slippageFactor, poolMargin))
            .wmul(indexPrice);
    }

    /**
     * @dev     Get update cash(collateral) of AMM if trader trades against AMM.
     *
     * @param   poolMargin      The pool margin of AMM.
     * @param   positionBefore  The position of AMM in the perpetual before trading.
     * @param   positionAfter   The position of AMM in the perpetual after trading.
     * @param   indexPrice      The index price of the perpetual.
     * @param   slippageFactor  The slippage factor of AMM in the perpetual.
     * @return  deltaCash       The update cash(collateral) of AMM after trading.
     */
    function getDeltaCash(
        int256 poolMargin,
        int256 positionBefore,
        int256 positionAfter,
        int256 indexPrice,
        int256 slippageFactor
    ) internal pure returns (int256 deltaCash) {
        deltaCash = positionAfter.add(positionBefore).wmul(indexPrice).div(2).wfrac(
            slippageFactor,
            poolMargin
        );
        deltaCash = Constant.SIGNED_ONE.sub(deltaCash).wmul(indexPrice).wmul(
            positionBefore.sub(positionAfter)
        );
    }

    /**
     * @dev     Get the max position of AMM in the perpetual when AMM is opening position, calculated by three restrictions:
     *          1. AMM must be safe after the trade.
     *          2. AMM mustn't exceed maximum leverage in any perpetual after the trade.
     *          3. AMM must offer positive price in any perpetual after the trade. It's easy to prove that, in the
     *             perpetual, AMM definitely offers positive price when AMM holds short position.
     *
     * @param   context         Context object of AMM, but current perpetual is not included.
     * @param   poolMargin      The pool margin of AMM.
     * @param   ammMaxLeverage  The max leverage of AMM in the perpetual.
     * @param   slippageFactor  The slippage factor of AMM in the perpetual.
     * @return  maxPosition     The max position of AMM in the perpetual.
     */
    function getMaxPosition(
        Context memory context,
        int256 poolMargin,
        int256 ammMaxLeverage,
        int256 slippageFactor,
        bool isLongSide
    ) internal pure returns (int256 maxPosition) {
        int256 indexPrice = context.indexPrice;
        int256 beforeSqrt = poolMargin.mul(poolMargin).mul(2).sub(context.squareValue).wdiv(
            slippageFactor
        );
        if (beforeSqrt <= 0) {
            // 1. already unsafe, can't open position
            // 2. initial AMM is also this case, position = 0, available cash = 0, pool margin = 0
            return 0;
        }
        int256 maxPosition3 = beforeSqrt.sqrt().wdiv(indexPrice);
        int256 maxPosition2;
        // context.squareValue is 10^36, so use div instead of wdiv
        beforeSqrt = poolMargin.sub(context.positionMargin).add(
            context.squareValue.div(poolMargin).div(2)
        );
        beforeSqrt = beforeSqrt.wmul(ammMaxLeverage).wmul(ammMaxLeverage).wmul(slippageFactor);
        beforeSqrt = poolMargin.sub(beforeSqrt.mul(2));
        if (beforeSqrt < 0) {
            // never exceed max leverage
            maxPosition2 = type(int256).max;
        } else {
            // might be negative, clip to zero
            maxPosition2 = poolMargin.sub(beforeSqrt.mul(poolMargin).sqrt()).max(0);
            maxPosition2 = maxPosition2.wdiv(ammMaxLeverage).wdiv(slippageFactor).wdiv(indexPrice);
        }
        maxPosition = maxPosition3.min(maxPosition2);
        if (isLongSide) {
            // long side has one more restriction than short side
            int256 maxPosition1 = poolMargin.wdiv(slippageFactor).wdiv(indexPrice);
            maxPosition = maxPosition.min(maxPosition1);
        } else {
            maxPosition = maxPosition.neg();
        }
    }

    /**
     * @dev     Get pool margin of AMM, equal to 1/2 margin of AMM when AMM is unsafe.
     *          Marin of AMM: cash + index price1 * position1 + index price2 * position2 + ...
     *
     * @param   context     Context object of AMM, but current perpetual is not included.
     * @return  poolMargin  The pool margin of AMM.
     * @return  isSafe      True if AMM is safe or false.
     */
    function getPoolMargin(Context memory context)
        internal
        pure
        returns (int256 poolMargin, bool isSafe)
    {
        isSafe = isAMMSafe(context, 0);
        if (isSafe) {
            poolMargin = calculatePoolMarginWhenSafe(context, 0);
        } else {
            poolMargin = context.availableCash.add(context.positionValue).div(2);
            require(poolMargin >= 0, "pool margin is negative when getting pool margin");
        }
    }

    /**
     * @dev Get pool margin of AMM, prepare context first.
     * @param liquidityPool The liquidity pool object
     * @return int256 The pool margin of AMM
     * @return bool True if AMM is safe
     */
    function getPoolMargin(LiquidityPoolStorage storage liquidityPool)
        public
        view
        returns (int256, bool)
    {
        return getPoolMargin(prepareContext(liquidityPool));
    }
}

File 6 of 49 : LiquidityPoolModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

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

import "../interface/IAccessControl.sol";
import "../interface/IGovernor.sol";
import "../interface/IPoolCreatorFull.sol";
import "../interface/ISymbolService.sol";

import "../libraries/SafeMathExt.sol";
import "../libraries/OrderData.sol";
import "../libraries/Utils.sol";

import "./AMMModule.sol";
import "./CollateralModule.sol";
import "./MarginAccountModule.sol";
import "./PerpetualModule.sol";

import "../Type.sol";

library LiquidityPoolModule {
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using SafeMathExt for int256;
    using SafeMathExt for uint256;
    using SafeMathUpgradeable for uint256;
    using SignedSafeMathUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    using OrderData for uint32;
    using AMMModule for LiquidityPoolStorage;
    using CollateralModule for LiquidityPoolStorage;
    using MarginAccountModule for PerpetualStorage;
    using PerpetualModule for PerpetualStorage;

    uint256 public constant OPERATOR_CHECK_IN_TIMEOUT = 10 days;
    uint256 public constant MAX_PERPETUAL_COUNT = 48;

    event AddLiquidity(
        address indexed trader,
        int256 addedCash,
        int256 mintedShare,
        int256 addedPoolMargin
    );
    event RemoveLiquidity(
        address indexed trader,
        int256 returnedCash,
        int256 burnedShare,
        int256 removedPoolMargin
    );
    event UpdatePoolMargin(int256 poolMargin);
    event TransferOperatorTo(address indexed newOperator);
    event ClaimOperator(address indexed newOperator);
    event RevokeOperator();
    event SetLiquidityPoolParameter(int256[4] value);
    event CreatePerpetual(
        uint256 perpetualIndex,
        address governor,
        address shareToken,
        address operator,
        address oracle,
        address collateral,
        int256[9] baseParams,
        int256[9] riskParams
    );
    event RunLiquidityPool();
    event OperatorCheckIn(address indexed operator);
    event DonateInsuranceFund(int256 amount);
    event TransferExcessInsuranceFundToLP(int256 amount);
    event SetTargetLeverage(uint256 perpetualIndex, address indexed trader, int256 targetLeverage);
    event AddAMMKeeper(uint256 perpetualIndex, address indexed keeper);
    event RemoveAMMKeeper(uint256 perpetualIndex, address indexed keeper);
    event AddTraderKeeper(uint256 perpetualIndex, address indexed keeper);
    event RemoveTraderKeeper(uint256 perpetualIndex, address indexed keeper);

    /**
     * @dev     Get the vault's address of the liquidity pool
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @return  vault           The vault's address of the liquidity pool
     */
    function getVault(LiquidityPoolStorage storage liquidityPool)
        public
        view
        returns (address vault)
    {
        vault = IPoolCreatorFull(liquidityPool.creator).getVault();
    }

    function getShareTransferDelay(LiquidityPoolStorage storage liquidityPool)
        public
        view
        returns (uint256 delay)
    {
        delay = liquidityPool.shareTransferDelay.max(1);
    }

    function getOperator(LiquidityPoolStorage storage liquidityPool)
        internal
        view
        returns (address)
    {
        return
            block.timestamp <= liquidityPool.operatorExpiration
                ? liquidityPool.operator
                : address(0);
    }

    function getTransferringOperator(LiquidityPoolStorage storage liquidityPool)
        internal
        view
        returns (address)
    {
        return
            block.timestamp <= liquidityPool.operatorExpiration
                ? liquidityPool.transferringOperator
                : address(0);
    }

    /**
     * @dev     Get the vault fee rate of the liquidity pool
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @return  vaultFeeRate    The vault fee rate.
     */
    function getVaultFeeRate(LiquidityPoolStorage storage liquidityPool)
        public
        view
        returns (int256 vaultFeeRate)
    {
        vaultFeeRate = IPoolCreatorFull(liquidityPool.creator).getVaultFeeRate();
    }

    /**
     * @dev     Get the available pool cash(collateral) of the liquidity pool excluding the specific perpetual. Available cash
     *          in a perpetual means: margin - initial margin
     *
     * @param   liquidityPool       The reference of liquidity pool storage.
     * @param   exclusiveIndex      The index of perpetual in the liquidity pool to exclude,
     *                              set to liquidityPool.perpetualCount to skip excluding.
     * @return  availablePoolCash   The available pool cash(collateral) of the liquidity pool excluding the specific perpetual
     */
    function getAvailablePoolCash(
        LiquidityPoolStorage storage liquidityPool,
        uint256 exclusiveIndex
    ) public view returns (int256 availablePoolCash) {
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (i == exclusiveIndex || perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            int256 markPrice = perpetual.getMarkPrice();
            availablePoolCash = availablePoolCash.add(
                perpetual.getMargin(address(this), markPrice).sub(
                    perpetual.getInitialMargin(address(this), markPrice)
                )
            );
        }
        return availablePoolCash.add(liquidityPool.poolCash);
    }

    /**
     * @dev     Get the available pool cash(collateral) of the liquidity pool.
     *          Sum of available cash of AMM in every perpetual in the liquidity pool, and add the pool cash.
     *
     * @param   liquidityPool       The reference of liquidity pool storage.
     * @return  availablePoolCash   The available pool cash(collateral) of the liquidity pool
     */
    function getAvailablePoolCash(LiquidityPoolStorage storage liquidityPool)
        public
        view
        returns (int256 availablePoolCash)
    {
        return getAvailablePoolCash(liquidityPool, liquidityPool.perpetualCount);
    }

    /**
     * @dev     Check if Trader is maintenance margin safe in the perpetual.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader
     * @param   tradeAmount     The amount of positions actually traded in the transaction
     * @return  isSafe          True if Trader is maintenance margin safe in the perpetual.
     */
    function isTraderMarginSafe(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 tradeAmount
    ) public view returns (bool isSafe) {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        bool hasOpened = Utils.hasOpenedPosition(perpetual.getPosition(trader), tradeAmount);
        int256 markPrice = perpetual.getMarkPrice();
        return
            hasOpened
                ? perpetual.isInitialMarginSafe(trader, markPrice)
                : perpetual.isMarginSafe(trader, markPrice);
    }

    /**
     * @dev     Initialize the liquidity pool and set up its configuration.
     *
     * @param   liquidityPool       The reference of liquidity pool storage.
     * @param   collateral          The collateral's address of the liquidity pool.
     * @param   collateralDecimals  The collateral's decimals of the liquidity pool.
     * @param   operator            The operator's address of the liquidity pool.
     * @param   governor            The governor's address of the liquidity pool.
     * @param   initData            The byte array contains data to initialize new created liquidity pool.
     */
    function initialize(
        LiquidityPoolStorage storage liquidityPool,
        address creator,
        address collateral,
        uint256 collateralDecimals,
        address operator,
        address governor,
        bytes memory initData
    ) public {
        require(collateral != address(0), "collateral is invalid");
        require(governor != address(0), "governor is invalid");
        (
            bool isFastCreationEnabled,
            int256 insuranceFundCap,
            uint256 liquidityCap,
            uint256 shareTransferDelay
        ) = abi.decode(initData, (bool, int256, uint256, uint256));
        require(liquidityCap >= 0, "liquidity cap should be greater than 0");
        require(shareTransferDelay >= 1, "share transfer delay should be at lease 1");

        liquidityPool.initializeCollateral(collateral, collateralDecimals);
        liquidityPool.creator = creator;
        liquidityPool.accessController = IPoolCreatorFull(creator).getAccessController();

        liquidityPool.operator = operator;
        liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
        liquidityPool.governor = governor;
        liquidityPool.shareToken = governor;

        liquidityPool.isFastCreationEnabled = isFastCreationEnabled;
        liquidityPool.insuranceFundCap = insuranceFundCap;
        liquidityPool.liquidityCap = liquidityCap;
        liquidityPool.shareTransferDelay = shareTransferDelay;
    }

    /**
     * @dev     Create and initialize new perpetual in the liquidity pool. Can only called by the operator
     *          if the liquidity pool is running or isFastCreationEnabled is set to true.
     *          Otherwise can only called by the governor
     * @param   liquidityPool       The reference of liquidity pool storage.
     * @param   oracle              The oracle's address of the perpetual
     * @param   baseParams          The base parameters of the perpetual
     * @param   riskParams          The risk parameters of the perpetual, must between minimum value and maximum value
     * @param   minRiskParamValues  The risk parameters' minimum values of the perpetual
     * @param   maxRiskParamValues  The risk parameters' maximum values of the perpetual
     */
    function createPerpetual(
        LiquidityPoolStorage storage liquidityPool,
        address oracle,
        int256[9] calldata baseParams,
        int256[9] calldata riskParams,
        int256[9] calldata minRiskParamValues,
        int256[9] calldata maxRiskParamValues
    ) public {
        require(
            liquidityPool.perpetualCount < MAX_PERPETUAL_COUNT,
            "perpetual count exceeds limit"
        );
        uint256 perpetualIndex = liquidityPool.perpetualCount;
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.initialize(
            perpetualIndex,
            oracle,
            baseParams,
            riskParams,
            minRiskParamValues,
            maxRiskParamValues
        );
        ISymbolService service = ISymbolService(
            IPoolCreatorFull(liquidityPool.creator).getSymbolService()
        );
        service.allocateSymbol(address(this), perpetualIndex);
        if (liquidityPool.isRunning) {
            perpetual.setNormalState();
        }
        liquidityPool.perpetualCount++;

        emit CreatePerpetual(
            perpetualIndex,
            liquidityPool.governor,
            liquidityPool.shareToken,
            getOperator(liquidityPool),
            oracle,
            liquidityPool.collateralToken,
            baseParams,
            riskParams
        );
    }

    /**
     * @dev     Run the liquidity pool. Can only called by the operator. The operator can create new perpetual before running
     *          or after running if isFastCreationEnabled is set to true
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     */
    function runLiquidityPool(LiquidityPoolStorage storage liquidityPool) public {
        uint256 length = liquidityPool.perpetualCount;
        require(length > 0, "there should be at least 1 perpetual to run");
        for (uint256 i = 0; i < length; i++) {
            liquidityPool.perpetuals[i].setNormalState();
        }
        liquidityPool.isRunning = true;
        emit RunLiquidityPool();
    }

    /**
     * @dev     Set the parameter of the liquidity pool. Can only called by the governor.
     *
     * @param   liquidityPool  The reference of liquidity pool storage.
     * @param   params         The new value of the parameter
     */
    function setLiquidityPoolParameter(
        LiquidityPoolStorage storage liquidityPool,
        int256[4] memory params
    ) public {
        validateLiquidityPoolParameter(params);
        liquidityPool.isFastCreationEnabled = (params[0] != 0);
        liquidityPool.insuranceFundCap = params[1];
        liquidityPool.liquidityCap = uint256(params[2]);
        liquidityPool.shareTransferDelay = uint256(params[3]);
        emit SetLiquidityPoolParameter(params);
    }

    /**
     * @dev     Validate the liquidity pool parameter:
     *            1. insurance fund cap >= 0
     * @param   liquidityPoolParams  The parameters of the liquidity pool.
     */
    function validateLiquidityPoolParameter(int256[4] memory liquidityPoolParams) public pure {
        require(liquidityPoolParams[1] >= 0, "insuranceFundCap < 0");
        require(liquidityPoolParams[2] >= 0, "liquidityCap < 0");
        require(liquidityPoolParams[3] >= 1, "shareTransferDelay < 1");
    }

    /**
     * @dev     Add an account to the whitelist, accounts in the whitelist is allowed to call `liquidateByAMM`.
     *          If never called, the whitelist in poolCreator will be used instead.
     *          Once called, the local whitelist will be used and the the whitelist in poolCreator will be ignored.
     *
     * @param   keeper          The account of keeper.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     */
    function addAMMKeeper(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address keeper
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        EnumerableSetUpgradeable.AddressSet storage whitelist = liquidityPool
            .perpetuals[perpetualIndex]
            .ammKeepers;
        require(!whitelist.contains(keeper), "keeper is already added");
        bool success = whitelist.add(keeper);
        require(success, "fail to add keeper to whitelist");
        emit AddAMMKeeper(perpetualIndex, keeper);
    }

    /**
     * @dev     Remove an account from the `liquidateByAMM` whitelist.
     *
     * @param   keeper          The account of keeper.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     */
    function removeAMMKeeper(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address keeper
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        EnumerableSetUpgradeable.AddressSet storage whitelist = liquidityPool
            .perpetuals[perpetualIndex]
            .ammKeepers;
        require(whitelist.contains(keeper), "keeper is not added");
        bool success = whitelist.remove(keeper);
        require(success, "fail to remove keeper from whitelist");
        emit RemoveAMMKeeper(perpetualIndex, keeper);
    }

    function setPerpetualOracle(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address newOracle
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.setOracle(newOracle);
    }

    /**
     * @dev     Set the base parameter of the perpetual. Can only called by the governor
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     * @param   baseParams      The new value of the base parameter
     */
    function setPerpetualBaseParameter(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256[9] memory baseParams
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.setBaseParameter(baseParams);
    }

    /**
     * @dev     Set the risk parameter of the perpetual, including minimum value and maximum value.
     *          Can only called by the governor
     * @param   liquidityPool       The reference of liquidity pool storage.
     * @param   perpetualIndex      The index of perpetual in the liquidity pool
     * @param   riskParams          The new value of the risk parameter, must between minimum value and maximum value
     * @param   minRiskParamValues  The minimum value of the risk parameter
     * @param   maxRiskParamValues  The maximum value of the risk parameter
     */
    function setPerpetualRiskParameter(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256[9] memory riskParams,
        int256[9] memory minRiskParamValues,
        int256[9] memory maxRiskParamValues
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.setRiskParameter(riskParams, minRiskParamValues, maxRiskParamValues);
    }

    /**
     * @dev     Set the risk parameter of the perpetual. Can only called by the governor
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     * @param   riskParams      The new value of the risk parameter, must between minimum value and maximum value
     */
    function updatePerpetualRiskParameter(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256[9] memory riskParams
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.updateRiskParameter(riskParams);
    }

    /**
     * @dev     Set the state of the perpetual to "EMERGENCY". Must rebalance first.
     *          After that the perpetual is not allowed to trade, deposit and withdraw.
     *          The price of the perpetual is freezed to the settlement price
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     */
    function setEmergencyState(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
        public
    {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        rebalance(liquidityPool, perpetualIndex);
        liquidityPool.perpetuals[perpetualIndex].setEmergencyState();
        if (!isAnyPerpetualIn(liquidityPool, PerpetualState.NORMAL)) {
            refundDonatedInsuranceFund(liquidityPool);
        }
    }

    /**
     * @dev     Check if all the perpetuals in the liquidity pool are not in a state.
     */
    function isAnyPerpetualIn(LiquidityPoolStorage storage liquidityPool, PerpetualState state)
        internal
        view
        returns (bool)
    {
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            if (liquidityPool.perpetuals[i].state == state) {
                return true;
            }
        }
        return false;
    }

    /**
     * @dev     Check if all the perpetuals in the liquidity pool are not in normal state.
     */
    function isAllPerpetualIn(LiquidityPoolStorage storage liquidityPool, PerpetualState state)
        internal
        view
        returns (bool)
    {
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            if (liquidityPool.perpetuals[i].state != state) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev     Refund donated insurance fund to current operator.
     *           - If current operator address is non-zero, all the donated funds will be forward to the operator address;
     *           - If no operator, the donated funds will be dispatched to the LPs according to the ratio of owned shares.
     */
    function refundDonatedInsuranceFund(LiquidityPoolStorage storage liquidityPool) internal {
        address operator = getOperator(liquidityPool);
        if (liquidityPool.donatedInsuranceFund > 0 && operator != address(0)) {
            int256 toRefund = liquidityPool.donatedInsuranceFund;
            liquidityPool.donatedInsuranceFund = 0;
            liquidityPool.transferToUser(operator, toRefund);
        }
    }

    /**
     * @dev     Set the state of all the perpetuals to "EMERGENCY". Use special type of rebalance.
     *          After rebalance, pool cash >= 0 and margin / initialMargin is the same in all perpetuals.
     *          Can only called when AMM is not maintenance margin safe in all perpetuals.
     *          After that all the perpetuals are not allowed to trade, deposit and withdraw.
     *          The price of every perpetual is freezed to the settlement price
     * @param   liquidityPool   The reference of liquidity pool storage.
     */
    function setAllPerpetualsToEmergencyState(LiquidityPoolStorage storage liquidityPool) public {
        require(liquidityPool.perpetualCount > 0, "no perpetual to settle");
        int256 margin;
        int256 maintenanceMargin;
        int256 initialMargin;
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            int256 markPrice = perpetual.getMarkPrice();
            maintenanceMargin = maintenanceMargin.add(
                perpetual.getMaintenanceMargin(address(this), markPrice)
            );
            initialMargin = initialMargin.add(perpetual.getInitialMargin(address(this), markPrice));
            margin = margin.add(perpetual.getMargin(address(this), markPrice));
        }
        margin = margin.add(liquidityPool.poolCash);
        require(
            margin < maintenanceMargin ||
                IPoolCreatorFull(liquidityPool.creator).isUniverseSettled(),
            "AMM's margin >= maintenance margin or not universe settled"
        );
        // rebalance for settle all perps
        // Floor to make sure poolCash >= 0
        int256 rate = initialMargin != 0 ? margin.wdiv(initialMargin, Round.FLOOR) : 0;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            int256 markPrice = perpetual.getMarkPrice();
            // Floor to make sure poolCash >= 0
            int256 newMargin = perpetual.getInitialMargin(address(this), markPrice).wmul(
                rate,
                Round.FLOOR
            );
            margin = perpetual.getMargin(address(this), markPrice);
            int256 deltaMargin = newMargin.sub(margin);
            if (deltaMargin > 0) {
                // from pool to perp
                perpetual.updateCash(address(this), deltaMargin);
                transferFromPoolToPerpetual(liquidityPool, i, deltaMargin);
            } else if (deltaMargin < 0) {
                // from perp to pool
                perpetual.updateCash(address(this), deltaMargin);
                transferFromPerpetualToPool(liquidityPool, i, deltaMargin.neg());
            }
            liquidityPool.perpetuals[i].setEmergencyState();
        }
        require(liquidityPool.poolCash >= 0, "negative poolCash after settle all");
        refundDonatedInsuranceFund(liquidityPool);
    }

    /**
     * @dev     Set the state of the perpetual to "CLEARED". Add the collateral of AMM in the perpetual to the pool cash.
     *          Can only called when all the active accounts in the perpetual are cleared
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     */
    function setClearedState(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
        public
    {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.countMargin(address(this));
        perpetual.setClearedState();
        int256 marginToReturn = perpetual.settle(address(this));
        transferFromPerpetualToPool(liquidityPool, perpetualIndex, marginToReturn);
    }

    /**
     * @dev     Specify a new address to be operator. See transferOperator in Governance.sol.
     * @param   liquidityPool    The liquidity pool storage.
     * @param   newOperator      The address of new operator to transfer to
     */
    function transferOperator(LiquidityPoolStorage storage liquidityPool, address newOperator)
        public
    {
        require(newOperator != address(0), "new operator is invalid");
        require(newOperator != getOperator(liquidityPool), "cannot transfer to current operator");
        liquidityPool.transferringOperator = newOperator;
        liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
        emit TransferOperatorTo(newOperator);
    }

    /**
     * @dev     A lease mechanism to check if the operator is alive as the pool manager.
     *          When called the operatorExpiration will be extended according to OPERATOR_CHECK_IN_TIMEOUT.
     *          After OPERATOR_CHECK_IN_TIMEOUT, the operator will no longer be the operator.
     *          New operator will only be raised by voting.
     *          Transfer operator to another account will renew the expiration.
     *
     * @param   liquidityPool   The liquidity pool storage.
     */
    function checkIn(LiquidityPoolStorage storage liquidityPool) public {
        liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
        emit OperatorCheckIn(getOperator(liquidityPool));
    }

    /**
     * @dev  Claim the ownership of the liquidity pool to claimer. See `transferOperator` in Governance.sol.
     * @param   liquidityPool   The liquidity pool storage.
     * @param   claimer         The address of claimer
     */
    function claimOperator(LiquidityPoolStorage storage liquidityPool, address claimer) public {
        require(claimer == getTransferringOperator(liquidityPool), "caller is not qualified");
        liquidityPool.operator = claimer;
        liquidityPool.operatorExpiration = block.timestamp.add(OPERATOR_CHECK_IN_TIMEOUT);
        liquidityPool.transferringOperator = address(0);
        IPoolCreatorFull(liquidityPool.creator).registerOperatorOfLiquidityPool(
            address(this),
            claimer
        );
        emit ClaimOperator(claimer);
    }

    /**
     * @dev  Revoke operator of the liquidity pool.
     * @param   liquidityPool   The liquidity pool object
     */
    function revokeOperator(LiquidityPoolStorage storage liquidityPool) public {
        liquidityPool.operator = address(0);
        IPoolCreatorFull(liquidityPool.creator).registerOperatorOfLiquidityPool(
            address(this),
            address(0)
        );
        emit RevokeOperator();
    }

    /**
     * @dev     Update the funding state of each perpetual of the liquidity pool. Funding payment of every account in the
     *          liquidity pool is updated
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   currentTime     The current timestamp
     */
    function updateFundingState(LiquidityPoolStorage storage liquidityPool, uint256 currentTime)
        public
    {
        if (liquidityPool.fundingTime >= currentTime) {
            // invalid time
            return;
        }
        int256 timeElapsed = currentTime.sub(liquidityPool.fundingTime).toInt256();
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            perpetual.updateFundingState(timeElapsed);
        }
        liquidityPool.fundingTime = currentTime;
    }

    /**
     * @dev     Update the funding rate of each perpetual of the liquidity pool
     * @param   liquidityPool   The reference of liquidity pool storage.
     */
    function updateFundingRate(LiquidityPoolStorage storage liquidityPool) public {
        (int256 poolMargin, bool isAMMSafe) = liquidityPool.getPoolMargin();
        emit UpdatePoolMargin(poolMargin);
        if (!isAMMSafe) {
            poolMargin = 0;
        }
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            perpetual.updateFundingRate(poolMargin);
        }
    }

    /**
     * @dev     Update the oracle price of each perpetual of the liquidity pool.
     *          If oracle is terminated, set market to EMERGENCY.
     *
     * @param   liquidityPool       The liquidity pool object
     * @param   ignoreTerminated    Ignore terminated oracle if set to True.
     */
    function updatePrice(LiquidityPoolStorage storage liquidityPool, bool ignoreTerminated) public {
        uint256 length = liquidityPool.perpetualCount;
        for (uint256 i = 0; i < length; i++) {
            PerpetualStorage storage perpetual = liquidityPool.perpetuals[i];
            if (perpetual.state != PerpetualState.NORMAL) {
                continue;
            }
            perpetual.updatePrice();
            if (IOracle(perpetual.oracle).isTerminated() && !ignoreTerminated) {
                setEmergencyState(liquidityPool, perpetual.id);
            }
        }
    }

    /**
     * @dev     Donate collateral to the insurance fund of the liquidity pool to make the liquidity pool safe.
     *
     * @param   liquidityPool   The liquidity pool object
     * @param   amount          The amount of collateral to donate
     */
    function donateInsuranceFund(
        LiquidityPoolStorage storage liquidityPool,
        address donator,
        int256 amount
    ) public {
        require(amount > 0, "invalid amount");
        liquidityPool.transferFromUser(donator, amount);
        liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.add(amount);
        emit DonateInsuranceFund(amount);
    }

    /**
     * @dev     Update the collateral of the insurance fund in the liquidity pool.
     *          If the collateral of the insurance fund exceeds the cap, the extra part of collateral belongs to LP.
     *          If the collateral of the insurance fund < 0, the donated insurance fund will cover it.
     *
     * @param   liquidityPool   The liquidity pool object
     * @param   deltaFund       The update collateral amount of the insurance fund in the perpetual
     * @return  penaltyToLP     The extra part of collateral if the collateral of the insurance fund exceeds the cap
     */
    function updateInsuranceFund(LiquidityPoolStorage storage liquidityPool, int256 deltaFund)
        public
        returns (int256 penaltyToLP)
    {
        if (deltaFund != 0) {
            int256 newInsuranceFund = liquidityPool.insuranceFund.add(deltaFund);
            if (deltaFund > 0) {
                if (newInsuranceFund > liquidityPool.insuranceFundCap) {
                    penaltyToLP = newInsuranceFund.sub(liquidityPool.insuranceFundCap);
                    newInsuranceFund = liquidityPool.insuranceFundCap;
                    emit TransferExcessInsuranceFundToLP(penaltyToLP);
                }
            } else {
                if (newInsuranceFund < 0) {
                    liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.add(
                        newInsuranceFund
                    );
                    require(
                        liquidityPool.donatedInsuranceFund >= 0,
                        "negative donated insurance fund"
                    );
                    newInsuranceFund = 0;
                }
            }
            liquidityPool.insuranceFund = newInsuranceFund;
        }
    }

    /**
     * @dev     Deposit collateral to the trader's account of the perpetual. The trader's cash will increase.
     *          Activate the perpetual for the trader if the account in the perpetual is empty before depositing.
     *          Empty means cash and position are zero.
     *
     * @param   liquidityPool   The liquidity pool object
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @param   trader          The address of the trader
     * @param   amount          The amount of collateral to deposit
     */
    function deposit(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 amount
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        transferFromUserToPerpetual(liquidityPool, perpetualIndex, trader, amount);
        if (liquidityPool.perpetuals[perpetualIndex].deposit(trader, amount)) {
            IPoolCreatorFull(liquidityPool.creator).activatePerpetualFor(trader, perpetualIndex);
        }
    }

    /**
     * @dev     Withdraw collateral from the trader's account of the perpetual. The trader's cash will decrease.
     *          Trader must be initial margin safe in the perpetual after withdrawing.
     *          Deactivate the perpetual for the trader if the account in the perpetual is empty after withdrawing.
     *          Empty means cash and position are zero.
     *
     * @param   liquidityPool   The liquidity pool object
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @param   trader          The address of the trader
     * @param   amount          The amount of collateral to withdraw
     */
    function withdraw(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 amount
    ) public {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        rebalance(liquidityPool, perpetualIndex);
        if (perpetual.withdraw(trader, amount)) {
            IPoolCreatorFull(liquidityPool.creator).deactivatePerpetualFor(trader, perpetualIndex);
        }
        transferFromPerpetualToUser(liquidityPool, perpetualIndex, trader, amount);
    }

    /**
     * @dev     If the state of the perpetual is "CLEARED", anyone authorized withdraw privilege by trader can settle
     *          trader's account in the perpetual. Which means to calculate how much the collateral should be returned
     *          to the trader, return it to trader's wallet and clear the trader's cash and position in the perpetual.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     */
    function settle(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader
    ) public {
        require(trader != address(0), "invalid trader");
        int256 marginToReturn = liquidityPool.perpetuals[perpetualIndex].settle(trader);
        require(marginToReturn > 0, "no margin to settle");
        transferFromPerpetualToUser(liquidityPool, perpetualIndex, trader, marginToReturn);
    }

    /**
     * @dev     Clear the next active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
     *          to sender. If all active accounts are cleared, the clear progress is done and the perpetual's state will
     *          change to "CLEARED". Active means the trader's account is not empty in the perpetual.
     *          Empty means cash and position are zero.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     */
    function clear(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader
    ) public {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        if (
            perpetual.keeperGasReward > 0 && perpetual.totalCollateral >= perpetual.keeperGasReward
        ) {
            transferFromPerpetualToUser(
                liquidityPool,
                perpetualIndex,
                trader,
                perpetual.keeperGasReward
            );
        }
        if (
            perpetual.activeAccounts.length() == 0 ||
            perpetual.clear(perpetual.getNextActiveAccount())
        ) {
            setClearedState(liquidityPool, perpetualIndex);
        }
    }

    /**
     * @dev Add collateral to the liquidity pool and get the minted share tokens.
     *      The share token is the credential and use to get the collateral back when removing liquidity.
     *      Can only called when at least 1 perpetual is in NORMAL state.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param trader The address of the trader that adding liquidity
     * @param cashToAdd The cash(collateral) to add
     */
    function addLiquidity(
        LiquidityPoolStorage storage liquidityPool,
        address trader,
        int256 cashToAdd
    ) public {
        require(cashToAdd > 0, "cash amount must be positive");
        uint256 length = liquidityPool.perpetualCount;
        bool allowAdd;
        for (uint256 i = 0; i < length; i++) {
            if (liquidityPool.perpetuals[i].state == PerpetualState.NORMAL) {
                allowAdd = true;
                break;
            }
        }
        require(allowAdd, "all perpetuals are NOT in NORMAL state");
        liquidityPool.transferFromUser(trader, cashToAdd);

        IGovernor shareToken = IGovernor(liquidityPool.shareToken);
        int256 shareTotalSupply = shareToken.totalSupply().toInt256();

        (int256 shareToMint, int256 addedPoolMargin) = liquidityPool.getShareToMint(
            shareTotalSupply,
            cashToAdd
        );
        require(shareToMint > 0, "received share must be positive");
        // pool cash cannot be added before calculation, DO NOT use transferFromUserToPool

        increasePoolCash(liquidityPool, cashToAdd);
        shareToken.mint(trader, shareToMint.toUint256());

        emit AddLiquidity(trader, cashToAdd, shareToMint, addedPoolMargin);
    }

    /**
     * @dev     Remove collateral from the liquidity pool and redeem the share tokens when the liquidity pool is running.
     *          Only one of shareToRemove or cashToReturn may be non-zero.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   trader          The address of the trader that removing liquidity.
     * @param   shareToRemove   The amount of the share token to redeem.
     * @param   cashToReturn    The amount of cash(collateral) to return.
     */
    function removeLiquidity(
        LiquidityPoolStorage storage liquidityPool,
        address trader,
        int256 shareToRemove,
        int256 cashToReturn
    ) public {
        IGovernor shareToken = IGovernor(liquidityPool.shareToken);
        int256 shareTotalSupply = shareToken.totalSupply().toInt256();
        int256 removedInsuranceFund;
        int256 removedDonatedInsuranceFund;
        int256 removedPoolMargin;
        if (cashToReturn == 0 && shareToRemove > 0) {
            (
                cashToReturn,
                removedInsuranceFund,
                removedDonatedInsuranceFund,
                removedPoolMargin
            ) = liquidityPool.getCashToReturn(shareTotalSupply, shareToRemove);
            require(cashToReturn > 0, "cash to return must be positive");
        } else if (cashToReturn > 0 && shareToRemove == 0) {
            (
                shareToRemove,
                removedInsuranceFund,
                removedDonatedInsuranceFund,
                removedPoolMargin
            ) = liquidityPool.getShareToRemove(shareTotalSupply, cashToReturn);
            require(shareToRemove > 0, "share to remove must be positive");
        } else {
            revert("invalid parameter");
        }
        require(
            shareToRemove.toUint256() <= shareToken.balanceOf(trader),
            "insufficient share balance"
        );
        int256 removedCashFromPool = cashToReturn.sub(removedInsuranceFund).sub(
            removedDonatedInsuranceFund
        );
        require(
            removedCashFromPool <= getAvailablePoolCash(liquidityPool),
            "insufficient pool cash"
        );
        shareToken.burn(trader, shareToRemove.toUint256());

        liquidityPool.transferToUser(trader, cashToReturn);
        liquidityPool.insuranceFund = liquidityPool.insuranceFund.sub(removedInsuranceFund);
        liquidityPool.donatedInsuranceFund = liquidityPool.donatedInsuranceFund.sub(
            removedDonatedInsuranceFund
        );
        decreasePoolCash(liquidityPool, removedCashFromPool);
        emit RemoveLiquidity(trader, cashToReturn, shareToRemove, removedPoolMargin);
    }

    /**
     * @dev     Add collateral to the liquidity pool without getting share tokens.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   trader          The address of the trader that adding liquidity
     * @param   cashToAdd       The cash(collateral) to add
     */
    function donateLiquidity(
        LiquidityPoolStorage storage liquidityPool,
        address trader,
        int256 cashToAdd
    ) public {
        require(cashToAdd > 0, "cash amount must be positive");
        (, int256 addedPoolMargin) = liquidityPool.getShareToMint(0, cashToAdd);
        liquidityPool.transferFromUser(trader, cashToAdd);
        // pool cash cannot be added before calculation, DO NOT use transferFromUserToPool
        increasePoolCash(liquidityPool, cashToAdd);
        emit AddLiquidity(trader, cashToAdd, 0, addedPoolMargin);
    }

    /**
     * @dev     To keep the AMM's margin equal to initial margin in the perpetual as possible.
     *          Transfer collateral between the perpetual and the liquidity pool's cash, then
     *          update the AMM's cash in perpetual. The liquidity pool's cash can be negative,
     *          but the available cash can't. If AMM need to transfer and the available cash
     *          is not enough, transfer all the rest available cash of collateral.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @return  The amount of rebalanced margin. A positive amount indicates the collaterals
     *          are moved from perpetual to pool, and a negative amount indicates the opposite.
     *          0 means no rebalance happened.
     */
    function rebalance(LiquidityPoolStorage storage liquidityPool, uint256 perpetualIndex)
        public
        returns (int256)
    {
        require(perpetualIndex < liquidityPool.perpetualCount, "perpetual index out of range");
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        if (perpetual.state != PerpetualState.NORMAL) {
            return 0;
        }
        int256 rebalanceMargin = perpetual.getRebalanceMargin();
        if (rebalanceMargin == 0) {
            // nothing to rebalance
            return 0;
        } else if (rebalanceMargin > 0) {
            // from perp to pool
            rebalanceMargin = rebalanceMargin.min(perpetual.totalCollateral);
            perpetual.updateCash(address(this), rebalanceMargin.neg());
            transferFromPerpetualToPool(liquidityPool, perpetualIndex, rebalanceMargin);
        } else {
            // from pool to perp
            int256 availablePoolCash = getAvailablePoolCash(liquidityPool, perpetualIndex);
            if (availablePoolCash <= 0) {
                // pool has no more collateral, nothing to rebalance
                return 0;
            }
            rebalanceMargin = rebalanceMargin.abs().min(availablePoolCash);
            perpetual.updateCash(address(this), rebalanceMargin);
            transferFromPoolToPerpetual(liquidityPool, perpetualIndex, rebalanceMargin);
        }
        return rebalanceMargin;
    }

    /**
     * @dev     Increase the liquidity pool's cash(collateral).
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   amount          The amount of cash(collateral) to increase.
     */
    function increasePoolCash(LiquidityPoolStorage storage liquidityPool, int256 amount) internal {
        require(amount >= 0, "increase negative pool cash");
        liquidityPool.poolCash = liquidityPool.poolCash.add(amount);
    }

    /**
     * @dev     Decrease the liquidity pool's cash(collateral).
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   amount          The amount of cash(collateral) to decrease.
     */
    function decreasePoolCash(LiquidityPoolStorage storage liquidityPool, int256 amount) internal {
        require(amount >= 0, "decrease negative pool cash");
        liquidityPool.poolCash = liquidityPool.poolCash.sub(amount);
    }

    // user <=> pool (addLiquidity/removeLiquidity)
    function transferFromUserToPool(
        LiquidityPoolStorage storage liquidityPool,
        address account,
        int256 amount
    ) public {
        liquidityPool.transferFromUser(account, amount);
        increasePoolCash(liquidityPool, amount);
    }

    function transferFromPoolToUser(
        LiquidityPoolStorage storage liquidityPool,
        address account,
        int256 amount
    ) public {
        if (amount == 0) {
            return;
        }
        liquidityPool.transferToUser(account, amount);
        decreasePoolCash(liquidityPool, amount);
    }

    // user <=> perpetual (deposit/withdraw)
    function transferFromUserToPerpetual(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address account,
        int256 amount
    ) public {
        liquidityPool.transferFromUser(account, amount);
        liquidityPool.perpetuals[perpetualIndex].increaseTotalCollateral(amount);
    }

    function transferFromPerpetualToUser(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address account,
        int256 amount
    ) public {
        if (amount == 0) {
            return;
        }
        liquidityPool.transferToUser(account, amount);
        liquidityPool.perpetuals[perpetualIndex].decreaseTotalCollateral(amount);
    }

    // pool <=> perpetual (fee/rebalance)
    function transferFromPerpetualToPool(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256 amount
    ) public {
        if (amount == 0) {
            return;
        }
        liquidityPool.perpetuals[perpetualIndex].decreaseTotalCollateral(amount);
        increasePoolCash(liquidityPool, amount);
    }

    function transferFromPoolToPerpetual(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        int256 amount
    ) public {
        if (amount == 0) {
            return;
        }
        liquidityPool.perpetuals[perpetualIndex].increaseTotalCollateral(amount);
        decreasePoolCash(liquidityPool, amount);
    }

    /**
     * @dev Check if the trader is authorized the privilege by the grantee. Any trader is authorized by himself
     * @param liquidityPool The reference of liquidity pool storage.
     * @param trader The address of the trader
     * @param grantee The address of the grantee
     * @param privilege The privilege
     * @return isGranted True if the trader is authorized
     */
    function isAuthorized(
        LiquidityPoolStorage storage liquidityPool,
        address trader,
        address grantee,
        uint256 privilege
    ) public view returns (bool isGranted) {
        isGranted =
            trader == grantee ||
            IAccessControl(liquidityPool.accessController).isGranted(trader, grantee, privilege);
    }

    /**
     * @dev     Deposit or withdraw to let effective leverage == target leverage
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     * @param   deltaPosition   The update position of the trader's account in the perpetual.
     * @param   deltaCash       The update cash(collateral) of the trader's account in the perpetual.
     * @param   totalFee        The total fee collected from the trader after the trade.
     * @param   flags           The flags of the trade.
     */
    function adjustMarginLeverage(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 deltaPosition,
        int256 deltaCash,
        int256 totalFee,
        uint32 flags
    ) public {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        // read perp
        int256 position = perpetual.getPosition(trader);
        int256 adjustCollateral;
        (int256 closePosition, int256 openPosition) = Utils.splitAmount(
            position.sub(deltaPosition),
            deltaPosition
        );
        if (closePosition != 0 && openPosition == 0) {
            // close only
            adjustCollateral = adjustClosedMargin(
                perpetual,
                trader,
                closePosition,
                deltaCash,
                totalFee
            );
        } else {
            // open only or close + open
            adjustCollateral = adjustOpenedMargin(
                perpetual,
                trader,
                deltaPosition,
                deltaCash,
                closePosition,
                openPosition,
                totalFee,
                flags
            );
        }
        // real deposit/withdraw
        if (adjustCollateral > 0) {
            deposit(liquidityPool, perpetualIndex, trader, adjustCollateral);
        } else if (adjustCollateral < 0) {
            withdraw(liquidityPool, perpetualIndex, trader, adjustCollateral.neg());
        }
    }

    function adjustClosedMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 closePosition,
        int256 deltaCash,
        int256 totalFee
    ) public view returns (int256 adjustCollateral) {
        int256 markPrice = perpetual.getMarkPrice();
        int256 position2 = perpetual.getPosition(trader);
        if (position2 == 0) {
            // close all, withdraw all
            return perpetual.getAvailableCash(trader).neg().min(0);
        }
        // when close, keep the margin ratio
        // -withdraw == (availableCash2 * close - (deltaCash - fee) * position2 + reservedValue) / position1
        // reservedValue = 0 if position2 == 0 else keeperGasReward * (-deltaPos)
        adjustCollateral = perpetual.getAvailableCash(trader).wmul(closePosition);
        adjustCollateral = adjustCollateral.sub(deltaCash.sub(totalFee).wmul(position2));
        if (position2 != 0) {
            adjustCollateral = adjustCollateral.sub(perpetual.keeperGasReward.wmul(closePosition));
        }
        adjustCollateral = adjustCollateral.wdiv(position2.sub(closePosition));
        // withdraw only when IM is satisfied
        adjustCollateral = adjustCollateral.max(
            perpetual.getAvailableMargin(trader, markPrice).neg()
        );
        // never deposit when close positions
        adjustCollateral = adjustCollateral.min(0);
    }

    // open only or close + open
    function adjustOpenedMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 deltaPosition,
        int256 deltaCash,
        int256 closePosition,
        int256 openPosition,
        int256 totalFee,
        uint32 flags
    ) public view returns (int256 adjustCollateral) {
        int256 markPrice = perpetual.getMarkPrice();
        int256 oldMargin = perpetual.getMargin(trader, markPrice);
        {
            int256 leverage = perpetual.getTargetLeverageWithFlags(trader, flags);
            require(leverage > 0, "target leverage = 0");
            // openPositionMargin
            adjustCollateral = openPosition.abs().wfrac(markPrice, leverage);
        }
        if (perpetual.getPosition(trader).sub(deltaPosition) != 0 && closePosition == 0) {
            // open from non-zero position
            // adjustCollateral = openPositionMargin + fee - pnl
            adjustCollateral = adjustCollateral
                .add(totalFee)
                .sub(markPrice.wmul(deltaPosition))
                .sub(deltaCash);
        } else {
            // open from 0 or close + open
            adjustCollateral = adjustCollateral.add(perpetual.keeperGasReward).sub(oldMargin);
        }
        // make sure after adjust: trader is initial margin safe
        adjustCollateral = adjustCollateral.max(
            perpetual.getAvailableMargin(trader, markPrice).neg()
        );
    }

    // deprecated
    function setTargetLeverage(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 targetLeverage
    ) public {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
        require(
            targetLeverage != perpetual.marginAccounts[trader].targetLeverage,
            "targetLeverage is already set"
        );
        int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
        require(targetLeverage <= maxLeverage, "targetLeverage exceeds maxLeverage");
        perpetual.setTargetLeverage(trader, targetLeverage);
        emit SetTargetLeverage(perpetualIndex, trader, targetLeverage);
    }

    // A readonly version of adjustMarginLeverage. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyAdjustMarginLeverage(
        PerpetualStorage storage perpetual,
        MarginAccount memory trader,
        int256 deltaPosition,
        int256 deltaCash,
        int256 totalFee,
        uint32 flags
    ) public view returns (int256 adjustCollateral) {
        // read perp
        (int256 closePosition, int256 openPosition) = Utils.splitAmount(
            trader.position.sub(deltaPosition),
            deltaPosition
        );
        if (closePosition != 0 && openPosition == 0) {
            // close only
            adjustCollateral = readonlyAdjustClosedMargin(
                perpetual,
                trader,
                closePosition,
                deltaCash,
                totalFee
            );
        } else {
            // open only or close + open
            adjustCollateral = readonlyAdjustOpenedMargin(
                perpetual,
                trader,
                deltaPosition,
                deltaCash,
                closePosition,
                openPosition,
                totalFee,
                flags
            );
        }
    }

    // A readonly version of adjustClosedMargin. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyAdjustClosedMargin(
        PerpetualStorage storage perpetual,
        MarginAccount memory trader,
        int256 closePosition,
        int256 deltaCash,
        int256 totalFee
    ) public view returns (int256 adjustCollateral) {
        int256 markPrice = perpetual.getMarkPrice();
        int256 position2 = trader.position;
        // was perpetual.getAvailableCash(trader)
        adjustCollateral = trader.cash.sub(position2.wmul(perpetual.unitAccumulativeFunding));
        if (position2 == 0) {
            // close all, withdraw all
            return adjustCollateral.neg().min(0);
        }
        // was adjustClosedMargin
        adjustCollateral = adjustCollateral.wmul(closePosition);
        adjustCollateral = adjustCollateral.sub(deltaCash.sub(totalFee).wmul(position2));
        if (position2 != 0) {
            adjustCollateral = adjustCollateral.sub(perpetual.keeperGasReward.wmul(closePosition));
        }
        adjustCollateral = adjustCollateral.wdiv(position2.sub(closePosition));
        // withdraw only when IM is satisfied
        adjustCollateral = adjustCollateral.max(
            readonlyGetAvailableMargin(perpetual, trader, markPrice).neg()
        );
        // never deposit when close positions
        adjustCollateral = adjustCollateral.min(0);
    }

    // A readonly version of adjustOpenedMargin. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyAdjustOpenedMargin(
        PerpetualStorage storage perpetual,
        MarginAccount memory trader,
        int256 deltaPosition,
        int256 deltaCash,
        int256 closePosition,
        int256 openPosition,
        int256 totalFee,
        uint32 flags
    ) public view returns (int256 adjustCollateral) {
        int256 markPrice = perpetual.getMarkPrice();
        int256 oldMargin = readonlyGetMargin(perpetual, trader, markPrice);
        // was perpetual.getTargetLeverageWithFlags
        int256 leverage;
        {
            bool _oldUseTargetLeverage = flags.oldUseTargetLeverage();
            bool _newUseTargetLeverage = flags.newUseTargetLeverage();
            require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
            if (_oldUseTargetLeverage) {
                leverage = trader.targetLeverage;
            } else {
                leverage = flags.getTargetLeverageByFlags();
            }
            require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
            int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
            leverage = leverage == 0 ? perpetual.defaultTargetLeverage.value : leverage;
            leverage = leverage.min(maxLeverage);
        }
        require(leverage > 0, "target leverage = 0");
        // openPositionMargin
        adjustCollateral = openPosition.abs().wfrac(markPrice, leverage);
        if (trader.position.sub(deltaPosition) != 0 && closePosition == 0) {
            // open from non-zero position
            // adjustCollateral = openPositionMargin + fee - pnl
            adjustCollateral = adjustCollateral
                .add(totalFee)
                .sub(markPrice.wmul(deltaPosition))
                .sub(deltaCash);
        } else {
            // open from 0 or close + open
            adjustCollateral = adjustCollateral.add(perpetual.keeperGasReward).sub(oldMargin);
        }
        // make sure after adjust: trader is initial margin safe
        adjustCollateral = adjustCollateral.max(
            readonlyGetAvailableMargin(perpetual, trader, markPrice).neg()
        );
    }

    // A readonly version of getMargin. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyGetMargin(
        PerpetualStorage storage perpetual,
        MarginAccount memory account,
        int256 price
    ) public view returns (int256 margin) {
        margin = account.position.wmul(price).add(
            account.cash.sub(account.position.wmul(perpetual.unitAccumulativeFunding))
        );
    }

    // A readonly version of getAvailableMargin. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyGetAvailableMargin(
        PerpetualStorage storage perpetual,
        MarginAccount memory account,
        int256 price
    ) public view returns (int256 availableMargin) {
        int256 threshold = account.position == 0
            ? 0 // was getInitialMargin
            : account.position.wmul(price).wmul(perpetual.initialMarginRate).abs().add(
                perpetual.keeperGasReward
            );
        // was getAvailableMargin
        availableMargin = readonlyGetMargin(perpetual, account, price).sub(threshold);
    }
}

File 7 of 49 : PerpetualModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";

import "../interface/IOracle.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";
import "../Type.sol";
import "./MarginAccountModule.sol";

library PerpetualModule {
    using SafeMathUpgradeable for uint256;
    using SafeMathExt for int256;
    using SafeCastUpgradeable for int256;
    using SafeCastUpgradeable for uint256;
    using AddressUpgradeable for address;
    using SignedSafeMathUpgradeable for int256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using MarginAccountModule for PerpetualStorage;

    int256 constant FUNDING_INTERVAL = 3600 * 8;

    uint256 internal constant INDEX_INITIAL_MARGIN_RATE = 0;
    uint256 internal constant INDEX_MAINTENANCE_MARGIN_RATE = 1;
    uint256 internal constant INDEX_OPERATOR_FEE_RATE = 2;
    uint256 internal constant INDEX_LP_FEE_RATE = 3;
    uint256 internal constant INDEX_REFERRAL_REBATE_RATE = 4;
    uint256 internal constant INDEX_LIQUIDATION_PENALTY_RATE = 5;
    uint256 internal constant INDEX_KEEPER_GAS_REWARD = 6;
    uint256 internal constant INDEX_INSURANCE_FUND_RATE = 7;
    uint256 internal constant INDEX_MAX_OPEN_INTEREST_RATE = 8;

    uint256 internal constant INDEX_HALF_SPREAD = 0;
    uint256 internal constant INDEX_OPEN_SLIPPAGE_FACTOR = 1;
    uint256 internal constant INDEX_CLOSE_SLIPPAGE_FACTOR = 2;
    uint256 internal constant INDEX_FUNDING_RATE_LIMIT = 3;
    uint256 internal constant INDEX_AMM_MAX_LEVERAGE = 4;
    uint256 internal constant INDEX_AMM_CLOSE_PRICE_DISCOUNT = 5;
    uint256 internal constant INDEX_FUNDING_RATE_FACTOR = 6;
    uint256 internal constant INDEX_DEFAULT_TARGET_LEVERAGE = 7;
    uint256 internal constant INDEX_BASE_FUNDING_RATE = 8;

    event Deposit(uint256 perpetualIndex, address indexed trader, int256 amount);
    event Withdraw(uint256 perpetualIndex, address indexed trader, int256 amount);
    event Clear(uint256 perpetualIndex, address indexed trader);
    event Settle(uint256 perpetualIndex, address indexed trader, int256 amount);
    event SetNormalState(uint256 perpetualIndex);
    event SetEmergencyState(uint256 perpetualIndex, int256 settlementPrice, uint256 settlementTime);
    event SetClearedState(uint256 perpetualIndex);
    event UpdateUnitAccumulativeFunding(uint256 perpetualIndex, int256 unitAccumulativeFunding);
    event SetPerpetualBaseParameter(uint256 perpetualIndex, int256[9] baseParams);
    event SetPerpetualRiskParameter(
        uint256 perpetualIndex,
        int256[9] riskParams,
        int256[9] minRiskParamValues,
        int256[9] maxRiskParamValues
    );
    event UpdatePerpetualRiskParameter(uint256 perpetualIndex, int256[9] riskParams);
    event SetOracle(uint256 perpetualIndex, address indexed oldOracle, address indexed newOracle);
    event UpdatePrice(
        uint256 perpetualIndex,
        address indexed oracle,
        int256 markPrice,
        uint256 markPriceUpdateTime,
        int256 indexPrice,
        uint256 indexPriceUpdateTime
    );
    event UpdateFundingRate(uint256 perpetualIndex, int256 fundingRate);

    /**
     * @dev     Get the mark price of the perpetual. If the state of the perpetual is not "NORMAL",
     *          return the settlement price
     * @param   perpetual   The reference of perpetual storage.
     * @return  markPrice   The mark price of current perpetual.
     */
    function getMarkPrice(PerpetualStorage storage perpetual)
        internal
        view
        returns (int256 markPrice)
    {
        markPrice = perpetual.state == PerpetualState.NORMAL
            ? perpetual.markPriceData.price
            : perpetual.settlementPriceData.price;
    }

    /**
     * @dev     Get the index price of the perpetual. If the state of the perpetual is not "NORMAL",
     *          return the settlement price
     * @param   perpetual   The reference of perpetual storage.
     * @return  indexPrice  The index price of current perpetual.
     */
    function getIndexPrice(PerpetualStorage storage perpetual)
        internal
        view
        returns (int256 indexPrice)
    {
        indexPrice = perpetual.state == PerpetualState.NORMAL
            ? perpetual.indexPriceData.price
            : perpetual.settlementPriceData.price;
    }

    /**
     * @dev     Get the margin to rebalance in the perpetual.
     *          Margin to rebalance = margin - initial margin
     * @param   perpetual The perpetual object
     * @return  marginToRebalance The margin to rebalance in the perpetual
     */
    function getRebalanceMargin(PerpetualStorage storage perpetual)
        public
        view
        returns (int256 marginToRebalance)
    {
        int256 price = getMarkPrice(perpetual);
        marginToRebalance = perpetual.getMargin(address(this), price).sub(
            perpetual.getInitialMargin(address(this), price)
        );
    }

    /**
     * @dev     Initialize the perpetual. Set up its configuration and validate parameters.
     *          If the validation passed, set the state of perpetual to "INITIALIZING"
     *          [minRiskParamValues, maxRiskParamValues] represents the range that the operator could
     *          update directly without proposal.
     *
     * @param   perpetual           The reference of perpetual storage.
     * @param   id                  The id of the perpetual (currently the index of perpetual)
     * @param   oracle              The address of oracle contract.
     * @param   baseParams          An int array of base parameter values.
     * @param   riskParams          An int array of risk parameter values.
     * @param   minRiskParamValues  An int array of minimal risk parameter values.
     * @param   maxRiskParamValues  An int array of maximum risk parameter values.
     */
    function initialize(
        PerpetualStorage storage perpetual,
        uint256 id,
        address oracle,
        int256[9] calldata baseParams,
        int256[9] calldata riskParams,
        int256[9] calldata minRiskParamValues,
        int256[9] calldata maxRiskParamValues
    ) public {
        perpetual.id = id;
        setOracle(perpetual, oracle);
        setBaseParameter(perpetual, baseParams);
        setRiskParameter(perpetual, riskParams, minRiskParamValues, maxRiskParamValues);
        perpetual.state = PerpetualState.INITIALIZING;
    }

    /**
     * @dev     Set oracle address of perpetual. New oracle must be different from the old one.
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   newOracle   The address of new oracle contract.
     */
    function setOracle(PerpetualStorage storage perpetual, address newOracle) public {
        require(newOracle != perpetual.oracle, "oracle not changed");
        validateOracle(newOracle);
        emit SetOracle(perpetual.id, perpetual.oracle, newOracle);
        perpetual.oracle = newOracle;
    }

    /**
     * @dev     Set the base parameter of the perpetual. Can only called by the governor
     * @param   perpetual   The perpetual object
     * @param   baseParams  The new value of the base parameter
     */
    function setBaseParameter(PerpetualStorage storage perpetual, int256[9] memory baseParams)
        public
    {
        validateBaseParameters(perpetual, baseParams);
        perpetual.initialMarginRate = baseParams[INDEX_INITIAL_MARGIN_RATE];
        perpetual.maintenanceMarginRate = baseParams[INDEX_MAINTENANCE_MARGIN_RATE];
        perpetual.operatorFeeRate = baseParams[INDEX_OPERATOR_FEE_RATE];
        perpetual.lpFeeRate = baseParams[INDEX_LP_FEE_RATE];
        perpetual.referralRebateRate = baseParams[INDEX_REFERRAL_REBATE_RATE];
        perpetual.liquidationPenaltyRate = baseParams[INDEX_LIQUIDATION_PENALTY_RATE];
        perpetual.keeperGasReward = baseParams[INDEX_KEEPER_GAS_REWARD];
        perpetual.insuranceFundRate = baseParams[INDEX_INSURANCE_FUND_RATE];
        perpetual.maxOpenInterestRate = baseParams[INDEX_MAX_OPEN_INTEREST_RATE];
        emit SetPerpetualBaseParameter(perpetual.id, baseParams);
    }

    /**
     * @dev     Set the risk parameter of the perpetual. New parameters will be validate first to apply.
     *          Using group set instead of one-by-one set to avoid revert due to constrains between values.
     *
     * @param   perpetual           The reference of perpetual storage.
     * @param   riskParams          An int array of risk parameter values.
     * @param   minRiskParamValues  An int array of minimal risk parameter values.
     * @param   maxRiskParamValues  An int array of maximum risk parameter values.
     */
    function setRiskParameter(
        PerpetualStorage storage perpetual,
        int256[9] memory riskParams,
        int256[9] memory minRiskParamValues,
        int256[9] memory maxRiskParamValues
    ) public {
        validateRiskParameters(perpetual, riskParams);
        setOption(
            perpetual.halfSpread,
            riskParams[INDEX_HALF_SPREAD],
            minRiskParamValues[INDEX_HALF_SPREAD],
            maxRiskParamValues[INDEX_HALF_SPREAD]
        );
        setOption(
            perpetual.openSlippageFactor,
            riskParams[INDEX_OPEN_SLIPPAGE_FACTOR],
            minRiskParamValues[INDEX_OPEN_SLIPPAGE_FACTOR],
            maxRiskParamValues[INDEX_OPEN_SLIPPAGE_FACTOR]
        );
        setOption(
            perpetual.closeSlippageFactor,
            riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR],
            minRiskParamValues[INDEX_CLOSE_SLIPPAGE_FACTOR],
            maxRiskParamValues[INDEX_CLOSE_SLIPPAGE_FACTOR]
        );
        setOption(
            perpetual.fundingRateLimit,
            riskParams[INDEX_FUNDING_RATE_LIMIT],
            minRiskParamValues[INDEX_FUNDING_RATE_LIMIT],
            maxRiskParamValues[INDEX_FUNDING_RATE_LIMIT]
        );
        setOption(
            perpetual.ammMaxLeverage,
            riskParams[INDEX_AMM_MAX_LEVERAGE],
            minRiskParamValues[INDEX_AMM_MAX_LEVERAGE],
            maxRiskParamValues[INDEX_AMM_MAX_LEVERAGE]
        );
        setOption(
            perpetual.maxClosePriceDiscount,
            riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT],
            minRiskParamValues[INDEX_AMM_CLOSE_PRICE_DISCOUNT],
            maxRiskParamValues[INDEX_AMM_CLOSE_PRICE_DISCOUNT]
        );
        setOption(
            perpetual.fundingRateFactor,
            riskParams[INDEX_FUNDING_RATE_FACTOR],
            minRiskParamValues[INDEX_FUNDING_RATE_FACTOR],
            maxRiskParamValues[INDEX_FUNDING_RATE_FACTOR]
        );
        setOption(
            perpetual.defaultTargetLeverage,
            riskParams[INDEX_DEFAULT_TARGET_LEVERAGE],
            minRiskParamValues[INDEX_DEFAULT_TARGET_LEVERAGE],
            maxRiskParamValues[INDEX_DEFAULT_TARGET_LEVERAGE]
        );
        setOption(
            perpetual.baseFundingRate,
            riskParams[INDEX_BASE_FUNDING_RATE],
            minRiskParamValues[INDEX_BASE_FUNDING_RATE],
            maxRiskParamValues[INDEX_BASE_FUNDING_RATE]
        );
        emit SetPerpetualRiskParameter(
            perpetual.id,
            riskParams,
            minRiskParamValues,
            maxRiskParamValues
        );
    }

    /**
     * @dev     Adjust the risk parameter. New values should always satisfied the constrains and min/max limit.
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   riskParams  An int array of risk parameter values.
     */
    function updateRiskParameter(PerpetualStorage storage perpetual, int256[9] memory riskParams)
        public
    {
        validateRiskParameters(perpetual, riskParams);
        updateOption(perpetual.halfSpread, riskParams[INDEX_HALF_SPREAD]);
        updateOption(perpetual.openSlippageFactor, riskParams[INDEX_OPEN_SLIPPAGE_FACTOR]);
        updateOption(perpetual.closeSlippageFactor, riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR]);
        updateOption(perpetual.fundingRateLimit, riskParams[INDEX_FUNDING_RATE_LIMIT]);
        updateOption(perpetual.ammMaxLeverage, riskParams[INDEX_AMM_MAX_LEVERAGE]);
        updateOption(perpetual.maxClosePriceDiscount, riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT]);
        updateOption(perpetual.fundingRateFactor, riskParams[INDEX_FUNDING_RATE_FACTOR]);
        updateOption(perpetual.defaultTargetLeverage, riskParams[INDEX_DEFAULT_TARGET_LEVERAGE]);
        updateOption(perpetual.baseFundingRate, riskParams[INDEX_BASE_FUNDING_RATE]);
        emit UpdatePerpetualRiskParameter(perpetual.id, riskParams);
    }

    /**
     * @dev     Update the unitAccumulativeFunding variable in perpetual.
     *          After that, funding payment of every account in the perpetual is updated,
     *
     *          nextUnitAccumulativeFunding = unitAccumulativeFunding
     *                                       + index * fundingRate * elapsedTime / fundingInterval
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   timeElapsed The elapsed time since last update.
     */
    function updateFundingState(PerpetualStorage storage perpetual, int256 timeElapsed) public {
        int256 deltaUnitLoss = timeElapsed
            .mul(getIndexPrice(perpetual))
            .wmul(perpetual.fundingRate)
            .div(FUNDING_INTERVAL);
        perpetual.unitAccumulativeFunding = perpetual.unitAccumulativeFunding.add(deltaUnitLoss);
        emit UpdateUnitAccumulativeFunding(perpetual.id, perpetual.unitAccumulativeFunding);
    }

    /**
     * @dev     Update the funding rate of the perpetual.
     *
     *            - funding rate = - index * position * limit / pool margin
     *            - funding rate = (+/-)limit when
     *                - pool margin = 0 and position != 0
     *                - abs(funding rate) > limit
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   poolMargin  The pool margin of liquidity pool.
     */
    function updateFundingRate(PerpetualStorage storage perpetual, int256 poolMargin) public {
        int256 position = perpetual.getPosition(address(this));
        int256 newFundingRate;
        if (
            ((perpetual.baseFundingRate.value > 0 && position <= 0) ||
                (perpetual.baseFundingRate.value < 0 && position >= 0)) &&
            perpetual.openInterest != 0
        ) {
            newFundingRate = perpetual.baseFundingRate.value;
        } else {
            newFundingRate = 0;
        }
        if (position != 0) {
            int256 fundingRateLimit = perpetual.fundingRateLimit.value;
            if (poolMargin != 0) {
                newFundingRate = newFundingRate.add(
                    getIndexPrice(perpetual).wfrac(position, poolMargin).neg().wmul(
                        perpetual.fundingRateFactor.value
                    )
                );
                newFundingRate = newFundingRate.min(fundingRateLimit).max(fundingRateLimit.neg());
            } else if (position > 0) {
                newFundingRate = fundingRateLimit.neg();
            } else {
                newFundingRate = fundingRateLimit;
            }
        }
        perpetual.fundingRate = newFundingRate;
        emit UpdateFundingRate(perpetual.id, newFundingRate);
    }

    /**
     * @dev     Update the oracle price of the perpetual, including the index price and the mark price
     * @param   perpetual   The reference of perpetual storage.
     */
    function updatePrice(PerpetualStorage storage perpetual) internal {
        IOracle oracle = IOracle(perpetual.oracle);
        updatePriceData(perpetual.markPriceData, oracle.priceTWAPLong);
        updatePriceData(perpetual.indexPriceData, oracle.priceTWAPShort);
        emit UpdatePrice(
            perpetual.id,
            address(oracle),
            perpetual.markPriceData.price,
            perpetual.markPriceData.time,
            perpetual.indexPriceData.price,
            perpetual.indexPriceData.time
        );
    }

    /**
     * @dev     Set the state of the perpetual to "NORMAL". The state must be "INITIALIZING" before
     * @param   perpetual   The reference of perpetual storage.
     */
    function setNormalState(PerpetualStorage storage perpetual) public {
        require(
            perpetual.state == PerpetualState.INITIALIZING,
            "perpetual should be in initializing state"
        );
        perpetual.state = PerpetualState.NORMAL;
        emit SetNormalState(perpetual.id);
    }

    /**
     * @dev     Set the state of the perpetual to "EMERGENCY". The state must be "NORMAL" before.
     *          The settlement price is the mark price at this time
     * @param   perpetual   The reference of perpetual storage.
     */
    function setEmergencyState(PerpetualStorage storage perpetual) public {
        require(perpetual.state == PerpetualState.NORMAL, "perpetual should be in NORMAL state");
        // use mark price as final price when emergency
        perpetual.settlementPriceData = perpetual.markPriceData;
        perpetual.totalAccount = perpetual.activeAccounts.length();
        perpetual.state = PerpetualState.EMERGENCY;
        emit SetEmergencyState(
            perpetual.id,
            perpetual.settlementPriceData.price,
            perpetual.settlementPriceData.time
        );
    }

    /**
     * @dev     Set the state of the perpetual to "CLEARED". The state must be "EMERGENCY" before.
     *          And settle the collateral of the perpetual, which means
     *          determining how much collateral should returned to every account.
     * @param   perpetual   The reference of perpetual storage.
     */
    function setClearedState(PerpetualStorage storage perpetual) public {
        require(
            perpetual.state == PerpetualState.EMERGENCY,
            "perpetual should be in emergency state"
        );
        settleCollateral(perpetual);
        perpetual.state = PerpetualState.CLEARED;
        emit SetClearedState(perpetual.id);
    }

    /**
     * @dev     Deposit collateral to the trader's account of the perpetual, that will increase the cash amount in
     *          trader's margin account.
     *
     *          If this is the first time the trader deposits in current perpetual, the address of trader will be
     *          push to a list, then the trader is defined as an 'Active' trader for this perpetual.
     *          List of active traders will be used during clearing.
     *
     * @param   perpetual           The reference of perpetual storage.
     * @param   trader              The address of the trader.
     * @param   amount              The amount of collateral to deposit.
     * @return  isInitialDeposit    True if the trader's account is empty before depositing.
     */
    function deposit(
        PerpetualStorage storage perpetual,
        address trader,
        int256 amount
    ) public returns (bool isInitialDeposit) {
        require(amount > 0, "amount should greater than 0");
        perpetual.updateCash(trader, amount);
        isInitialDeposit = registerActiveAccount(perpetual, trader);
        emit Deposit(perpetual.id, trader, amount);
    }

    /**
     * @dev     Withdraw collateral from the trader's account of the perpetual, that will increase the cash amount in
     *          trader's margin account.
     *
     *          Trader must be initial margin safe in the perpetual after withdrawing.
     *          Making the margin account 'Empty' will mark this account as a 'Deactive' trader then be removed from
     *          list of active traders.
     *
     * @param   perpetual           The reference of perpetual storage.
     * @param   trader              The address of the trader.
     * @param   amount              The amount of collateral to withdraw.
     * @return  isLastWithdrawal    True if the trader's account is empty after withdrawing.
     */
    function withdraw(
        PerpetualStorage storage perpetual,
        address trader,
        int256 amount
    ) public returns (bool isLastWithdrawal) {
        require(
            perpetual.getPosition(trader) == 0 || !IOracle(perpetual.oracle).isMarketClosed(),
            "market is closed"
        );
        require(amount > 0, "amount should greater than 0");
        perpetual.updateCash(trader, amount.neg());
        int256 markPrice = getMarkPrice(perpetual);
        require(
            perpetual.isInitialMarginSafe(trader, markPrice),
            "margin is unsafe after withdrawal"
        );
        isLastWithdrawal = perpetual.isEmptyAccount(trader);
        if (isLastWithdrawal) {
            deregisterActiveAccount(perpetual, trader);
        }
        emit Withdraw(perpetual.id, trader, amount);
    }

    /**
     * @dev     Clear the active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
     *          to sender.
     *          If all active accounts are cleared, the clear progress is done and the perpetual's state will
     *          change to "CLEARED".
     *
     * @param   perpetual       The reference of perpetual storage.
     * @param   trader          The address of the trader to clear.
     * @return  isAllCleared    True if all the active accounts are cleared.
     */
    function clear(PerpetualStorage storage perpetual, address trader)
        public
        returns (bool isAllCleared)
    {
        require(perpetual.activeAccounts.length() > 0, "no account to clear");
        require(
            perpetual.activeAccounts.contains(trader),
            "account cannot be cleared or already cleared"
        );
        countMargin(perpetual, trader);
        perpetual.activeAccounts.remove(trader);
        isAllCleared = (perpetual.activeAccounts.length() == 0);
        emit Clear(perpetual.id, trader);
    }

    /**
     * @dev     Check the margin balance of trader's account, update total margin.
     *          If the margin of the trader's account is not positive, it will be counted as 0.
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   trader      The address of the trader to be counted.
     */
    function countMargin(PerpetualStorage storage perpetual, address trader) public {
        int256 margin = perpetual.getMargin(trader, getMarkPrice(perpetual));
        if (margin <= 0) {
            return;
        }
        if (perpetual.getPosition(trader) != 0) {
            perpetual.totalMarginWithPosition = perpetual.totalMarginWithPosition.add(margin);
        } else {
            perpetual.totalMarginWithoutPosition = perpetual.totalMarginWithoutPosition.add(margin);
        }
    }

    /**
     * @dev     Get the address of the next active account in the perpetual.
     *
     * @param   perpetual   The reference of perpetual storage.
     * @return  account     The address of the next active account.
     */
    function getNextActiveAccount(PerpetualStorage storage perpetual)
        public
        view
        returns (address account)
    {
        require(perpetual.activeAccounts.length() > 0, "no active account");
        account = perpetual.activeAccounts.at(0);
    }

    /**
     * @dev     If the state of the perpetual is "CLEARED".
     *          The traders is able to settle all margin balance left in account.
     *          How much collateral can be returned is determined by the ratio of margin balance left in account to the
     *          total amount of collateral in perpetual.
     *          The priority is:
     *              - accounts withou position;
     *              - accounts with positions;
     *              - accounts with negative margin balance will get nothing back.
     *
     * @param   perpetual       The reference of perpetual storage.
     * @param   trader          The address of the trader to settle.
     * @param   marginToReturn  The actual collateral will be returned to the trader.
     */
    function settle(PerpetualStorage storage perpetual, address trader)
        public
        returns (int256 marginToReturn)
    {
        int256 price = getMarkPrice(perpetual);
        marginToReturn = perpetual.getSettleableMargin(trader, price);
        perpetual.resetAccount(trader);
        emit Settle(perpetual.id, trader, marginToReturn);
    }

    /**
     * @dev     Settle the total collateral of the perpetual, which means update redemptionRateWithPosition
     *          and redemptionRateWithoutPosition variables.
     *          If the total collateral is not enough for the accounts without position,
     *          all the total collateral is given to them proportionally.
     *          If the total collateral is more than the accounts without position needs,
     *          the extra part of collateral is given to the accounts with position proportionally.
     *
     * @param   perpetual   The reference of perpetual storage.
     */
    function settleCollateral(PerpetualStorage storage perpetual) public {
        int256 totalCollateral = perpetual.totalCollateral;
        // 2. cover margin without position
        if (totalCollateral < perpetual.totalMarginWithoutPosition) {
            // margin without positions get balance / total margin
            // smaller rate to make sure total redemption margin < total collateral of perpetual
            perpetual.redemptionRateWithoutPosition = perpetual.totalMarginWithoutPosition > 0
                ? totalCollateral.wdiv(perpetual.totalMarginWithoutPosition, Round.FLOOR)
                : 0;
            // margin with positions will get nothing
            perpetual.redemptionRateWithPosition = 0;
        } else {
            // 3. covere margin with position
            perpetual.redemptionRateWithoutPosition = Constant.SIGNED_ONE;
            // smaller rate to make sure total redemption margin < total collateral of perpetual
            perpetual.redemptionRateWithPosition = perpetual.totalMarginWithPosition > 0
                ? totalCollateral.sub(perpetual.totalMarginWithoutPosition).wdiv(
                    perpetual.totalMarginWithPosition,
                    Round.FLOOR
                )
                : 0;
        }
    }

    /**
     * @dev     Register the trader's account to the active accounts in the perpetual
     * @param   perpetual   The reference of perpetual storage.
     * @param   trader      The address of the trader.
     * @return  True if the trader is added to account for the first time.
     */
    function registerActiveAccount(PerpetualStorage storage perpetual, address trader)
        internal
        returns (bool)
    {
        return perpetual.activeAccounts.add(trader);
    }

    /**
     * @dev     Deregister the trader's account from the active accounts in the perpetual
     * @param   perpetual   The reference of perpetual storage.
     * @param   trader      The address of the trader.
     * @return  True if the trader is removed to account for the first time.
     */
    function deregisterActiveAccount(PerpetualStorage storage perpetual, address trader)
        internal
        returns (bool)
    {
        return perpetual.activeAccounts.remove(trader);
    }

    /**
     * @dev     Update the price data, which means the price and the update time
     * @param   priceData   The price data to update.
     * @param   priceGetter The function pointer to retrieve current price data.
     */
    function updatePriceData(
        OraclePriceData storage priceData,
        function() external returns (int256, uint256) priceGetter
    ) internal {
        (int256 price, uint256 time) = priceGetter();
        require(price > 0 && time != 0, "invalid price data");
        if (time >= priceData.time) {
            priceData.price = price;
            priceData.time = time;
        }
    }

    /**
     * @dev     Increase the total collateral of the perpetual
     * @param   perpetual   The reference of perpetual storage.
     * @param   amount      The amount of collateral to increase
     */
    function increaseTotalCollateral(PerpetualStorage storage perpetual, int256 amount) internal {
        require(amount >= 0, "amount is negative");
        perpetual.totalCollateral = perpetual.totalCollateral.add(amount);
    }

    /**
     * @dev     Decrease the total collateral of the perpetual
     * @param   perpetual   The reference of perpetual storage.
     * @param   amount      The amount of collateral to decrease
     */
    function decreaseTotalCollateral(PerpetualStorage storage perpetual, int256 amount) internal {
        require(amount >= 0, "amount is negative");
        perpetual.totalCollateral = perpetual.totalCollateral.sub(amount);
        require(perpetual.totalCollateral >= 0, "collateral is negative");
    }

    /**
     * @dev     Update the option
     * @param   option      The option to update
     * @param   newValue    The new value of the option, must between the minimum value and the maximum value
     */
    function updateOption(Option storage option, int256 newValue) internal {
        require(newValue >= option.minValue && newValue <= option.maxValue, "value out of range");
        option.value = newValue;
    }

    /**
     * @dev     Set the option value, with constraints that newMinValue <= newValue <= newMaxValue.
     *
     * @param   option      The reference of option storage.
     * @param   newValue    The new value of the option, must be within range of [newMinValue, newMaxValue].
     * @param   newMinValue The minimum value of the option.
     * @param   newMaxValue The maximum value of the option.
     */
    function setOption(
        Option storage option,
        int256 newValue,
        int256 newMinValue,
        int256 newMaxValue
    ) internal {
        require(newValue >= newMinValue && newValue <= newMaxValue, "value out of range");
        option.value = newValue;
        option.minValue = newMinValue;
        option.maxValue = newMaxValue;
    }

    /**
     * @dev     Validate oracle contract, including each method of oracle
     *
     * @param   oracle   The address of oracle contract.
     */
    function validateOracle(address oracle) public {
        require(oracle != address(0), "invalid oracle address");
        require(oracle.isContract(), "oracle must be contract");
        bool success;
        bytes memory data;
        (success, data) = oracle.call(abi.encodeWithSignature("isMarketClosed()"));
        require(success && data.length == 32, "invalid function: isMarketClosed");
        (success, data) = oracle.call(abi.encodeWithSignature("isTerminated()"));
        require(success && data.length == 32, "invalid function: isTerminated");
        require(!abi.decode(data, (bool)), "oracle is terminated");
        (success, data) = oracle.call(abi.encodeWithSignature("collateral()"));
        require(success && data.length > 0, "invalid function: collateral");
        string memory result;
        result = abi.decode(data, (string));
        require(keccak256(bytes(result)) != keccak256(bytes("")), "oracle's collateral is empty");
        (success, data) = oracle.call(abi.encodeWithSignature("underlyingAsset()"));
        require(success && data.length > 0, "invalid function: underlyingAsset");
        result = abi.decode(data, (string));
        require(
            keccak256(bytes(result)) != keccak256(bytes("")),
            "oracle's underlyingAsset is empty"
        );
        (success, data) = oracle.call(abi.encodeWithSignature("priceTWAPLong()"));
        require(success && data.length > 0, "invalid function: priceTWAPLong");
        (int256 price, uint256 timestamp) = abi.decode(data, (int256, uint256));
        require(price > 0 && timestamp > 0, "oracle's twap long price is not updated");
        (success, data) = oracle.call(abi.encodeWithSignature("priceTWAPShort()"));
        require(success && data.length > 0, "invalid function: priceTWAPShort");
        (price, timestamp) = abi.decode(data, (int256, uint256));
        require(price > 0 && timestamp > 0, "oracle's twap short price is not updated");
    }

    /**
     * @dev     Validate the base parameters of the perpetual:
     *            1. initial margin rate > 0
     *            2. 0 < maintenance margin rate <= initial margin rate
     *            3. 0 <= operator fee rate <= 0.01
     *            4. 0 <= lp fee rate <= 0.01
     *            5. 0 <= liquidation penalty rate < maintenance margin rate
     *            6. keeper gas reward >= 0
     *
     * @param   perpetual   The reference of perpetual storage.
     * @param   baseParams  The base parameters of the perpetual.
     */
    function validateBaseParameters(PerpetualStorage storage perpetual, int256[9] memory baseParams)
        public
        view
    {
        require(baseParams[INDEX_INITIAL_MARGIN_RATE] > 0, "initialMarginRate <= 0");
        require(
            perpetual.initialMarginRate == 0 ||
                baseParams[INDEX_INITIAL_MARGIN_RATE] <= perpetual.initialMarginRate,
            "cannot increase initialMarginRate"
        );
        int256 maxLeverage = Constant.SIGNED_ONE.wdiv(baseParams[INDEX_INITIAL_MARGIN_RATE]);
        require(
            perpetual.defaultTargetLeverage.value <= maxLeverage,
            "default target leverage exceeds max leverage"
        );
        require(
            perpetual.maintenanceMarginRate == 0 ||
                baseParams[INDEX_MAINTENANCE_MARGIN_RATE] <= perpetual.maintenanceMarginRate,
            "cannot increase maintenanceMarginRate"
        );
        require(baseParams[INDEX_MAINTENANCE_MARGIN_RATE] > 0, "maintenanceMarginRate <= 0");
        require(
            baseParams[INDEX_MAINTENANCE_MARGIN_RATE] <= baseParams[INDEX_INITIAL_MARGIN_RATE],
            "maintenanceMarginRate > initialMarginRate"
        );
        require(baseParams[INDEX_OPERATOR_FEE_RATE] >= 0, "operatorFeeRate < 0");
        require(
            baseParams[INDEX_OPERATOR_FEE_RATE] <= (Constant.SIGNED_ONE / 100),
            "operatorFeeRate > 1%"
        );
        require(baseParams[INDEX_LP_FEE_RATE] >= 0, "lpFeeRate < 0");
        require(baseParams[INDEX_LP_FEE_RATE] <= (Constant.SIGNED_ONE / 100), "lpFeeRate > 1%");

        require(baseParams[INDEX_REFERRAL_REBATE_RATE] >= 0, "referralRebateRate < 0");
        require(
            baseParams[INDEX_REFERRAL_REBATE_RATE] <= Constant.SIGNED_ONE,
            "referralRebateRate > 100%"
        );

        require(baseParams[INDEX_LIQUIDATION_PENALTY_RATE] >= 0, "liquidationPenaltyRate < 0");
        require(
            baseParams[INDEX_LIQUIDATION_PENALTY_RATE] <= baseParams[INDEX_MAINTENANCE_MARGIN_RATE],
            "liquidationPenaltyRate > maintenanceMarginRate"
        );
        require(baseParams[INDEX_KEEPER_GAS_REWARD] >= 0, "keeperGasReward < 0");
        require(baseParams[INDEX_INSURANCE_FUND_RATE] >= 0, "insuranceFundRate < 0");
        require(baseParams[INDEX_MAX_OPEN_INTEREST_RATE] > 0, "maxOpenInterestRate <= 0");
    }

    /**
     * @dev     alidate the risk parameters of the perpetual
     *            1. 0 <= half spread < 1
     *            2. open slippage factor > 0
     *            3. 0 < close slippage factor <= open slippage factor
     *            4. funding rate limit >= 0
     *            5. AMM max leverage > 0
     *            6. 0 <= max close price discount < 1
     *
     * @param   perpetual   The reference of perpetual storage.
     */
    function validateRiskParameters(PerpetualStorage storage perpetual, int256[9] memory riskParams)
        public
        view
    {
        // must set risk parameters after setting base parameters
        require(perpetual.initialMarginRate > 0, "need to set base parameters first");
        require(riskParams[INDEX_HALF_SPREAD] >= 0, "halfSpread < 0");
        require(riskParams[INDEX_HALF_SPREAD] < Constant.SIGNED_ONE, "halfSpread >= 100%");
        require(riskParams[INDEX_OPEN_SLIPPAGE_FACTOR] > 0, "openSlippageFactor < 0");
        require(riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR] > 0, "closeSlippageFactor < 0");
        require(
            riskParams[INDEX_CLOSE_SLIPPAGE_FACTOR] <= riskParams[INDEX_OPEN_SLIPPAGE_FACTOR],
            "closeSlippageFactor > openSlippageFactor"
        );
        require(riskParams[INDEX_FUNDING_RATE_FACTOR] >= 0, "fundingRateFactor < 0");
        require(riskParams[INDEX_FUNDING_RATE_LIMIT] >= 0, "fundingRateLimit < 0");
        require(riskParams[INDEX_AMM_MAX_LEVERAGE] >= 0, "ammMaxLeverage < 0");
        require(
            riskParams[INDEX_AMM_MAX_LEVERAGE] <=
                Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate, Round.FLOOR),
            "ammMaxLeverage > 1 / initialMarginRate"
        );
        require(riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT] >= 0, "maxClosePriceDiscount < 0");
        require(
            riskParams[INDEX_AMM_CLOSE_PRICE_DISCOUNT] < Constant.SIGNED_ONE,
            "maxClosePriceDiscount >= 100%"
        );
        require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
        int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
        require(
            riskParams[INDEX_DEFAULT_TARGET_LEVERAGE] <= maxLeverage,
            "default target leverage exceeds max leverage"
        );
    }
}

File 8 of 49 : Getter.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

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

import "./interface/ILiquidityPoolGetter.sol";

import "./libraries/SafeMathExt.sol";
import "./libraries/Utils.sol";

import "./module/MarginAccountModule.sol";
import "./module/PerpetualModule.sol";
import "./module/LiquidityPoolModule.sol";
import "./module/TradeModule.sol";
import "./module/AMMModule.sol";

import "./Type.sol";
import "./Storage.sol";

/**
 * @notice  Getter is a helper to help getting status of liquidity from external.
 */
contract Getter is Storage, ILiquidityPoolGetter {
    using SafeMathUpgradeable for uint256;
    using SafeCastUpgradeable for uint256;
    using SafeMathExt for int256;
    using SafeMathExt for uint256;
    using Utils for EnumerableSetUpgradeable.AddressSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using MarginAccountModule for PerpetualStorage;
    using PerpetualModule for PerpetualStorage;
    using LiquidityPoolModule for LiquidityPoolStorage;
    using TradeModule for LiquidityPoolStorage;
    using AMMModule for LiquidityPoolStorage;

    /**
     * @notice  Get the info of the liquidity pool.
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     *
     * @return  isRunning               True if the liquidity pool is running.
     * @return  isFastCreationEnabled   True if the operator of the liquidity pool is allowed to create new perpetual
     *                                  when the liquidity pool is running.
     * @return  addresses               The related addresses of the liquidity pool.
     * @return  intNums                 An fixed length array of int type properties, see comments for details.
     * @return  uintNums                An fixed length array of uint type properties, see comments for details.
     */
    function getLiquidityPoolInfo()
        external
        view
        override
        returns (
            bool isRunning,
            bool isFastCreationEnabled,
            // [0] creator,
            // [1] operator,
            // [2] transferringOperator,
            // [3] governor,
            // [4] shareToken,
            // [5] collateralToken,
            // [6] vault,
            address[7] memory addresses,
            // [0] vaultFeeRate,
            // [1] poolCash,
            // [2] insuranceFundCap,
            // [3] insuranceFund,
            // [4] donatedInsuranceFund,
            int256[5] memory intNums,
            // [0] collateralDecimals,
            // [1] perpetualCount,
            // [2] fundingTime,
            // [3] operatorExpiration,
            // [4] liquidityCap,
            // [5] shareTransferDelay,
            uint256[6] memory uintNums
        )
    {
        isRunning = _liquidityPool.isRunning;
        isFastCreationEnabled = _liquidityPool.isFastCreationEnabled;
        addresses = [
            _liquidityPool.creator,
            _liquidityPool.getOperator(),
            _liquidityPool.getTransferringOperator(),
            _liquidityPool.governor,
            _liquidityPool.shareToken,
            _liquidityPool.collateralToken,
            _liquidityPool.getVault()
        ];
        intNums[0] = _liquidityPool.getVaultFeeRate();
        intNums[1] = _liquidityPool.poolCash;
        intNums[2] = _liquidityPool.insuranceFundCap;
        intNums[3] = _liquidityPool.insuranceFund;
        intNums[4] = _liquidityPool.donatedInsuranceFund;
        uintNums[0] = _liquidityPool.collateralDecimals;
        uintNums[1] = _liquidityPool.perpetualCount;
        uintNums[2] = _liquidityPool.fundingTime;
        uintNums[3] = _liquidityPool.operatorExpiration;
        uintNums[4] = _liquidityPool.liquidityCap;
        uintNums[5] = _liquidityPool.getShareTransferDelay();
    }

    /**
     * @notice  Get the info of the perpetual. Need to update the funding state and the oracle price
     *          of each perpetual before and update the funding rate of each perpetual after.
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @return  state           The state of the perpetual.
     * @return  oracle          The address of the current oracle in perpetual.
     * @return  nums            An fixed length array of uint type properties, see comments for details.
     */
    function getPerpetualInfo(uint256 perpetualIndex)
        external
        view
        override
        onlyExistedPerpetual(perpetualIndex)
        returns (
            PerpetualState state,
            address oracle,
            // [0] totalCollateral
            // [1] markPrice, (return settlementPrice if it is in EMERGENCY state)
            // [2] indexPrice,
            // [3] fundingRate,
            // [4] unitAccumulativeFunding,
            // [5] initialMarginRate,
            // [6] maintenanceMarginRate,
            // [7] operatorFeeRate,
            // [8] lpFeeRate,
            // [9] referralRebateRate,
            // [10] liquidationPenaltyRate,
            // [11] keeperGasReward,
            // [12] insuranceFundRate,
            // [13-15] halfSpread value, min, max,
            // [16-18] openSlippageFactor value, min, max,
            // [19-21] closeSlippageFactor value, min, max,
            // [22-24] fundingRateLimit value, min, max,
            // [25-27] ammMaxLeverage value, min, max,
            // [28-30] maxClosePriceDiscount value, min, max,
            // [31] openInterest,
            // [32] maxOpenInterestRate,
            // [33-35] fundingRateFactor value, min, max,
            // [36-38] defaultTargetLeverage value, min, max,
            // [39-41] baseFundingRate value, min, max,
            int256[42] memory nums
        )
    {
        PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
        state = perpetual.state;
        oracle = perpetual.oracle;
        nums = [
            // [0]
            perpetual.totalCollateral,
            perpetual.getMarkPrice(),
            perpetual.getIndexPrice(),
            perpetual.fundingRate,
            perpetual.unitAccumulativeFunding,
            perpetual.initialMarginRate,
            perpetual.maintenanceMarginRate,
            perpetual.operatorFeeRate,
            perpetual.lpFeeRate,
            perpetual.referralRebateRate,
            // [10]
            perpetual.liquidationPenaltyRate,
            perpetual.keeperGasReward,
            perpetual.insuranceFundRate,
            perpetual.halfSpread.value,
            perpetual.halfSpread.minValue,
            perpetual.halfSpread.maxValue,
            perpetual.openSlippageFactor.value,
            perpetual.openSlippageFactor.minValue,
            perpetual.openSlippageFactor.maxValue,
            perpetual.closeSlippageFactor.value,
            // [20]
            perpetual.closeSlippageFactor.minValue,
            perpetual.closeSlippageFactor.maxValue,
            perpetual.fundingRateLimit.value,
            perpetual.fundingRateLimit.minValue,
            perpetual.fundingRateLimit.maxValue,
            perpetual.ammMaxLeverage.value,
            perpetual.ammMaxLeverage.minValue,
            perpetual.ammMaxLeverage.maxValue,
            perpetual.maxClosePriceDiscount.value,
            perpetual.maxClosePriceDiscount.minValue,
            // [30]
            perpetual.maxClosePriceDiscount.maxValue,
            perpetual.openInterest,
            perpetual.maxOpenInterestRate,
            perpetual.fundingRateFactor.value,
            perpetual.fundingRateFactor.minValue,
            perpetual.fundingRateFactor.maxValue,
            perpetual.defaultTargetLeverage.value,
            perpetual.defaultTargetLeverage.minValue,
            perpetual.defaultTargetLeverage.maxValue,
            perpetual.baseFundingRate.value,
            // [40]
            perpetual.baseFundingRate.minValue,
            perpetual.baseFundingRate.maxValue
        ];
    }

    /**
     * @notice  Get the account info of the trader. Need to update the funding state and the oracle price
     *          of each perpetual before and update the funding rate of each perpetual after
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     *
     * @param perpetualIndex    The index of the perpetual in the liquidity pool.
     * @param trader            The address of the trader.
     *                          When trader == liquidityPool, isSafe are meanless. Do not forget to sum
     *                          poolCash and availableCash of all perpetuals in a liquidityPool when
     *                          calculating AMM margin
     * @return cash                     The cash of the account.
     * @return position                 The position of the account.
     * @return availableMargin          The available margin of the account.
     * @return margin                   The margin of the account.
     * @return settleableMargin         The settleable margin of the account.
     * @return isInitialMarginSafe      True if the account is initial margin safe.
     * @return isMaintenanceMarginSafe  True if the account is maintenance margin safe.
     * @return isMarginSafe             True if the total value of margin account is beyond 0.
     * @return targetLeverage           The target leverage for openning position.
     */
    function getMarginAccount(uint256 perpetualIndex, address trader)
        external
        view
        override
        onlyExistedPerpetual(perpetualIndex)
        returns (
            int256 cash,
            int256 position,
            int256 availableMargin,
            int256 margin,
            int256 settleableMargin,
            bool isInitialMarginSafe,
            bool isMaintenanceMarginSafe,
            bool isMarginSafe,
            int256 targetLeverage
        )
    {
        PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
        MarginAccount storage account = perpetual.marginAccounts[trader];
        int256 markPrice = perpetual.getMarkPrice();
        cash = account.cash;
        position = account.position;
        availableMargin = perpetual.getAvailableMargin(trader, markPrice);
        margin = perpetual.getMargin(trader, markPrice);
        settleableMargin = perpetual.getSettleableMargin(trader, markPrice);
        isInitialMarginSafe = perpetual.isInitialMarginSafe(trader, markPrice);
        isMaintenanceMarginSafe = perpetual.isMaintenanceMarginSafe(trader, markPrice);
        isMarginSafe = perpetual.isMarginSafe(trader, markPrice);
        targetLeverage = perpetual.getTargetLeverage(trader);
    }

    /**
     * @notice  Get the number of active accounts in the given perpetual.
     *          Active means the trader has margin (margin != 0) in the margin account.
     * @param   perpetualIndex      The index of the perpetual in liquidity pool.
     * @return  activeAccountCount  The number of active accounts in the perpetual.
     */
    function getActiveAccountCount(uint256 perpetualIndex)
        external
        view
        override
        onlyExistedPerpetual(perpetualIndex)
        returns (uint256 activeAccountCount)
    {
        activeAccountCount = _liquidityPool.perpetuals[perpetualIndex].activeAccounts.length();
    }

    /**
     * @notice  Get the active accounts in the perpetual whose index with range [begin, end).
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   begin           The begin index of account to retrieve.
     * @param   end             The end index of account, exclusive.
     * @return  result          An array of active addresses.
     */
    function listActiveAccounts(
        uint256 perpetualIndex,
        uint256 begin,
        uint256 end
    )
        external
        view
        override
        onlyExistedPerpetual(perpetualIndex)
        returns (address[] memory result)
    {
        PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
        result = perpetual.activeAccounts.toArray(begin, end);
    }

    /**
     * @notice  Get the progress of clearing active accounts.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @return  left            Number of left active accounts.
     * @return  total           Number of total active accounts.
     */
    function getClearProgress(uint256 perpetualIndex)
        external
        view
        override
        onlyExistedPerpetual(perpetualIndex)
        returns (uint256 left, uint256 total)
    {
        PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
        left = perpetual.activeAccounts.length();
        total = perpetual.state == PerpetualState.NORMAL
            ? perpetual.activeAccounts.length()
            : perpetual.totalAccount;
    }

    /**
     * @notice  Get the pool margin of the liquidity pool.
     *          Pool margin is how much collateral of the pool considering the AMM's positions of perpetuals
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     * @return  poolMargin  The pool margin of the liquidity pool
     * @return  isAMMSafe   True if AMM is safe
     */
    function getPoolMargin() external view override returns (int256 poolMargin, bool isAMMSafe) {
        (poolMargin, isAMMSafe) = _liquidityPool.getPoolMargin();
    }

    /**
     * @notice  Query the price, fees and cost when trade agaist amm.
     *          The trading price is determined by the AMM based on the index price of the perpetual.
     *          This method should returns the same result as a 'read-only' trade.
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     *
     *          Flags is a 32 bit uint value which indicates: (from highest bit)
     *            - close only      only close position during trading;
     *            - market order    do not check limit price during trading;
     *            - stop loss       only available in brokerTrade mode;
     *            - take profit     only available in brokerTrade mode;
     *          For stop loss and take profit, see `validateTriggerPrice` in OrderModule.sol for details.
     *
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   trader          The address of trader.
     * @param   amount          The amount of position to trader, positive for buying and negative for selling. The amount always use decimals 18.
     * @param   referrer        The address of referrer who will get rebate from the deal.
     * @param   flags           The flags of the trade.
     * @return  tradePrice      The average fill price.
     * @return  totalFee        The total fee collected from the trader after the trade.
     * @return  cost            Deposit or withdraw to let effective leverage == targetLeverage if flags contain USE_TARGET_LEVERAGE. > 0 if deposit, < 0 if withdraw.
     */
    function queryTrade(
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        address referrer,
        uint32 flags
    )
        external
        override
        returns (
            int256 tradePrice,
            int256 totalFee,
            int256 cost
        )
    {
        require(trader != address(0), "invalid trader");
        require(amount != 0, "invalid amount");
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        return _liquidityPool.queryTrade(perpetualIndex, trader, amount, referrer, flags);
    }

    /**
     * @notice  Query cash to add / share to mint when adding liquidity to the liquidity pool.
     *          Only one of cashToAdd or shareToMint may be non-zero.
     *          Can only called when the pool is running.
     *
     * @param   cashToAdd         The amount of cash to add, always use decimals 18.
     * @param   shareToMint       The amount of share token to mint, always use decimals 18.
     * @return  cashToAddResult   The amount of cash to add, always use decimals 18. Equal to cashToAdd if cashToAdd is non-zero.
     * @return  shareToMintResult The amount of cash to add, always use decimals 18. Equal to shareToMint if shareToMint is non-zero.
     */
    function queryAddLiquidity(int256 cashToAdd, int256 shareToMint)
        external
        view
        override
        returns (int256 cashToAddResult, int256 shareToMintResult)
    {
        require(_liquidityPool.isRunning, "pool is not running");
        int256 shareTotalSupply = IGovernor(_liquidityPool.shareToken).totalSupply().toInt256();
        if (cashToAdd > 0 && shareToMint == 0) {
            (shareToMintResult, ) = _liquidityPool.getShareToMint(shareTotalSupply, cashToAdd);
            cashToAddResult = cashToAdd;
        } else if (cashToAdd == 0 && shareToMint > 0) {
            cashToAddResult = _liquidityPool.getCashToAdd(shareTotalSupply, shareToMint);
            shareToMintResult = shareToMint;
        } else {
            revert("invalid parameter");
        }
    }

    /**
     * @notice  Query cash to return / share to redeem when removing liquidity from the liquidity pool.
     *          Only one of shareToRemove or cashToReturn may be non-zero.
     *          Can only called when the pool is running.
     *
     * @param   shareToRemove       The amount of share token to redeem, always use decimals 18.
     * @param   cashToReturn        The amount of cash to return, always use decimals 18.
     * @return  shareToRemoveResult The amount of share token to redeem, always use decimals 18. Equal to shareToRemove if shareToRemove is non-zero.
     * @return  cashToReturnResult  The amount of cash to return, always use decimals 18. Equal to cashToReturn if cashToReturn is non-zero.
     */
    function queryRemoveLiquidity(int256 shareToRemove, int256 cashToReturn)
        external
        view
        override
        returns (int256 shareToRemoveResult, int256 cashToReturnResult)
    {
        require(_liquidityPool.isRunning, "pool is not running");
        int256 shareTotalSupply = IGovernor(_liquidityPool.shareToken).totalSupply().toInt256();
        if (shareToRemove > 0 && cashToReturn == 0) {
            (cashToReturnResult, , , ) = _liquidityPool.getCashToReturn(
                shareTotalSupply,
                shareToRemove
            );
            shareToRemoveResult = shareToRemove;
        } else if (shareToRemove == 0 && cashToReturn > 0) {
            (shareToRemoveResult, , , ) = _liquidityPool.getShareToRemove(
                shareTotalSupply,
                cashToReturn
            );
            cashToReturnResult = cashToReturn;
        } else {
            revert("invalid parameter");
        }
    }

    /**
     * @notice  List all local keepers who are able to call `liquidateByAMM`.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   begin           The begin index of keeper to retrieve.
     * @param   end             The end index of keeper, exclusive.
     * @return  result          An array of keeper addresses.
     */
    function listByAMMKeepers(
        uint256 perpetualIndex,
        uint256 begin,
        uint256 end
    ) external view onlyExistedPerpetual(perpetualIndex) returns (address[] memory result) {
        result = _liquidityPool.perpetuals[perpetualIndex].ammKeepers.toArray(begin, end);
    }

    bytes32[50] private __gap;
}

File 9 of 49 : Governance.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "./module/PerpetualModule.sol";
import "./Type.sol";
import "./Storage.sol";

import "./interface/ILiquidityPoolGovernance.sol";

// @title Governance is the contract to maintain liquidityPool parameters.
contract Governance is Storage, ILiquidityPoolGovernance {
    using SafeMathUpgradeable for uint256;
    using PerpetualModule for PerpetualStorage;
    using MarginAccountModule for PerpetualStorage;
    using LiquidityPoolModule for LiquidityPoolStorage;

    modifier onlyGovernor() {
        require(_msgSender() == _liquidityPool.governor, "only governor is allowed");
        _;
    }

    modifier onlyOperator() {
        require(_msgSender() == _liquidityPool.getOperator(), "only operator is allowed");
        _;
    }

    modifier onlyOperatorOrGovernor() {
        address operator = _liquidityPool.getOperator();
        if (operator != address(0)) {
            // has operator
            require(_msgSender() == operator, "can only be initiated by operator");
        } else {
            require(_msgSender() == _liquidityPool.governor, "can only be initiated by governor");
        }
        _;
    }

    /**
        @notice
     */
    function checkIn() public onlyOperator {
        _liquidityPool.checkIn();
    }

    /**
     * @notice  Use in a two phase operator transfer design:
     *            1. transfer operator to new operator;
     *            2. new operator claim to finish transfer.
     *          Before claimOperator is called, operator wil remain to be the previous address.
     *
     *          There are condition when calling transferring operator:
     *            1. when operator exists, only operator is able to call transfer;
     *            2. when operator not exists, call should be from a succeeded governor proposal.
     * @param   newOperator The address of new operator to transfer to.
     */
    function transferOperator(address newOperator) external onlyOperatorOrGovernor {
        require(newOperator != address(0), "new operator is zero address");
        _liquidityPool.transferOperator(newOperator);
    }

    /**
     * @notice  Claim the ownership of the liquidity pool to sender. See `transferOperator` for details.
     *          The caller must be the one specified by `transferOperator` first.
     */
    function claimOperator() public {
        _liquidityPool.claimOperator(_msgSender());
    }

    /**
     * @notice  Revoke the operator of the liquidity pool. Can only called by the operator.
     */
    function revokeOperator() public onlyOperator {
        _liquidityPool.revokeOperator();
    }

    /**
     * @notice  Set the parameter of the liquidity pool. Can only called by the governor.
     * @param   params  New values of parameter set.
     */
    function setLiquidityPoolParameter(int256[4] calldata params) public onlyGovernor {
        _liquidityPool.setLiquidityPoolParameter(params);
    }

    function setOracle(uint256 perpetualIndex, address oracle) public onlyGovernor {
        _liquidityPool.setPerpetualOracle(perpetualIndex, oracle);
    }

    /**
     * @notice  Set the base parameter of the perpetual. Can only called by the governor.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   baseParams      Values of new base parameter set
     */
    function setPerpetualBaseParameter(uint256 perpetualIndex, int256[9] calldata baseParams)
        public
        onlyGovernor
    {
        _liquidityPool.setPerpetualBaseParameter(perpetualIndex, baseParams);
    }

    /**
     * @notice  Set the risk parameter and adjust range of the perpetual. Can only called by the governor.
     * @param   perpetualIndex      The index of the perpetual in liquidity pool.
     * @param   riskParams          Values of new risk parameter set, each should be within range of related [min, max].
     * @param   minRiskParamValues  Min values of new risk parameter.
     * @param   maxRiskParamValues  Max values of new risk parameter.
     */
    function setPerpetualRiskParameter(
        uint256 perpetualIndex,
        int256[9] calldata riskParams,
        int256[9] calldata minRiskParamValues,
        int256[9] calldata maxRiskParamValues
    ) external onlyGovernor {
        _liquidityPool.setPerpetualRiskParameter(
            perpetualIndex,
            riskParams,
            minRiskParamValues,
            maxRiskParamValues
        );
    }

    /**
     * @notice  Update the risk parameter of the perpetual. Can only called by the operator
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   riskParams      The new value of the risk parameter, must between minimum value and maximum value
     */
    function updatePerpetualRiskParameter(uint256 perpetualIndex, int256[9] calldata riskParams)
        external
        onlyOperator
    {
        _liquidityPool.updatePerpetualRiskParameter(perpetualIndex, riskParams);
    }

    /**
     * @dev     Add an account to the whitelist, accounts in the whitelist is allowed to call `liquidateByAMM`.
     *          If never called, the whitelist in poolCreator will be used instead.
     *          Once called, the local whitelist will be used and the the whitelist in poolCreator will be ignored.
     *
     * @param   keeper          The account of keeper.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     */
    function addAMMKeeper(uint256 perpetualIndex, address keeper) external onlyOperatorOrGovernor {
        _liquidityPool.addAMMKeeper(perpetualIndex, keeper);
    }

    /**
     * @dev     Remove an account from the `liquidateByAMM` whitelist.
     *
     * @param   keeper          The account of keeper.
     * @param   perpetualIndex  The index of perpetual in the liquidity pool
     */
    function removeAMMKeeper(uint256 perpetualIndex, address keeper)
        external
        onlyOperatorOrGovernor
    {
        _liquidityPool.removeAMMKeeper(perpetualIndex, keeper);
    }

    /**
     * @notice  Force to set the state of the perpetual to "EMERGENCY" and set the settlement price.
     *          Can only called by the governor.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     */
    function forceToSetEmergencyState(uint256 perpetualIndex, int256 settlementPrice)
        external
        syncState(true)
        onlyGovernor
    {
        require(settlementPrice >= 0, "negative settlement price");
        OraclePriceData memory settlementPriceData = OraclePriceData({
            price: settlementPrice,
            time: block.timestamp
        });
        PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
        perpetual.markPriceData = settlementPriceData;
        perpetual.indexPriceData = settlementPriceData;
        _liquidityPool.setEmergencyState(perpetualIndex);
    }

    /**
     * @notice  Set perpetual into "EMERGENCY" state.
     *          1. if the oracle contract declares itself as "terminated", call setEmergencyState(index).
     *          2. if the AMM is maintenance margin unsafe, call
     *             setEmergencyState(SET_ALL_PERPETUALS_TO_EMERGENCY_STATE).
     * @param   perpetualIndex  The index of the perpetual in liquidity pool or
     *                          SET_ALL_PERPETUALS_TO_EMERGENCY_STATE to settle the whole pool
     */
    function setEmergencyState(uint256 perpetualIndex) public override syncState(true) {
        if (perpetualIndex == Constant.SET_ALL_PERPETUALS_TO_EMERGENCY_STATE) {
            _liquidityPool.setAllPerpetualsToEmergencyState();
        } else {
            PerpetualStorage storage perpetual = _liquidityPool.perpetuals[perpetualIndex];
            require(IOracle(perpetual.oracle).isTerminated(), "prerequisite not met");
            _liquidityPool.setEmergencyState(perpetualIndex);
        }
    }

    bytes32[50] private __gap;
}

File 10 of 49 : LibraryEvents.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

/**
 * @notice  The libraryEvents defines events that will be raised from modules (contract/modules).
 * @dev     DO REMEMBER to add new events in modules here.
 */
contract LibraryEvents {
    // PerpetualModule
    event Deposit(uint256 perpetualIndex, address indexed trader, int256 amount);
    event Withdraw(uint256 perpetualIndex, address indexed trader, int256 amount);
    event Clear(uint256 perpetualIndex, address indexed trader);
    event Settle(uint256 perpetualIndex, address indexed trader, int256 amount);
    event SetNormalState(uint256 perpetualIndex);
    event SetEmergencyState(uint256 perpetualIndex, int256 settlementPrice, uint256 settlementTime);
    event SetClearedState(uint256 perpetualIndex);
    event UpdateUnitAccumulativeFunding(uint256 perpetualIndex, int256 unitAccumulativeFunding);
    event SetPerpetualBaseParameter(uint256 perpetualIndex, int256[9] baseParams);
    event SetPerpetualRiskParameter(
        uint256 perpetualIndex,
        int256[9] riskParams,
        int256[9] minRiskParamValues,
        int256[9] maxRiskParamValues
    );
    event UpdatePerpetualRiskParameter(uint256 perpetualIndex, int256[9] riskParams);
    event SetOracle(uint256 perpetualIndex, address indexed oldOracle, address indexed newOracle);
    event UpdatePrice(
        uint256 perpetualIndex,
        address indexed oracle,
        int256 markPrice,
        uint256 markPriceUpdateTime,
        int256 indexPrice,
        uint256 indexPriceUpdateTime
    );
    event UpdateFundingRate(uint256 perpetualIndex, int256 fundingRate);

    // LiquidityPoolModule
    event AddLiquidity(
        address indexed trader,
        int256 addedCash,
        int256 mintedShare,
        int256 addedPoolMargin
    );
    event RemoveLiquidity(
        address indexed trader,
        int256 returnedCash,
        int256 burnedShare,
        int256 removedPoolMargin
    );
    event UpdatePoolMargin(int256 poolMargin);
    event TransferOperatorTo(address indexed newOperator);
    event ClaimOperator(address indexed newOperator);
    event RevokeOperator();
    event SetLiquidityPoolParameter(int256[4] value);
    event CreatePerpetual(
        uint256 perpetualIndex,
        address governor,
        address shareToken,
        address operator,
        address oracle,
        address collateral,
        int256[9] baseParams,
        int256[9] riskParams
    );
    event RunLiquidityPool();
    event OperatorCheckIn(address indexed operator);
    event DonateInsuranceFund(int256 amount);
    event TransferExcessInsuranceFundToLP(int256 amount);
    event SetTargetLeverage(uint256 perpetualIndex, address indexed trader, int256 targetLeverage);
    event AddAMMKeeper(uint256 perpetualIndex, address indexed keeper);
    event RemoveAMMKeeper(uint256 perpetualIndex, address indexed keeper);
    event AddTraderKeeper(uint256 perpetualIndex, address indexed keeper);
    event RemoveTraderKeeper(uint256 perpetualIndex, address indexed keeper);

    // TradeModule
    event Trade(
        uint256 perpetualIndex,
        address indexed trader,
        int256 position,
        int256 price,
        int256 fee,
        int256 lpFee
    );
    event Liquidate(
        uint256 perpetualIndex,
        address indexed liquidator,
        address indexed trader,
        int256 amount,
        int256 price,
        int256 penalty,
        int256 penaltyToLP
    );
    event TransferFeeToVault(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed vault,
        int256 vaultFee
    );
    event TransferFeeToOperator(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed operator,
        int256 operatorFee
    );
    event TransferFeeToReferrer(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed referrer,
        int256 referralRebate
    );
}

File 11 of 49 : Perpetual.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";

import "./interface/IPerpetual.sol";

import "./libraries/Constant.sol";
import "./libraries/OrderData.sol";

import "./module/TradeModule.sol";
import "./module/OrderModule.sol";
import "./module/LiquidityPoolModule.sol";

import "./Storage.sol";
import "./Type.sol";

contract Perpetual is Storage, ReentrancyGuardUpgradeable, IPerpetual {
    using OrderData for bytes;
    using OrderData for uint32;
    using OrderModule for LiquidityPoolStorage;
    using TradeModule for LiquidityPoolStorage;
    using LiquidityPoolModule for LiquidityPoolStorage;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    // deprecated
    function setTargetLeverage(
        uint256 perpetualIndex,
        address trader,
        int256 targetLeverage
    )
        external
        onlyAuthorized(
            trader,
            Constant.PRIVILEGE_TRADE | Constant.PRIVILEGE_DEPOSIT | Constant.PRIVILEGE_WITHDRAW
        )
    {
        require(trader != address(0), "invalid trader");
        require(targetLeverage % Constant.SIGNED_ONE == 0, "targetLeverage must be integer");
        require(targetLeverage > 0, "targetLeverage is negative");
        _liquidityPool.setTargetLeverage(perpetualIndex, trader, targetLeverage);
    }

    /**
     * @notice  Deposit collateral to the perpetual.
     *          Can only called when the perpetual's state is "NORMAL".
     *          This method will always increase `cash` amount in trader's margin account.
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     * @param   amount          The amount of collateral to deposit. The amount always use decimals 18.
     */
    function deposit(
        uint256 perpetualIndex,
        address trader,
        int256 amount
    )
        external
        override
        nonReentrant
        onlyNotUniverseSettled
        onlyAuthorized(trader, Constant.PRIVILEGE_DEPOSIT)
    {
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        require(trader != address(0), "invalid trader");
        require(amount > 0, "invalid amount");
        _liquidityPool.deposit(perpetualIndex, trader, amount);
    }

    /**
     * @notice  Withdraw collateral from the trader's account of the perpetual.
     *          After withdrawn, trader shall at least has maintenance margin left in account.
     *          Can only called when the perpetual's state is "NORMAL".
     *          Margin account must at least keep
     *          The trader's cash will decrease in the perpetual.
     *          Need to update the funding state and the oracle price of each perpetual before
     *          and update the funding rate of each perpetual after
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     * @param   amount          The amount of collateral to withdraw. The amount always use decimals 18.
     */
    function withdraw(
        uint256 perpetualIndex,
        address trader,
        int256 amount
    )
        external
        override
        nonReentrant
        onlyNotUniverseSettled
        syncState(false)
        onlyAuthorized(trader, Constant.PRIVILEGE_WITHDRAW)
    {
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        require(trader != address(0), "invalid trader");
        require(amount > 0, "invalid amount");
        _liquidityPool.withdraw(perpetualIndex, trader, amount);
    }

    /**
     * @notice  If the state of the perpetual is "CLEARED", anyone authorized withdraw privilege by trader can settle
     *          trader's account in the perpetual. Which means to calculate how much the collateral should be returned
     *          to the trader, return it to trader's wallet and clear the trader's cash and position in the perpetual.
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @param   trader          The address of the trader.
     */
    function settle(uint256 perpetualIndex, address trader)
        external
        override
        onlyAuthorized(trader, Constant.PRIVILEGE_WITHDRAW)
        nonReentrant
    {
        require(trader != address(0), "invalid trader");
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.CLEARED,
            "perpetual should be in CLEARED state"
        );
        _liquidityPool.settle(perpetualIndex, trader);
    }

    /**
     * @notice  Clear the next active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
     *          to sender. If all active accounts are cleared, the clear progress is done and the perpetual's state will
     *          change to "CLEARED". Active means the trader's account is not empty in the perpetual.
     *          Empty means cash and position are zero
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     */
    function clear(uint256 perpetualIndex) external override nonReentrant {
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.EMERGENCY,
            "perpetual should be in EMERGENCY state"
        );
        _liquidityPool.clear(perpetualIndex, _msgSender());
    }

    /**
     * @notice  Trade with AMM in the perpetual, require sender is granted the trade privilege by the trader.
     *          The trading price is determined by the AMM based on the index price of the perpetual.
     *          A successful trade should:
     *            - The trade transaction not exceeds deadline;
     *            - Current liquidity of amm is enough to make the deal;
     *            - to open position:
     *              - Trader's margin balance must be greater then or equal to initial margin after trading;
     *              - Full trading fee will be charged if trader is opening position.
     *            - to close position:
     *              - Trader's margin balance must be greater then or equal to 0 after trading;
     *              - Trader need to pay the trading fee as much as possible before all the margin balance drained.
     *          If one trade transaction does close and open at same time (Open positions in the opposite direction)
     *          It will be treat as opening position.
     *
     *
     *          Flags is a 32 bit uint value which indicates: (from highest bit)
     *            31               27 26                     7 6              0
     *           +---+---+---+---+---+------------------------+----------------+
     *           | C | M | S | T | R | Target leverage 20bits | Reserved 7bits |
     *           +---+---+---+---+---+------------------------+----------------+
     *             |   |   |   |   |   ` Target leverage  Fixed-point decimal with 2 decimal digits. 
     *             |   |   |   |   |                      0 means don't automatically deposit / withdraw.
     *             |   |   |   |   `---  Reserved
     *             |   |   |   `-------  Take profit      Only available in brokerTrade mode.
     *             |   |   `-----------  Stop loss        Only available in brokerTrade mode.
     *             |   `---------------  Market order     Do not check limit price during trading.
     *             `-------------------  Close only       Only close position during trading.
     *          For stop loss and take profit, see `validateTriggerPrice` in OrderModule.sol for details.
     *
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   trader          The address of trader.
     * @param   amount          The amount of position to trader, positive for buying and negative for selling. The amount always use decimals 18.
     * @param   limitPrice      The worst price the trader accepts.
     * @param   deadline        The deadline of trade transaction.
     * @param   referrer        The address of referrer who will get rebate from the deal.
     * @param   flags           The flags of the trade.
     * @return  tradeAmount     The amount of positions actually traded in the transaction. The amount always use decimals 18.
     */
    function trade(
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        int256 limitPrice,
        uint256 deadline,
        address referrer,
        uint32 flags
    )
        external
        override
        onlyAuthorized(
            trader,
            flags.useTargetLeverage()
                ? Constant.PRIVILEGE_TRADE |
                    Constant.PRIVILEGE_DEPOSIT |
                    Constant.PRIVILEGE_WITHDRAW
                : Constant.PRIVILEGE_TRADE
        )
        syncState(false)
        returns (int256 tradeAmount)
    {
        require(trader != address(0), "invalid trader");
        require(amount != 0, "invalid amount");
        require(deadline >= block.timestamp, "deadline exceeded");
        tradeAmount = _trade(perpetualIndex, trader, amount, limitPrice, referrer, flags);
    }

    /**
     * @notice  Trade with AMM by the order, initiated by the broker. order is passed in through packed data structure.
     *          All the fields of order are verified by signature.
     *          See `trade` for details.
     * @param   orderData   The order data object
     * @param   amount      The amount of position to trader, positive for buying and negative for selling.
     *                      This amount should be lower then or equal to amount in `orderData`. The amount always use decimals 18.
     * @return  tradeAmount The amount of positions actually traded in the transaction. The amount always use decimals 18.
     */
    function brokerTrade(bytes memory orderData, int256 amount)
        external
        override
        syncState(false)
        returns (int256 tradeAmount)
    {
        Order memory order = orderData.decodeOrderData();
        bytes memory signature = orderData.decodeSignature();
        _liquidityPool.validateSignature(order, signature);
        _liquidityPool.validateOrder(order, amount);
        _liquidityPool.validateTriggerPrice(order);
        tradeAmount = _trade(
            order.perpetualIndex,
            order.trader,
            amount,
            order.limitPrice,
            order.referrer,
            order.flags
        );
    }

    /**
     * @notice  Liquidate the trader if the trader's margin balance is lower than maintenance margin (unsafe).
     *          Liquidate can be considered as a forced trading between AMM and unsafe margin account;
     *          Based on current liquidity of AMM, it may take positions up to an amount equal to all the position
     *          of the unsafe account. Besides the position, trader need to pay an extra penalty to AMM
     *          for taking the unsafe assets. See TradeModule.sol for ehe strategy of penalty.
     *
     *          The liquidate price will be determined by AMM.
     *          Caller of this method can be anyone, then get a reward to make up for transaction gas fee.
     *
     *          If a trader's margin balance is lower than 0 (bankrupt), insurance fund will be use to fill the loss
     *          to make the total profit and loss balanced. (first the `insuranceFund` then the `donatedInsuranceFund`)
     *
     *          If insurance funds are drained, the state of perpetual will turn to enter "EMERGENCY" than shutdown.
     *          Can only liquidate when the perpetual's state is "NORMAL".
     *
     * @param   perpetualIndex      The index of the perpetual in liquidity pool
     * @param   trader              The address of trader to be liquidated.
     * @return  liquidationAmount   The amount of positions actually liquidated in the transaction. The amount always use decimals 18.
     */
    function liquidateByAMM(uint256 perpetualIndex, address trader)
        external
        override
        nonReentrant
        onlyNotUniverseSettled
        syncState(false)
        returns (int256 liquidationAmount)
    {
        require(_isAMMKeeper(perpetualIndex, _msgSender()), "caller must be keeper");
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        require(trader != address(0), "invalid trader");
        require(trader != address(this), "cannot liquidate AMM");
        liquidationAmount = _liquidityPool.liquidateByAMM(perpetualIndex, _msgSender(), trader);
    }

    /**
     * @notice  This method is generally consistent with `liquidateByAMM` function, but there some difference:
     *           - The liquidation price is no longer determined by AMM, but the mark price;
     *           - The penalty is taken by trader who takes position but AMM;
     *
     * @param   perpetualIndex      The index of the perpetual in liquidity pool.
     * @param   liquidator          The address of liquidator to receive the liquidated position.
     * @param   trader              The address of trader to be liquidated.
     * @param   amount              The amount of position to be taken from liquidated trader. The amount always use decimals 18.
     * @param   limitPrice          The worst price liquidator accepts.
     * @param   deadline            The deadline of transaction.
     * @return  liquidationAmount   The amount of positions actually liquidated in the transaction.
     */
    function liquidateByTrader(
        uint256 perpetualIndex,
        address liquidator,
        address trader,
        int256 amount,
        int256 limitPrice,
        uint256 deadline
    )
        external
        override
        nonReentrant
        onlyNotUniverseSettled
        onlyAuthorized(liquidator, Constant.PRIVILEGE_LIQUIDATE)
        syncState(false)
        returns (int256 liquidationAmount)
    {
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        require(trader != address(0), "invalid trader");
        require(trader != address(this), "cannot liquidate AMM");
        require(amount != 0, "invalid amount");
        require(limitPrice >= 0, "invalid limit price");
        require(deadline >= block.timestamp, "deadline exceeded");
        liquidationAmount = _liquidityPool.liquidateByTrader(
            perpetualIndex,
            liquidator,
            trader,
            amount,
            limitPrice
        );
    }

    function _trade(
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        int256 limitPrice,
        address referrer,
        uint32 flags
    ) internal onlyNotUniverseSettled returns (int256 tradeAmount) {
        require(
            _liquidityPool.perpetuals[perpetualIndex].state == PerpetualState.NORMAL,
            "perpetual should be in NORMAL state"
        );
        tradeAmount = _liquidityPool.trade(
            perpetualIndex,
            trader,
            amount,
            limitPrice,
            referrer,
            flags
        );
    }

    function _isAMMKeeper(uint256 perpetualIndex, address liquidator) internal view returns (bool) {
        EnumerableSetUpgradeable.AddressSet storage whitelist = _liquidityPool
            .perpetuals[perpetualIndex]
            .ammKeepers;
        if (whitelist.length() == 0) {
            return IPoolCreatorFull(_liquidityPool.creator).isKeeper(liquidator);
        } else {
            return whitelist.contains(liquidator);
        }
    }

    bytes32[50] private __gap;
}

File 12 of 49 : Storage.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

import "@openzeppelin/contracts-upgradeable/GSN/ContextUpgradeable.sol";

import "./interface/IPoolCreatorFull.sol";
import "./module/LiquidityPoolModule.sol";
import "./Type.sol";

contract Storage is ContextUpgradeable {
    using LiquidityPoolModule for LiquidityPoolStorage;

    LiquidityPoolStorage internal _liquidityPool;

    modifier onlyNotUniverseSettled() {
        require(!IPoolCreatorFull(_liquidityPool.creator).isUniverseSettled(), "universe settled");
        _;
    }

    modifier onlyExistedPerpetual(uint256 perpetualIndex) {
        require(perpetualIndex < _liquidityPool.perpetualCount, "perpetual not exist");
        _;
    }

    modifier syncState(bool ignoreTerminated) {
        uint256 currentTime = block.timestamp;
        _liquidityPool.updateFundingState(currentTime);
        _liquidityPool.updatePrice(ignoreTerminated);
        _;
        _liquidityPool.updateFundingRate();
    }

    modifier onlyAuthorized(address trader, uint256 privilege) {
        require(
            _liquidityPool.isAuthorized(trader, _msgSender(), privilege),
            "unauthorized caller"
        );
        _;
    }

    bytes32[28] private __gap;
}

File 13 of 49 : Type.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";

/**
 * @notice  Perpetual state:
 *          - INVALID:      Uninitialized or not non-existent perpetual;
 *          - INITIALIZING: Only when LiquidityPoolStorage.isRunning == false. Traders cannot perform operations;
 *          - NORMAL:       Full functional state. Traders is able to perform all operations;
 *          - EMERGENCY:    Perpetual is unsafe and only clear is available;
 *          - CLEARED:      All margin account is cleared. Trade could withdraw remaining margin balance.
 */
enum PerpetualState {
    INVALID,
    INITIALIZING,
    NORMAL,
    EMERGENCY,
    CLEARED
}
enum OrderType {
    LIMIT,
    MARKET,
    STOP
}

/**
 * @notice  Data structure to store risk parameter value.
 */
struct Option {
    int256 value;
    int256 minValue;
    int256 maxValue;
}

/**
 * @notice  Data structure to store oracle price data.
 */
struct OraclePriceData {
    int256 price;
    uint256 time;
}

/**
 * @notice  Data structure to store user margin information. See MarginAccountModule.sol for details.
 */
struct MarginAccount {
    int256 cash;
    int256 position;
    int256 targetLeverage;
}

/**
 * @notice  Data structure of an order object.
 */
struct Order {
    address trader;
    address broker;
    address relayer;
    address referrer;
    address liquidityPool;
    int256 minTradeAmount;
    int256 amount;
    int256 limitPrice;
    int256 triggerPrice;
    uint256 chainID;
    uint64 expiredAt;
    uint32 perpetualIndex;
    uint32 brokerFeeLimit;
    uint32 flags;
    uint32 salt;
}

/**
 * @notice  Core data structure, a core .
 */
struct LiquidityPoolStorage {
    bool isRunning;
    bool isFastCreationEnabled;
    // addresses
    address creator;
    address operator;
    address transferringOperator;
    address governor;
    address shareToken;
    address accessController;
    bool reserved3; // isWrapped
    uint256 scaler;
    uint256 collateralDecimals;
    address collateralToken;
    // pool attributes
    int256 poolCash;
    uint256 fundingTime;
    uint256 reserved5;
    uint256 operatorExpiration;
    mapping(address => int256) reserved1;
    bytes32[] reserved2;
    // perpetuals
    uint256 perpetualCount;
    mapping(uint256 => PerpetualStorage) perpetuals;
    // insurance fund
    int256 insuranceFundCap;
    int256 insuranceFund;
    int256 donatedInsuranceFund;
    address reserved4;
    uint256 liquidityCap;
    uint256 shareTransferDelay;
    // reserved slot for future upgrade
    bytes32[14] reserved;
}

/**
 * @notice  Core data structure, storing perpetual information.
 */
struct PerpetualStorage {
    uint256 id;
    PerpetualState state;
    address oracle;
    int256 totalCollateral;
    int256 openInterest;
    // prices
    OraclePriceData indexPriceData;
    OraclePriceData markPriceData;
    OraclePriceData settlementPriceData;
    // funding state
    int256 fundingRate;
    int256 unitAccumulativeFunding;
    // base parameters
    int256 initialMarginRate;
    int256 maintenanceMarginRate;
    int256 operatorFeeRate;
    int256 lpFeeRate;
    int256 referralRebateRate;
    int256 liquidationPenaltyRate;
    int256 keeperGasReward;
    int256 insuranceFundRate;
    int256 reserved1;
    int256 maxOpenInterestRate;
    // risk parameters
    Option halfSpread;
    Option openSlippageFactor;
    Option closeSlippageFactor;
    Option fundingRateLimit;
    Option fundingRateFactor;
    Option ammMaxLeverage;
    Option maxClosePriceDiscount;
    // users
    uint256 totalAccount;
    int256 totalMarginWithoutPosition;
    int256 totalMarginWithPosition;
    int256 redemptionRateWithoutPosition;
    int256 redemptionRateWithPosition;
    EnumerableSetUpgradeable.AddressSet activeAccounts;
    // insurance fund
    int256 reserved2;
    int256 reserved3;
    // accounts
    mapping(address => MarginAccount) marginAccounts;
    Option defaultTargetLeverage;
    // keeper
    address reserved4;
    EnumerableSetUpgradeable.AddressSet ammKeepers;
    EnumerableSetUpgradeable.AddressSet reserved5;
    Option baseFundingRate;
    // reserved slot for future upgrade
    bytes32[9] reserved;
}

File 14 of 49 : 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 15 of 49 : Constant.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

library Constant {
    address internal constant INVALID_ADDRESS = address(0);

    int256 internal constant SIGNED_ONE = 10**18;
    uint256 internal constant UNSIGNED_ONE = 10**18;

    uint256 internal constant PRIVILEGE_DEPOSIT = 0x1;
    uint256 internal constant PRIVILEGE_WITHDRAW = 0x2;
    uint256 internal constant PRIVILEGE_TRADE = 0x4;
    uint256 internal constant PRIVILEGE_LIQUIDATE = 0x8;
    uint256 internal constant PRIVILEGE_GUARD =
        PRIVILEGE_DEPOSIT | PRIVILEGE_WITHDRAW | PRIVILEGE_TRADE | PRIVILEGE_LIQUIDATE;
    // max number of uint256
    uint256 internal constant SET_ALL_PERPETUALS_TO_EMERGENCY_STATE =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
}

File 16 of 49 : Math.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.7.4;

library Math {
    /**
     * @dev Get the most significant bit of the number,
            example: 0 ~ 1 => 0, 2 ~ 3 => 1, 4 ~ 7 => 2, 8 ~ 15 => 3,
            about use 606 ~ 672 gas
     * @param x The number
     * @return uint8 The significant bit of the number
     */
    function mostSignificantBit(uint256 x) internal pure returns (uint8) {
        uint256 t;
        uint8 r;
        if ((t = (x >> 128)) > 0) {
            x = t;
            r += 128;
        }
        if ((t = (x >> 64)) > 0) {
            x = t;
            r += 64;
        }
        if ((t = (x >> 32)) > 0) {
            x = t;
            r += 32;
        }
        if ((t = (x >> 16)) > 0) {
            x = t;
            r += 16;
        }
        if ((t = (x >> 8)) > 0) {
            x = t;
            r += 8;
        }
        if ((t = (x >> 4)) > 0) {
            x = t;
            r += 4;
        }
        if ((t = (x >> 2)) > 0) {
            x = t;
            r += 2;
        }
        if ((t = (x >> 1)) > 0) {
            x = t;
            r += 1;
        }
        return r;
    }

    // https://en.wikipedia.org/wiki/Integer_square_root
    /**
     * @dev Get the square root of the number
     * @param x The number, usually 10^36
     * @return int256 The square root of the number, usually 10^18
     */
    function sqrt(int256 x) internal pure returns (int256) {
        require(x >= 0, "negative sqrt");
        if (x < 3) {
            return (x + 1) / 2;
        }

        // binary estimate
        // inspired by https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_estimates
        uint8 n = mostSignificantBit(uint256(x));
        // make sure initial estimate > sqrt(x)
        // 2^ceil((n + 1) / 2) as initial estimate
        // 2^(n + 1) > x
        // => 2^ceil((n + 1) / 2) > 2^((n + 1) / 2) > sqrt(x)
        n = (n + 1) / 2 + 1;

        // modified babylonian method
        int256 next = int256(1 << n);
        int256 y;
        do {
            y = next;
            next = (next + x / next) >> 1;
        } while (next < y);
        return y;
    }
}

File 17 of 49 : SafeMathExt.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

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

import "./Constant.sol";
import "./Utils.sol";

enum Round {
    CEIL,
    FLOOR
}

library SafeMathExt {
    using SafeMathUpgradeable for uint256;
    using SignedSafeMathUpgradeable for int256;

    /*
     * @dev Always half up for uint256
     */
    function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.mul(y).add(Constant.UNSIGNED_ONE / 2) / Constant.UNSIGNED_ONE;
    }

    /*
     * @dev Always half up for uint256
     */
    function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.mul(Constant.UNSIGNED_ONE).add(y / 2).div(y);
    }

    /*
     * @dev Always half up for uint256
     */
    function wfrac(
        uint256 x,
        uint256 y,
        uint256 z
    ) internal pure returns (uint256 r) {
        r = x.mul(y).add(z / 2).div(z);
    }

    /*
     * @dev Always half up if no rounding parameter
     */
    function wmul(int256 x, int256 y) internal pure returns (int256 z) {
        z = roundHalfUp(x.mul(y), Constant.SIGNED_ONE) / Constant.SIGNED_ONE;
    }

    /*
     * @dev Always half up if no rounding parameter
     */
    function wdiv(int256 x, int256 y) internal pure returns (int256 z) {
        if (y < 0) {
            y = neg(y);
            x = neg(x);
        }
        z = roundHalfUp(x.mul(Constant.SIGNED_ONE), y).div(y);
    }

    /*
     * @dev Always half up if no rounding parameter
     */
    function wfrac(
        int256 x,
        int256 y,
        int256 z
    ) internal pure returns (int256 r) {
        int256 t = x.mul(y);
        if (z < 0) {
            z = neg(z);
            t = neg(t);
        }
        r = roundHalfUp(t, z).div(z);
    }

    function wmul(
        int256 x,
        int256 y,
        Round round
    ) internal pure returns (int256 z) {
        z = div(x.mul(y), Constant.SIGNED_ONE, round);
    }

    function wdiv(
        int256 x,
        int256 y,
        Round round
    ) internal pure returns (int256 z) {
        z = div(x.mul(Constant.SIGNED_ONE), y, round);
    }

    function wfrac(
        int256 x,
        int256 y,
        int256 z,
        Round round
    ) internal pure returns (int256 r) {
        int256 t = x.mul(y);
        r = div(t, z, round);
    }

    function abs(int256 x) internal pure returns (int256) {
        return x >= 0 ? x : neg(x);
    }

    function neg(int256 a) internal pure returns (int256) {
        return SignedSafeMathUpgradeable.sub(int256(0), a);
    }

    /*
     * @dev ROUND_HALF_UP rule helper.
     *      You have to call roundHalfUp(x, y) / y to finish the rounding operation.
     *      0.5 ≈ 1, 0.4 ≈ 0, -0.5 ≈ -1, -0.4 ≈ 0
     */
    function roundHalfUp(int256 x, int256 y) internal pure returns (int256) {
        require(y > 0, "roundHalfUp only supports y > 0");
        if (x >= 0) {
            return x.add(y / 2);
        }
        return x.sub(y / 2);
    }

    /*
     * @dev Division, rounding ceil or rounding floor
     */
    function div(
        int256 x,
        int256 y,
        Round round
    ) internal pure returns (int256 divResult) {
        require(y != 0, "division by zero");
        divResult = x.div(y);
        if (x % y == 0) {
            return divResult;
        }
        bool isSameSign = Utils.hasTheSameSign(x, y);
        if (round == Round.CEIL && isSameSign) {
            divResult = divResult.add(1);
        }
        if (round == Round.FLOOR && !isSameSign) {
            divResult = divResult.sub(1);
        }
    }

    function max(int256 a, int256 b) internal pure returns (int256) {
        return a >= b ? a : b;
    }

    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}

File 18 of 49 : Utils.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";

import "./SafeMathExt.sol";

library Utils {
    using SafeMathExt for int256;
    using SafeMathExt for uint256;
    using SafeMathUpgradeable for uint256;
    using SignedSafeMathUpgradeable for int256;
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.Bytes32Set;

    /*
     * @dev Check if two numbers have the same sign. Zero has the same sign with any number
     */
    function hasTheSameSign(int256 x, int256 y) internal pure returns (bool) {
        if (x == 0 || y == 0) {
            return true;
        }
        return (x ^ y) >> 255 == 0;
    }

    /**
     * @dev     Check if the trader has opened position in the trade.
     *          Example: 2, 1 => true; 2, -1 => false; -2, -3 => true
     * @param   amount  The position of the trader after the trade
     * @param   delta   The update position amount of the trader after the trade
     * @return  True if the trader has opened position in the trade
     */
    function hasOpenedPosition(int256 amount, int256 delta) internal pure returns (bool) {
        if (amount == 0) {
            return false;
        }
        return Utils.hasTheSameSign(amount, delta);
    }

    /*
     * @dev Split the delta to two numbers.
     *      Use for splitting the trading amount to the amount to close position and the amount to open position.
     *      Examples: 2, 1 => 0, 1; 2, -1 => -1, 0; 2, -3 => -2, -1
     */
    function splitAmount(int256 amount, int256 delta) internal pure returns (int256, int256) {
        if (Utils.hasTheSameSign(amount, delta)) {
            return (0, delta);
        } else if (amount.abs() >= delta.abs()) {
            return (delta, 0);
        } else {
            return (amount.neg(), amount.add(delta));
        }
    }

    /*
     * @dev Check if amount will be away from zero or cross zero if added the delta.
     *      Use for checking if trading amount will make trader open position.
     *      Example: 2, 1 => true; 2, -1 => false; 2, -3 => true
     */
    function isOpen(int256 amount, int256 delta) internal pure returns (bool) {
        return Utils.hasTheSameSign(amount, delta) || amount.abs() < delta.abs();
    }

    /*
     * @dev Get the id of the current chain
     */
    function chainID() internal pure returns (uint256 id) {
        assembly {
            id := chainid()
        }
    }

    // function toArray(
    //     EnumerableSet.AddressSet storage set,
    //     uint256 begin,
    //     uint256 end
    // ) internal view returns (address[] memory result) {
    //     require(end > begin, "begin should be lower than end");
    //     uint256 length = set.length();
    //     if (begin >= length) {
    //         return result;
    //     }
    //     uint256 safeEnd = end.min(length);
    //     result = new address[](safeEnd.sub(begin));
    //     for (uint256 i = begin; i < safeEnd; i++) {
    //         result[i.sub(begin)] = set.at(i);
    //     }
    //     return result;
    // }

    function toArray(
        EnumerableSetUpgradeable.AddressSet storage set,
        uint256 begin,
        uint256 end
    ) internal view returns (address[] memory result) {
        require(end > begin, "begin should be lower than end");
        uint256 length = set.length();
        if (begin >= length) {
            return result;
        }
        uint256 safeEnd = end.min(length);
        result = new address[](safeEnd.sub(begin));
        for (uint256 i = begin; i < safeEnd; i++) {
            result[i.sub(begin)] = set.at(i);
        }
        return result;
    }

    function toArray(
        EnumerableSetUpgradeable.Bytes32Set storage set,
        uint256 begin,
        uint256 end
    ) internal view returns (bytes32[] memory result) {
        require(end > begin, "begin should be lower than end");
        uint256 length = set.length();
        if (begin >= length) {
            return result;
        }
        uint256 safeEnd = end.min(length);
        result = new bytes32[](safeEnd.sub(begin));
        for (uint256 i = begin; i < safeEnd; i++) {
            result[i.sub(begin)] = set.at(i);
        }
        return result;
    }
}

File 19 of 49 : MarginAccountModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;

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

import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";
import "../libraries/OrderData.sol";

import "../Type.sol";

library MarginAccountModule {
    using SafeMathExt for int256;
    using SafeCastUpgradeable for uint256;
    using SignedSafeMathUpgradeable for int256;
    using OrderData for uint32;

    /**
     * @dev Get the initial margin of the trader in the perpetual.
     *      Initial margin = price * abs(position) * initial margin rate
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the initial margin
     * @return initialMargin The initial margin of the trader in the perpetual
     */
    function getInitialMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (int256 initialMargin) {
        initialMargin = perpetual
            .marginAccounts[trader]
            .position
            .wmul(price)
            .wmul(perpetual.initialMarginRate)
            .abs();
    }

    /**
     * @dev Get the maintenance margin of the trader in the perpetual.
     *      Maintenance margin = price * abs(position) * maintenance margin rate
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the  maintenance margin
     * @return maintenanceMargin The maintenance margin of the trader in the perpetual
     */
    function getMaintenanceMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (int256 maintenanceMargin) {
        maintenanceMargin = perpetual
            .marginAccounts[trader]
            .position
            .wmul(price)
            .wmul(perpetual.maintenanceMarginRate)
            .abs();
    }

    /**
     * @dev Get the available cash of the trader in the perpetual.
     *      Available cash = cash - position * unit accumulative funding
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @return availableCash The available cash of the trader in the perpetual
     */
    function getAvailableCash(PerpetualStorage storage perpetual, address trader)
        internal
        view
        returns (int256 availableCash)
    {
        MarginAccount storage account = perpetual.marginAccounts[trader];
        availableCash = account.cash.sub(account.position.wmul(perpetual.unitAccumulativeFunding));
    }

    /**
     * @dev Get the position of the trader in the perpetual
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @return position The position of the trader in the perpetual
     */
    function getPosition(PerpetualStorage storage perpetual, address trader)
        internal
        view
        returns (int256 position)
    {
        position = perpetual.marginAccounts[trader].position;
    }

    /**
     * @dev Get the margin of the trader in the perpetual.
     *      Margin = available cash + position * price
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the margin
     * @return margin The margin of the trader in the perpetual
     */
    function getMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (int256 margin) {
        margin = perpetual.marginAccounts[trader].position.wmul(price).add(
            getAvailableCash(perpetual, trader)
        );
    }

    /**
     * @dev Get the settleable margin of the trader in the perpetual.
     *      This is the margin trader can withdraw when the state of the perpetual is "CLEARED".
     *      If the state of the perpetual is not "CLEARED", the settleable margin is always zero
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the settleable margin
     * @return margin The settleable margin of the trader in the perpetual
     */
    function getSettleableMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (int256 margin) {
        margin = getMargin(perpetual, trader, price);
        if (margin > 0) {
            int256 rate = (getPosition(perpetual, trader) == 0)
                ? perpetual.redemptionRateWithoutPosition
                : perpetual.redemptionRateWithPosition;
            // make sure total redemption margin < total collateral of perpetual
            margin = margin.wmul(rate, Round.FLOOR);
        } else {
            margin = 0;
        }
    }

    /**
     * @dev     Get the available margin of the trader in the perpetual.
     *          Available margin = margin - (initial margin + keeper gas reward), keeper gas reward = 0 if position = 0
     * @param   perpetual   The perpetual object
     * @param   trader      The address of the trader
     * @param   price       The price to calculate available margin
     * @return  availableMargin The available margin of the trader in the perpetual
     */
    function getAvailableMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (int256 availableMargin) {
        int256 threshold = getPosition(perpetual, trader) == 0
            ? 0
            : getInitialMargin(perpetual, trader, price).add(perpetual.keeperGasReward);
        availableMargin = getMargin(perpetual, trader, price).sub(threshold);
    }

    /**
     * @dev     Check if the trader is initial margin safe in the perpetual, which means available margin >= 0
     * @param   perpetual   The perpetual object
     * @param   trader      The address of the trader
     * @param   price       The price to calculate the available margin
     * @return  isSafe      True if the trader is initial margin safe in the perpetual
     */
    function isInitialMarginSafe(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (bool isSafe) {
        isSafe = (getAvailableMargin(perpetual, trader, price) >= 0);
    }

    /**
     * @dev Check if the trader is maintenance margin safe in the perpetual, which means
     *      margin >= maintenance margin + keeper gas reward. Keeper gas reward = 0 if position = 0
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the maintenance margin
     * @return isSafe True if the trader is maintenance margin safe in the perpetual
     */
    function isMaintenanceMarginSafe(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (bool isSafe) {
        int256 threshold = getPosition(perpetual, trader) == 0
            ? 0
            : getMaintenanceMargin(perpetual, trader, price).add(perpetual.keeperGasReward);
        isSafe = getMargin(perpetual, trader, price) >= threshold;
    }

    /**
     * @dev Check if the trader is margin safe in the perpetual, which means margin >= keeper gas reward.
     *      Keeper gas reward = 0 if position = 0
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param price The price to calculate the margin
     * @return isSafe True if the trader is margin safe in the perpetual
     */
    function isMarginSafe(
        PerpetualStorage storage perpetual,
        address trader,
        int256 price
    ) internal view returns (bool isSafe) {
        int256 threshold = getPosition(perpetual, trader) == 0 ? 0 : perpetual.keeperGasReward;
        isSafe = getMargin(perpetual, trader, price) >= threshold;
    }

    /**
     * @dev Check if the account of the trader is empty in the perpetual, which means cash = 0 and position = 0
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @return isEmpty True if the account of the trader is empty in the perpetual
     */
    function isEmptyAccount(PerpetualStorage storage perpetual, address trader)
        internal
        view
        returns (bool isEmpty)
    {
        MarginAccount storage account = perpetual.marginAccounts[trader];
        isEmpty = (account.cash == 0 && account.position == 0);
    }

    /**
     * @dev Update the trader's cash in the perpetual
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param deltaCash The update cash(collateral) of the trader's account in the perpetual
     */
    function updateCash(
        PerpetualStorage storage perpetual,
        address trader,
        int256 deltaCash
    ) internal {
        if (deltaCash == 0) {
            return;
        }
        MarginAccount storage account = perpetual.marginAccounts[trader];
        account.cash = account.cash.add(deltaCash);
    }

    /**
     * @dev Update the trader's account in the perpetual
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     * @param deltaPosition The update position of the trader's account in the perpetual
     * @param deltaCash The update cash(collateral) of the trader's account in the perpetual
     */
    function updateMargin(
        PerpetualStorage storage perpetual,
        address trader,
        int256 deltaPosition,
        int256 deltaCash
    ) internal returns (int256 deltaOpenInterest) {
        MarginAccount storage account = perpetual.marginAccounts[trader];
        int256 oldPosition = account.position;
        account.position = account.position.add(deltaPosition);
        account.cash = account.cash.add(deltaCash).add(
            perpetual.unitAccumulativeFunding.wmul(deltaPosition)
        );
        if (oldPosition > 0) {
            deltaOpenInterest = oldPosition.neg();
        }
        if (account.position > 0) {
            deltaOpenInterest = deltaOpenInterest.add(account.position);
        }
        perpetual.openInterest = perpetual.openInterest.add(deltaOpenInterest);
    }

    /**
     * @dev Reset the trader's account in the perpetual to empty, which means position = 0 and cash = 0
     * @param perpetual The perpetual object
     * @param trader The address of the trader
     */
    function resetAccount(PerpetualStorage storage perpetual, address trader) internal {
        MarginAccount storage account = perpetual.marginAccounts[trader];
        account.cash = 0;
        account.position = 0;
    }

    // deprecated
    function setTargetLeverage(
        PerpetualStorage storage perpetual,
        address trader,
        int256 targetLeverage
    ) internal {
        perpetual.marginAccounts[trader].targetLeverage = targetLeverage;
    }

    function getTargetLeverage(PerpetualStorage storage perpetual, address trader)
        internal
        view
        returns (int256)
    {
        require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
        int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
        int256 targetLeverage = perpetual.marginAccounts[trader].targetLeverage;
        targetLeverage = targetLeverage == 0
            ? perpetual.defaultTargetLeverage.value
            : targetLeverage;
        return targetLeverage.min(maxLeverage);
    }

    function getTargetLeverageWithFlags(
        PerpetualStorage storage perpetual,
        address trader,
        uint32 flags
    ) internal view returns (int256 targetLeverage) {
        require(perpetual.initialMarginRate != 0, "initialMarginRate is not set");
        int256 maxLeverage = Constant.SIGNED_ONE.wdiv(perpetual.initialMarginRate);
        bool _oldUseTargetLeverage = flags.oldUseTargetLeverage();
        bool _newUseTargetLeverage = flags.newUseTargetLeverage();
        require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
        if (_oldUseTargetLeverage) {
            targetLeverage = perpetual.marginAccounts[trader].targetLeverage;
        } else {
            targetLeverage = flags.getTargetLeverageByFlags();
        }
        targetLeverage = targetLeverage == 0
            ? perpetual.defaultTargetLeverage.value
            : targetLeverage;
        targetLeverage = targetLeverage.min(maxLeverage);
    }
}

File 20 of 49 : 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, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @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) {
        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, reverting 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) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * 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);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * 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);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * 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 49 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 22 of 49 : OrderData.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "../libraries/Utils.sol";
import "../Type.sol";

library OrderData {
    uint32 internal constant MASK_CLOSE_ONLY = 0x80000000;
    uint32 internal constant MASK_MARKET_ORDER = 0x40000000;
    uint32 internal constant MASK_STOP_LOSS_ORDER = 0x20000000;
    uint32 internal constant MASK_TAKE_PROFIT_ORDER = 0x10000000;
    uint32 internal constant MASK_USE_TARGET_LEVERAGE = 0x08000000;

    // old domain, will be removed in future
    string internal constant DOMAIN_NAME = "Mai Protocol v3";
    bytes32 internal constant EIP712_DOMAIN_TYPEHASH =
        keccak256(abi.encodePacked("EIP712Domain(string name)"));
    bytes32 internal constant DOMAIN_SEPARATOR =
        keccak256(abi.encodePacked(EIP712_DOMAIN_TYPEHASH, keccak256(bytes(DOMAIN_NAME))));
    bytes32 internal constant EIP712_ORDER_TYPE =
        keccak256(
            abi.encodePacked(
                "Order(address trader,address broker,address relayer,address referrer,address liquidityPool,",
                "int256 minTradeAmount,int256 amount,int256 limitPrice,int256 triggerPrice,uint256 chainID,",
                "uint64 expiredAt,uint32 perpetualIndex,uint32 brokerFeeLimit,uint32 flags,uint32 salt)"
            )
        );

    /*
     * @dev Check if the order is close-only order. Close-only order means the order can only close position
     *      of the trader
     * @param order The order object
     * @return bool True if the order is close-only order
     */
    function isCloseOnly(Order memory order) internal pure returns (bool) {
        return (order.flags & MASK_CLOSE_ONLY) > 0;
    }

    /*
     * @dev Check if the order is market order. Market order means the order which has no limit price, should be
     *      executed immediately
     * @param order The order object
     * @return bool True if the order is market order
     */
    function isMarketOrder(Order memory order) internal pure returns (bool) {
        return (order.flags & MASK_MARKET_ORDER) > 0;
    }

    /*
     * @dev Check if the order is stop-loss order. Stop-loss order means the order will trigger when the
     *      price is worst than the trigger price
     * @param order The order object
     * @return bool True if the order is stop-loss order
     */
    function isStopLossOrder(Order memory order) internal pure returns (bool) {
        return (order.flags & MASK_STOP_LOSS_ORDER) > 0;
    }

    /*
     * @dev Check if the order is take-profit order. Take-profit order means the order will trigger when
     *      the price is better than the trigger price
     * @param order The order object
     * @return bool True if the order is take-profit order
     */
    function isTakeProfitOrder(Order memory order) internal pure returns (bool) {
        return (order.flags & MASK_TAKE_PROFIT_ORDER) > 0;
    }

    /*
     * @dev Check if the flags contain close-only flag
     * @param flags The flags
     * @return bool True if the flags contain close-only flag
     */
    function isCloseOnly(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_CLOSE_ONLY) > 0;
    }

    /*
     * @dev Check if the flags contain market flag
     * @param flags The flags
     * @return bool True if the flags contain market flag
     */
    function isMarketOrder(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_MARKET_ORDER) > 0;
    }

    /*
     * @dev Check if the flags contain stop-loss flag
     * @param flags The flags
     * @return bool True if the flags contain stop-loss flag
     */
    function isStopLossOrder(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_STOP_LOSS_ORDER) > 0;
    }

    /*
     * @dev Check if the flags contain take-profit flag
     * @param flags The flags
     * @return bool True if the flags contain take-profit flag
     */
    function isTakeProfitOrder(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_TAKE_PROFIT_ORDER) > 0;
    }

    function oldUseTargetLeverage(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_USE_TARGET_LEVERAGE) > 0;
    }

    function newUseTargetLeverage(uint32 flags) internal pure returns (bool) {
        return getTargetLeverageByFlags(flags) > 0;
    }

    function getTargetLeverageByFlags(uint32 flags) internal pure returns (int256) {
        return int256((flags >> 7) & 0xfffff) * 10**16;
    }

    function useTargetLeverage(uint32 flags) internal pure returns (bool) {
        bool _oldUseTargetLeverage = oldUseTargetLeverage(flags);
        bool _newUseTargetLeverage = newUseTargetLeverage(flags);
        require(!(_oldUseTargetLeverage && _newUseTargetLeverage), "invalid flags");
        return _oldUseTargetLeverage || _newUseTargetLeverage;
    }

    /*
     * @dev Get the hash of the order
     * @param order The order object
     * @return bytes32 The hash of the order
     */
    function getOrderHash(Order memory order) internal pure returns (bytes32) {
        bytes32 result = keccak256(abi.encode(EIP712_ORDER_TYPE, order));
        return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, result));
    }

    /*
     * @dev Decode the signature from the data
     * @param data The data object to decode
     * @return signature The signature
     */
    function decodeSignature(bytes memory data) internal pure returns (bytes memory signature) {
        require(data.length >= 350, "broken data");
        bytes32 r;
        bytes32 s;
        uint8 v;
        uint8 signType;
        assembly {
            r := mload(add(data, 318))
            s := mload(add(data, 350))
            v := byte(24, mload(add(data, 292)))
            signType := byte(25, mload(add(data, 292)))
        }
        signature = abi.encodePacked(r, s, v, signType);
    }

    /*
     * @dev Decode the order from the data
     * @param data The data object to decode
     * @return order The order
     */
    function decodeOrderData(bytes memory data) internal pure returns (Order memory order) {
        require(data.length >= 256, "broken data");
        bytes32 tmp;
        assembly {
            // trader / 20
            mstore(add(order, 0), mload(add(data, 20)))
            // broker / 20
            mstore(add(order, 32), mload(add(data, 40)))
            // relayer / 20
            mstore(add(order, 64), mload(add(data, 60)))
            // referrer / 20
            mstore(add(order, 96), mload(add(data, 80)))
            // liquidityPool / 20
            mstore(add(order, 128), mload(add(data, 100)))
            // minTradeAmount / 32
            mstore(add(order, 160), mload(add(data, 132)))
            // amount / 32
            mstore(add(order, 192), mload(add(data, 164)))
            // limitPrice / 32
            mstore(add(order, 224), mload(add(data, 196)))
            // triggerPrice / 32
            mstore(add(order, 256), mload(add(data, 228)))
            // chainID / 32
            mstore(add(order, 288), mload(add(data, 260)))
            // expiredAt + perpetualIndex + brokerFeeLimit + flags + salt + v + signType / 26
            tmp := mload(add(data, 292))
        }
        order.expiredAt = uint64(bytes8(tmp));
        order.perpetualIndex = uint32(bytes4(tmp << 64));
        order.brokerFeeLimit = uint32(bytes4(tmp << 96));
        order.flags = uint32(bytes4(tmp << 128));
        order.salt = uint32(bytes4(tmp << 160));
    }
}

File 23 of 49 : 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);
            }
        }
    }
}

File 24 of 49 : IOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface IOracle {
    /**
     * @dev The market is closed if the market is not in its regular trading period.
     */
    function isMarketClosed() external returns (bool);

    /**
     * @dev The oracle service was shutdown and never online again.
     */
    function isTerminated() external returns (bool);

    /**
     * @dev Get collateral symbol.
     */
    function collateral() external view returns (string memory);

    /**
     * @dev Get underlying asset symbol.
     */
    function underlyingAsset() external view returns (string memory);

    /**
     * @dev Mark price.
     */
    function priceTWAPLong() external returns (int256 newPrice, uint256 newTimestamp);

    /**
     * @dev Index price.
     */
    function priceTWAPShort() external returns (int256 newPrice, uint256 newTimestamp);
}

File 25 of 49 : IAccessControl.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface IAccessControl {
    function grantPrivilege(address trader, uint256 privilege) external;

    function revokePrivilege(address trader, uint256 privilege) external;

    function isGranted(
        address owner,
        address trader,
        uint256 privilege
    ) external view returns (bool);
}

File 26 of 49 : IGovernor.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IGovernor {
    function initialize(
        string memory name,
        string memory symbol,
        address minter,
        address target_,
        address rewardToken,
        address poolCreator
    ) external;

    function totalSupply() external view returns (uint256);

    function getTarget() external view returns (address);

    function mint(address account, uint256 amount) external;

    function burn(address account, uint256 amount) external;

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

File 27 of 49 : IPoolCreatorFull.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "./IAccessControl.sol";
import "./IPoolCreator.sol";
import "./ITracer.sol";
import "./IVersionControl.sol";
import "./IVariables.sol";
import "./IKeeperWhitelist.sol";

interface IPoolCreatorFull is
    IPoolCreator,
    IVersionControl,
    ITracer,
    IVariables,
    IAccessControl,
    IKeeperWhitelist
{
    /**
     * @notice Owner of version control.
     */
    function owner() external view override(IVersionControl, IVariables) returns (address);
}

File 28 of 49 : ISymbolService.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface ISymbolService {
    function isWhitelistedFactory(address factory) external view returns (bool);

    function addWhitelistedFactory(address factory) external;

    function removeWhitelistedFactory(address factory) external;

    function getPerpetualUID(uint256 symbol)
        external
        view
        returns (address liquidityPool, uint256 perpetualIndex);

    function getSymbols(address liquidityPool, uint256 perpetualIndex)
        external
        view
        returns (uint256[] memory symbols);

    function allocateSymbol(address liquidityPool, uint256 perpetualIndex) external;

    function assignReservedSymbol(
        address liquidityPool,
        uint256 perpetualIndex,
        uint256 symbol
    ) external;
}

File 29 of 49 : CollateralModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol";

import "../interface/IDecimals.sol";

import "../libraries/Constant.sol";

import "../Type.sol";

/**
 * @title   Collateral Module
 * @dev     Handle underlying collaterals.
 *          In this file, parameter named with:
 *              - [amount] means internal amount
 *              - [rawAmount] means amount in decimals of underlying collateral
 */
library CollateralModule {
    using SafeMathUpgradeable for uint256;
    using SafeCastUpgradeable for int256;
    using SafeCastUpgradeable for uint256;
    using SignedSafeMathUpgradeable for int256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    uint256 internal constant SYSTEM_DECIMALS = 18;

    /**
     * @notice  Initialize the collateral of the liquidity pool. Set up address, scaler and decimals of collateral
     * @param   liquidityPool       The liquidity pool object
     * @param   collateral          The address of the collateral
     * @param   collateralDecimals  The decimals of the collateral, must less than SYSTEM_DECIMALS,
     *                              must equal to decimals() if the function exists
     */
    function initializeCollateral(
        LiquidityPoolStorage storage liquidityPool,
        address collateral,
        uint256 collateralDecimals
    ) public {
        require(collateralDecimals <= SYSTEM_DECIMALS, "collateral decimals is out of range");
        try IDecimals(collateral).decimals() returns (uint8 decimals) {
            require(decimals == collateralDecimals, "decimals not match");
        } catch {}
        uint256 factor = 10**(SYSTEM_DECIMALS.sub(collateralDecimals));
        liquidityPool.scaler = (factor == 0 ? 1 : factor);
        liquidityPool.collateralToken = collateral;
        liquidityPool.collateralDecimals = collateralDecimals;
    }

    /**
     * @notice  Transfer collateral from the account to the liquidity pool.
     * @param   liquidityPool   The liquidity pool object
     * @param   account         The address of the account
     * @param   amount          The amount of erc20 token to transfer. Always use decimals 18.
     */
    function transferFromUser(
        LiquidityPoolStorage storage liquidityPool,
        address account,
        int256 amount
    ) public {
        if (amount <= 0) {
            return;
        }
        uint256 rawAmount = _toRawAmountRoundUp(liquidityPool, amount);
        IERC20Upgradeable collateralToken = IERC20Upgradeable(liquidityPool.collateralToken);
        uint256 previousBalance = collateralToken.balanceOf(address(this));
        collateralToken.safeTransferFrom(account, address(this), rawAmount);
        uint256 postBalance = collateralToken.balanceOf(address(this));
        require(postBalance.sub(previousBalance) == rawAmount, "incorrect transferred in amount");
    }

    /**
     * @notice  Transfer collateral from the liquidity pool to the account.
     * @param   liquidityPool   The liquidity pool object
     * @param   account         The address of the account
     * @param   amount          The amount of collateral to transfer. always use decimals 18.
     */
    function transferToUser(
        LiquidityPoolStorage storage liquidityPool,
        address account,
        int256 amount
    ) public {
        if (amount <= 0) {
            return;
        }
        uint256 rawAmount = _toRawAmount(liquidityPool, amount);
        IERC20Upgradeable collateralToken = IERC20Upgradeable(liquidityPool.collateralToken);
        uint256 previousBalance = collateralToken.balanceOf(address(this));
        collateralToken.safeTransfer(account, rawAmount);
        uint256 postBalance = collateralToken.balanceOf(address(this));
        require(previousBalance.sub(postBalance) == rawAmount, "incorrect transferred out amount");
    }

    function _toRawAmount(LiquidityPoolStorage storage liquidityPool, int256 amount)
        private
        view
        returns (uint256 rawAmount)
    {
        rawAmount = amount.toUint256().div(liquidityPool.scaler);
    }

    function _toRawAmountRoundUp(LiquidityPoolStorage storage liquidityPool, int256 amount)
        private
        view
        returns (uint256 rawAmount)
    {
        rawAmount = amount.toUint256();
        rawAmount = rawAmount.div(liquidityPool.scaler).add(
            rawAmount % liquidityPool.scaler > 0 ? 1 : 0
        );
    }
}

File 30 of 49 : IPoolCreator.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "./IProxyAdmin.sol";

interface IPoolCreator {
    function upgradeAdmin() external view returns (IProxyAdmin proxyAdmin);

    /**
     * @notice  Create a liquidity pool with the latest version.
     *          The sender will be the operator of pool.
     *
     * @param   collateral              he collateral address of the liquidity pool.
     * @param   collateralDecimals      The collateral's decimals of the liquidity pool.
     * @param   nonce                   A random nonce to calculate the address of deployed contracts.
     * @param   initData                A bytes array contains data to initialize new created liquidity pool.
     * @return  liquidityPool           The address of the created liquidity pool.
     */
    function createLiquidityPool(
        address collateral,
        uint256 collateralDecimals,
        int256 nonce,
        bytes calldata initData
    ) external returns (address liquidityPool, address governor);

    /**
     * @notice  Upgrade a liquidity pool and governor pair then call a patch function on the upgraded contract (optional).
     *          This method checks the sender and forwards the request to ProxyAdmin to do upgrading.
     *
     * @param   targetVersionKey        The key of version to be upgrade up. The target version must be compatible with
     *                                  current version.
     * @param   dataForLiquidityPool    The patch calldata for upgraded liquidity pool.
     * @param   dataForGovernor         The patch calldata of upgraded governor.
     */
    function upgradeToAndCall(
        bytes32 targetVersionKey,
        bytes memory dataForLiquidityPool,
        bytes memory dataForGovernor
    ) external;

    /**
     * @notice  Indicates the universe settle state.
     *          If the flag set to true:
     *              - all the pereptual created by this poolCreator can be settled immediately;
     *              - all the trading method will be unavailable.
     */
    function isUniverseSettled() external view returns (bool);
}

File 31 of 49 : ITracer.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "./IProxyAdmin.sol";

interface ITracer {
    /**
     * @notice  Activate the perpetual for the trader. Active means the trader's account is not empty in
     *          the perpetual. Empty means cash and position are zero. Can only called by a liquidity pool.
     *
     * @param   trader          The address of the trader.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @return  True if the activation is successful.
     */
    function activatePerpetualFor(address trader, uint256 perpetualIndex) external returns (bool);

    /**
     * @notice  Deactivate the perpetual for the trader. Active means the trader's account is not empty in
     *          the perpetual. Empty means cash and position are zero. Can only called by a liquidity pool.
     *
     * @param   trader          The address of the trader.
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @return  True if the deactivation is successful.
     */
    function deactivatePerpetualFor(address trader, uint256 perpetualIndex) external returns (bool);

    /**
     * @notice  Liquidity pool must call this method when changing its ownership to the new operator.
     *          Can only be called by a liquidity pool. This method does not affect 'ownership' or privileges
     *          of operator but only make a record for further query.
     *
     * @param   liquidityPool   The address of the liquidity pool.
     * @param   operator        The address of the new operator, must be different from the old operator.
     */
    function registerOperatorOfLiquidityPool(address liquidityPool, address operator) external;
}

File 32 of 49 : IVersionControl.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "./IProxyAdmin.sol";

interface IVersionControl {
    function owner() external view returns (address);

    function getLatestVersion() external view returns (bytes32 latestVersionKey);

    /**
     * @notice  Get the details of the version.
     *
     * @param   versionKey              The key of the version to get.
     * @return  liquidityPoolTemplate   The address of the liquidity pool template.
     * @return  governorTemplate        The address of the governor template.
     * @return  compatibility           The compatibility of the specified version.
     */
    function getVersion(bytes32 versionKey)
        external
        view
        returns (
            address liquidityPoolTemplate,
            address governorTemplate,
            uint256 compatibility
        );

    /**
     * @notice  Get the description of the implementation of liquidity pool.
     *          Description contains creator, create time, compatibility and note
     *
     * @param  liquidityPool        The address of the liquidity pool.
     * @param  governor             The address of the governor.
     * @return appliedVersionKey    The version key of given liquidity pool and governor.
     */
    function getAppliedVersionKey(address liquidityPool, address governor)
        external
        view
        returns (bytes32 appliedVersionKey);

    /**
     * @notice  Check if a key is valid (exists).
     *
     * @param   versionKey  The key of the version to test.
     * @return  isValid     Return true if the version of given key is valid.
     */
    function isVersionKeyValid(bytes32 versionKey) external view returns (bool isValid);

    /**
     * @notice  Check if the implementation of liquidity pool target is compatible with the implementation base.
     *          Being compatible means having larger compatibility.
     *
     * @param   targetVersionKey    The key of the version to be upgraded to.
     * @param   baseVersionKey      The key of the version to be upgraded from.
     * @return  isCompatible        True if the target version is compatible with the base version.
     */
    function isVersionCompatible(bytes32 targetVersionKey, bytes32 baseVersionKey)
        external
        view
        returns (bool isCompatible);

    /**
     * @dev     Get a certain number of implementations of liquidity pool within range [begin, end).
     *
     * @param   begin       The index of first element to retrieve.
     * @param   end         The end index of element, exclusive.
     * @return  versionKeys An array contains current version keys.
     */
    function listAvailableVersions(uint256 begin, uint256 end)
        external
        view
        returns (bytes32[] memory versionKeys);
}

File 33 of 49 : IVariables.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "./IProxyAdmin.sol";

interface IVariables {
    function owner() external view returns (address);

    /**
     * @notice Get the address of the vault
     * @return address The address of the vault
     */
    function getVault() external view returns (address);

    /**
     * @notice Get the vault fee rate
     * @return int256 The vault fee rate
     */
    function getVaultFeeRate() external view returns (int256);

    /**
     * @notice Get the address of the access controller. It's always its own address.
     *
     * @return address The address of the access controller.
     */
    function getAccessController() external view returns (address);

    /**
     * @notice  Get the address of the symbol service.
     *
     * @return  Address The address of the symbol service.
     */
    function getSymbolService() external view returns (address);

    /**
     * @notice  Set the vault address. Can only called by owner.
     *
     * @param   newVault    The new value of the vault fee rate
     */
    function setVault(address newVault) external;

    /**
     * @notice  Get the address of the mcb token.
     * @dev     [ConfirmBeforeDeployment]
     *
     * @return  Address The address of the mcb token.
     */
    function getMCBToken() external pure returns (address);

    /**
     * @notice  Set the vault fee rate. Can only called by owner.
     *
     * @param   newVaultFeeRate The new value of the vault fee rate
     */
    function setVaultFeeRate(int256 newVaultFeeRate) external;
}

File 34 of 49 : IKeeperWhitelist.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface IKeeperWhitelist {
    /**
     * @notice Add an address to keeper whitelist.
     */
    function addKeeper(address keeper) external;

    /**
     * @notice Remove an address from keeper whitelist.
     */
    function removeKeeper(address keeper) external;

    /**
     * @notice Check if an address is in keeper whitelist.
     */
    function isKeeper(address keeper) external view returns (bool);
}

File 35 of 49 : IProxyAdmin.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface IProxyAdmin {
    function getProxyImplementation(address proxy) external view returns (address);

    /**
     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgrade(address proxy, address implementation) external;

    /**
     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
     * {TransparentUpgradeableProxy-upgradeToAndCall}.
     *
     * Requirements:
     *
     * - This contract must be the admin of `proxy`.
     */
    function upgradeAndCall(
        address proxy,
        address implementation,
        bytes memory data
    ) external payable;
}

File 36 of 49 : 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 37 of 49 : 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 38 of 49 : IDecimals.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface IDecimals {
    function decimals() external view returns (uint8);
}

File 39 of 49 : ILiquidityPoolGetter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "../Type.sol";

interface ILiquidityPoolGetter {
    /**
     * @notice Get the info of the liquidity pool
     * @return isRunning True if the liquidity pool is running
     * @return isFastCreationEnabled True if the operator of the liquidity pool is allowed to create new perpetual
     *                               when the liquidity pool is running
     * @return addresses The related addresses of the liquidity pool
     * @return intNums Int type properties, see below for details.
     * @return uintNums Uint type properties, see below for details.
     */
    function getLiquidityPoolInfo()
        external
        view
        returns (
            bool isRunning,
            bool isFastCreationEnabled,
            // [0] creator,
            // [1] operator,
            // [2] transferringOperator,
            // [3] governor,
            // [4] shareToken,
            // [5] collateralToken,
            // [6] vault,
            address[7] memory addresses,
            // [0] vaultFeeRate,
            // [1] poolCash,
            // [2] insuranceFundCap,
            // [3] insuranceFund,
            // [4] donatedInsuranceFund,
            int256[5] memory intNums,
            // [0] collateralDecimals,
            // [1] perpetualCount,
            // [2] fundingTime,
            // [3] operatorExpiration,
            // [4] liquidityCap,
            // [5] shareTransferDelay,
            uint256[6] memory uintNums
        );

    /**
     * @notice Get the info of the perpetual. Need to update the funding state and the oracle price
     *         of each perpetual before and update the funding rate of each perpetual after
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @return state The state of the perpetual
     * @return oracle The oracle's address of the perpetual
     * @return nums The related numbers of the perpetual
     */
    function getPerpetualInfo(uint256 perpetualIndex)
        external
        view
        returns (
            PerpetualState state,
            address oracle,
            // [0] totalCollateral
            // [1] markPrice, (return settlementPrice if it is in EMERGENCY state)
            // [2] indexPrice,
            // [3] fundingRate,
            // [4] unitAccumulativeFunding,
            // [5] initialMarginRate,
            // [6] maintenanceMarginRate,
            // [7] operatorFeeRate,
            // [8] lpFeeRate,
            // [9] referralRebateRate,
            // [10] liquidationPenaltyRate,
            // [11] keeperGasReward,
            // [12] insuranceFundRate,
            // [13-15] halfSpread value, min, max,
            // [16-18] openSlippageFactor value, min, max,
            // [19-21] closeSlippageFactor value, min, max,
            // [22-24] fundingRateLimit value, min, max,
            // [25-27] ammMaxLeverage value, min, max,
            // [28-30] maxClosePriceDiscount value, min, max,
            // [31] openInterest,
            // [32] maxOpenInterestRate,
            // [33-35] fundingRateFactor value, min, max,
            // [36-38] defaultTargetLeverage value, min, max,
            // [39-41] baseFundingRate value, min, max,
            int256[42] memory nums
        );

    /**
     * @notice Get the account info of the trader. Need to update the funding state and the oracle price
     *         of each perpetual before and update the funding rate of each perpetual after
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @param trader The address of the trader
     * @return cash The cash(collateral) of the account
     * @return position The position of the account
     * @return availableMargin The available margin of the account
     * @return margin The margin of the account
     * @return settleableMargin The settleable margin of the account
     * @return isInitialMarginSafe True if the account is initial margin safe
     * @return isMaintenanceMarginSafe True if the account is maintenance margin safe
     * @return isMarginSafe True if the total value of margin account is beyond 0
     * @return targetLeverage   The target leverage for openning position.
     */
    function getMarginAccount(uint256 perpetualIndex, address trader)
        external
        view
        returns (
            int256 cash,
            int256 position,
            int256 availableMargin,
            int256 margin,
            int256 settleableMargin,
            bool isInitialMarginSafe,
            bool isMaintenanceMarginSafe,
            bool isMarginSafe, // bankrupt
            int256 targetLeverage
        );

    /**
     * @notice Get the number of active accounts in the perpetual.
     *         Active means the trader's account is not empty in the perpetual.
     *         Empty means cash and position are zero
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @return activeAccountCount The number of active accounts in the perpetual
     */
    function getActiveAccountCount(uint256 perpetualIndex) external view returns (uint256);

    /**
     * @notice Get the active accounts in the perpetual whose index between begin and end.
     *         Active means the trader's account is not empty in the perpetual.
     *         Empty means cash and position are zero
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @param begin The begin index
     * @param end The end index
     * @return result The active accounts in the perpetual whose index between begin and end
     */
    function listActiveAccounts(
        uint256 perpetualIndex,
        uint256 begin,
        uint256 end
    ) external view returns (address[] memory result);

    /**
     * @notice Get the progress of clearing active accounts.
     *         Return the number of total active accounts and the number of active accounts not cleared
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @return left The left active accounts
     * @return total The total active accounts
     */
    function getClearProgress(uint256 perpetualIndex)
        external
        view
        returns (uint256 left, uint256 total);

    /**
     * @notice Get the pool margin of the liquidity pool.
     *         Pool margin is how much collateral of the pool considering the AMM's positions of perpetuals
     * @return poolMargin The pool margin of the liquidity pool
     */
    function getPoolMargin() external view returns (int256 poolMargin, bool isSafe);

    /**
     * @notice  Query the price, fees and cost when trade agaist amm.
     *          The trading price is determined by the AMM based on the index price of the perpetual.
     *          This method should returns the same result as a 'read-only' trade.
     *          WARN: the result of this function is base on current storage of liquidityPool, not the latest.
     *          To get the latest status, call `syncState` first.
     *
     *          Flags is a 32 bit uint value which indicates: (from highest bit)
     *            - close only      only close position during trading;
     *            - market order    do not check limit price during trading;
     *            - stop loss       only available in brokerTrade mode;
     *            - take profit     only available in brokerTrade mode;
     *          For stop loss and take profit, see `validateTriggerPrice` in OrderModule.sol for details.
     *
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   trader          The address of trader.
     * @param   amount          The amount of position to trader, positive for buying and negative for selling. The amount always use decimals 18.
     * @param   referrer        The address of referrer who will get rebate from the deal.
     * @param   flags           The flags of the trade.
     * @return  tradePrice      The average fill price.
     * @return  totalFee        The total fee collected from the trader after the trade.
     * @return  cost            Deposit or withdraw to let effective leverage == targetLeverage if flags contain USE_TARGET_LEVERAGE. > 0 if deposit, < 0 if withdraw.
     */
    function queryTrade(
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        address referrer,
        uint32 flags
    )
        external
        returns (
            int256 tradePrice,
            int256 totalFee,
            int256 cost
        );

    /**
     * @notice  Query cash to add / share to mint when adding liquidity to the liquidity pool.
     *          Only one of cashToAdd or shareToMint may be non-zero.
     *
     * @param   cashToAdd         The amount of cash to add, always use decimals 18.
     * @param   shareToMint       The amount of share token to mint, always use decimals 18.
     * @return  cashToAddResult   The amount of cash to add, always use decimals 18. Equal to cashToAdd if cashToAdd is non-zero.
     * @return  shareToMintResult The amount of cash to add, always use decimals 18. Equal to shareToMint if shareToMint is non-zero.
     */
    function queryAddLiquidity(int256 cashToAdd, int256 shareToMint)
        external
        view
        returns (int256 cashToAddResult, int256 shareToMintResult);

    /**
     * @notice  Query cash to return / share to redeem when removing liquidity from the liquidity pool.
     *          Only one of shareToRemove or cashToReturn may be non-zero.
     *          Can only called when the pool is running.
     *
     * @param   shareToRemove       The amount of share token to redeem, always use decimals 18.
     * @param   cashToReturn        The amount of cash to return, always use decimals 18.
     * @return  shareToRemoveResult The amount of share token to redeem, always use decimals 18. Equal to shareToRemove if shareToRemove is non-zero.
     * @return  cashToReturnResult  The amount of cash to return, always use decimals 18. Equal to cashToReturn if cashToReturn is non-zero.
     */
    function queryRemoveLiquidity(int256 shareToRemove, int256 cashToReturn)
        external
        view
        returns (int256 shareToRemoveResult, int256 cashToReturnResult);
}

File 40 of 49 : TradeModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

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

import "../interface/IOracle.sol";

import "../libraries/OrderData.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Utils.sol";

import "./AMMModule.sol";
import "./LiquidityPoolModule.sol";
import "./MarginAccountModule.sol";
import "./PerpetualModule.sol";

import "../Type.sol";

library TradeModule {
    using SafeMathExt for int256;
    using SignedSafeMathUpgradeable for int256;
    using OrderData for uint32;

    using AMMModule for LiquidityPoolStorage;
    using LiquidityPoolModule for LiquidityPoolStorage;
    using MarginAccountModule for PerpetualStorage;
    using PerpetualModule for PerpetualStorage;
    using MarginAccountModule for MarginAccount;

    event Trade(
        uint256 perpetualIndex,
        address indexed trader,
        int256 position,
        int256 price,
        int256 fee,
        int256 lpFee
    );
    event Liquidate(
        uint256 perpetualIndex,
        address indexed liquidator,
        address indexed trader,
        int256 amount,
        int256 price,
        int256 penalty,
        int256 penaltyToLP
    );
    event TransferFeeToVault(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed vault,
        int256 vaultFee
    );
    event TransferFeeToOperator(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed operator,
        int256 operatorFee
    );
    event TransferFeeToReferrer(
        uint256 perpetualIndex,
        address indexed trader,
        address indexed referrer,
        int256 referralRebate
    );

    /**
     * @dev     See `trade` in Perpetual.sol for details.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   trader          The address of trader.
     * @param   amount          The amount of position to trader, positive for buying and negative for selling.
     * @param   limitPrice      The worst price the trader accepts.
     * @param   referrer        The address of referrer who will get rebate in the deal.
     * @param   flags           The flags of the trade, contains extra config for trading.
     * @return  tradeAmount     The amount of positions actually traded in the transaction.
     */
    function trade(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        int256 limitPrice,
        address referrer,
        uint32 flags
    ) public returns (int256 tradeAmount) {
        (int256 deltaCash, int256 deltaPosition) = preTrade(
            liquidityPool,
            perpetualIndex,
            trader,
            amount,
            limitPrice,
            flags
        );
        doTrade(liquidityPool, perpetualIndex, trader, deltaCash, deltaPosition);
        (int256 lpFee, int256 totalFee) = postTrade(
            liquidityPool,
            perpetualIndex,
            trader,
            referrer,
            deltaCash,
            deltaPosition,
            flags
        );
        emit Trade(
            perpetualIndex,
            trader,
            deltaPosition.neg(),
            deltaCash.wdiv(deltaPosition).abs(),
            totalFee,
            lpFee
        );
        tradeAmount = deltaPosition.neg();
        require(
            liquidityPool.isTraderMarginSafe(perpetualIndex, trader, tradeAmount),
            "trader margin unsafe"
        );
    }

    function preTrade(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        int256 limitPrice,
        uint32 flags
    ) internal returns (int256 deltaCash, int256 deltaPosition) {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        require(!IOracle(perpetual.oracle).isMarketClosed(), "market is closed now");
        // handle close only flag
        if (flags.isCloseOnly()) {
            amount = getMaxPositionToClose(perpetual.getPosition(trader), amount);
            require(amount != 0, "no amount to close");
        }
        // query price from AMM
        (deltaCash, deltaPosition) = liquidityPool.queryTradeWithAMM(
            perpetualIndex,
            amount.neg(),
            false
        );
        // check price
        if (!flags.isMarketOrder()) {
            int256 tradePrice = deltaCash.wdiv(deltaPosition).abs();
            validatePrice(amount >= 0, tradePrice, limitPrice);
        }
    }

    function doTrade(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 deltaCash,
        int256 deltaPosition
    ) internal {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        int256 deltaOpenInterest1 = perpetual.updateMargin(address(this), deltaPosition, deltaCash);
        int256 deltaOpenInterest2 = perpetual.updateMargin(
            trader,
            deltaPosition.neg(),
            deltaCash.neg()
        );
        require(perpetual.openInterest >= 0, "negative open interest");
        if (deltaOpenInterest1.add(deltaOpenInterest2) > 0) {
            // open interest will increase, check limit
            (int256 poolMargin, ) = liquidityPool.getPoolMargin();
            require(
                perpetual.openInterest <=
                    perpetual.maxOpenInterestRate.wfrac(poolMargin, perpetual.getIndexPrice()),
                "open interest exceeds limit"
            );
        }
    }

    /**
     * @dev Execute the trade. If the trader has opened position in the trade, his account should be
     *          initial margin safe after the trade. If not, his account should be margin safe
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of pereptual storage.
     * @param   trader          The address of trader.
     * @param   referrer        The address of referrer who will get rebate from the deal.
     * @param   deltaCash       The amount of cash changes in a trade.
     * @param   deltaPosition   The amount of position changes in a trade.
     * @return  lpFee           The amount of fee for lp provider.
     * @return  totalFee        The total fee collected from the trader after the trade.
     */
    function postTrade(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        address referrer,
        int256 deltaCash,
        int256 deltaPosition,
        uint32 flags
    ) internal returns (int256 lpFee, int256 totalFee) {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        // fees
        int256 operatorFee;
        int256 vaultFee;
        int256 referralRebate;
        {
            bool hasOpened = Utils.hasOpenedPosition(
                perpetual.getPosition(trader),
                deltaPosition.neg()
            );
            (lpFee, operatorFee, vaultFee, referralRebate) = getFees(
                liquidityPool,
                perpetual,
                trader,
                referrer,
                deltaCash.abs(),
                hasOpened
            );
        }
        totalFee = lpFee.add(operatorFee).add(vaultFee).add(referralRebate);
        perpetual.updateCash(trader, totalFee.neg());
        // trader deposit/withdraw
        if (flags.useTargetLeverage()) {
            liquidityPool.adjustMarginLeverage(
                perpetualIndex,
                trader,
                deltaPosition.neg(),
                deltaCash.neg(),
                totalFee,
                flags
            );
        }
        // send fee
        transferFee(
            liquidityPool,
            perpetualIndex,
            trader,
            referrer,
            lpFee,
            operatorFee,
            vaultFee,
            referralRebate
        );
    }

    /**
     * @dev     Get the fees of the trade. If the margin of the trader is not enough for fee:
     *            1. If trader open position, the trade will be reverted.
     *            2. If trader close position, the fee will be decreasing in proportion according to
     *               the margin left in the trader's account
     *          The rebate of referral will only calculate the lpFee and operatorFee.
     *          The vault fee will not be counted in.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetual       The reference of pereptual storage.
     * @param   trader          The address of trader.
     * @param   referrer        The address of referrer who will get rebate from the deal.
     * @param   tradeValue      The amount of trading value, measured by collateral, abs of deltaCash.
     * @return  lpFee           The amount of fee to the Liquidity provider.
     * @return  operatorFee     The amount of fee to the operator.
     * @return  vaultFee        The amount of fee to the vault.
     * @return  referralRebate  The amount of rebate of the refferral.
     */
    function getFees(
        LiquidityPoolStorage storage liquidityPool,
        PerpetualStorage storage perpetual,
        address trader,
        address referrer,
        int256 tradeValue,
        bool hasOpened
    )
        public
        view
        returns (
            int256 lpFee,
            int256 operatorFee,
            int256 vaultFee,
            int256 referralRebate
        )
    {
        require(tradeValue >= 0, "trade value is negative");
        vaultFee = tradeValue.wmul(liquidityPool.getVaultFeeRate());
        lpFee = tradeValue.wmul(perpetual.lpFeeRate);
        if (liquidityPool.getOperator() != address(0)) {
            operatorFee = tradeValue.wmul(perpetual.operatorFeeRate);
        }
        int256 totalFee = lpFee.add(operatorFee).add(vaultFee);
        int256 availableMargin = perpetual.getAvailableMargin(trader, perpetual.getMarkPrice());
        if (!hasOpened) {
            if (availableMargin <= 0) {
                lpFee = 0;
                operatorFee = 0;
                vaultFee = 0;
                referralRebate = 0;
            } else if (totalFee > availableMargin) {
                // make sure the sum of fees < available margin
                int256 rate = availableMargin.wdiv(totalFee, Round.FLOOR);
                operatorFee = operatorFee.wmul(rate, Round.FLOOR);
                vaultFee = vaultFee.wmul(rate, Round.FLOOR);
                lpFee = availableMargin.sub(operatorFee).sub(vaultFee);
            }
        }
        if (
            referrer != address(0) && perpetual.referralRebateRate > 0 && lpFee.add(operatorFee) > 0
        ) {
            int256 lpFeeRebate = lpFee.wmul(perpetual.referralRebateRate);
            int256 operatorFeeRabate = operatorFee.wmul(perpetual.referralRebateRate);
            referralRebate = lpFeeRebate.add(operatorFeeRabate);
            lpFee = lpFee.sub(lpFeeRebate);
            operatorFee = operatorFee.sub(operatorFeeRabate);
        }
    }

    function transferFee(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        address referrer,
        int256 lpFee,
        int256 operatorFee,
        int256 vaultFee,
        int256 referralRebate
    ) internal {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        perpetual.updateCash(address(this), lpFee);

        address vault = liquidityPool.getVault();
        liquidityPool.transferFromPerpetualToUser(perpetual.id, vault, vaultFee);
        emit TransferFeeToVault(perpetual.id, trader, vault, vaultFee);

        address operator = liquidityPool.getOperator();
        liquidityPool.transferFromPerpetualToUser(perpetual.id, operator, operatorFee);
        emit TransferFeeToOperator(perpetual.id, trader, operator, operatorFee);

        liquidityPool.transferFromPerpetualToUser(perpetual.id, referrer, referralRebate);
        emit TransferFeeToReferrer(perpetual.id, trader, referrer, referralRebate);
    }

    /**
     * @dev     See `liquidateByAMM` in Perpetual.sol for details.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   liquidator      The address of the account calling the liquidation method.
     * @param   trader          The address of the liquidated account.
     * @return  liquidatedAmount    The amount of positions actually liquidated in the transaction.
     */
    function liquidateByAMM(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address liquidator,
        address trader
    ) public returns (int256 liquidatedAmount) {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        require(
            !perpetual.isMaintenanceMarginSafe(trader, perpetual.getMarkPrice()),
            "trader is safe"
        );
        int256 position = perpetual.getPosition(trader);
        // 0. price / amount
        (int256 deltaCash, int256 deltaPosition) = liquidityPool.queryTradeWithAMM(
            perpetualIndex,
            position,
            true
        );
        require(deltaPosition != 0, "insufficient liquidity");
        // 2. trade
        int256 liquidatePrice = deltaCash.wdiv(deltaPosition).abs();
        perpetual.updateMargin(address(this), deltaPosition, deltaCash);
        perpetual.updateMargin(
            trader,
            deltaPosition.neg(),
            deltaCash.add(perpetual.keeperGasReward).neg()
        );
        require(perpetual.openInterest >= 0, "negative open interest");
        liquidityPool.transferFromPerpetualToUser(
            perpetual.id,
            liquidator,
            perpetual.keeperGasReward
        );
        // 3. penalty  min(markPrice * liquidationPenaltyRate, margin / position) * deltaPosition
        (int256 penalty, int256 penaltyToLiquidator) = postLiquidate(
            liquidityPool,
            perpetual,
            address(this),
            trader,
            position,
            deltaPosition.neg()
        );
        emit Liquidate(
            perpetualIndex,
            address(this),
            trader,
            deltaPosition.neg(),
            liquidatePrice,
            penalty,
            penaltyToLiquidator
        );
        liquidatedAmount = deltaPosition.neg();
    }

    /**
     * @dev     See `liquidateByTrader` in Perpetual.sol for details.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   liquidator          The address of the account calling the liquidation method.
     * @param   trader              The address of the liquidated account.
     * @param   amount              The amount of position to be taken from liquidated trader.
     * @param   limitPrice          The worst price liquidator accepts.
     * @return  liquidatedAmount    The amount of positions actually liquidated in the transaction.
     */
    function liquidateByTrader(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address liquidator,
        address trader,
        int256 amount,
        int256 limitPrice
    ) public returns (int256 liquidatedAmount) {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        int256 markPrice = perpetual.getMarkPrice();
        require(!perpetual.isMaintenanceMarginSafe(trader, markPrice), "trader is safe");
        // 0. price / amount
        validatePrice(amount >= 0, markPrice, limitPrice);
        int256 position = perpetual.getPosition(trader);
        int256 deltaPosition = getMaxPositionToClose(position, amount.neg()).neg();
        int256 deltaCash = markPrice.wmul(deltaPosition).neg();
        // 1. execute
        perpetual.updateMargin(liquidator, deltaPosition, deltaCash);
        perpetual.updateMargin(trader, deltaPosition.neg(), deltaCash.neg());
        require(perpetual.openInterest >= 0, "negative open interest");
        // 2. penalty  min(markPrice * liquidationPenaltyRate, margin / position) * deltaPosition
        (int256 penalty, ) = postLiquidate(
            liquidityPool,
            perpetual,
            liquidator,
            trader,
            position,
            deltaPosition.neg()
        );
        liquidatedAmount = deltaPosition.neg();
        require(
            liquidityPool.isTraderMarginSafe(perpetualIndex, liquidator, liquidatedAmount),
            "liquidator margin unsafe"
        );
        emit Liquidate(perpetualIndex, liquidator, trader, liquidatedAmount, markPrice, penalty, 0);
    }

    /**
     * @dev     Handle liquidate penalty / fee.
     *
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetual       The reference of perpetual storage.
     * @param   liquidator      The address of the account calling the liquidation method.
     * @param   trader          The address of the liquidated account.
     * @param   position        The amount of position owned by trader before liquidation.
     * @param   deltaPosition   The amount of position to be taken from liquidated trader.
     * @return  penalty             The amount of positions actually liquidated in the transaction.
     * @return  penaltyToLiquidator The amount of positions actually liquidated in the transaction.
     */
    function postLiquidate(
        LiquidityPoolStorage storage liquidityPool,
        PerpetualStorage storage perpetual,
        address liquidator,
        address trader,
        int256 position,
        int256 deltaPosition
    ) public returns (int256 penalty, int256 penaltyToLiquidator) {
        int256 vaultFee = 0;
        {
            int256 markPrice = perpetual.getMarkPrice();
            int256 remainingMargin = perpetual.getMargin(trader, markPrice);
            int256 liquidationValue = markPrice.wmul(deltaPosition).abs();
            penalty = liquidationValue.wmul(perpetual.liquidationPenaltyRate).min(
                remainingMargin.wfrac(deltaPosition.abs(), position.abs())
            );
            remainingMargin = remainingMargin.sub(penalty);
            if (remainingMargin > 0) {
                vaultFee = liquidationValue.wmul(liquidityPool.getVaultFeeRate()).min(
                    remainingMargin
                );
                liquidityPool.transferFromPerpetualToUser(
                    perpetual.id,
                    liquidityPool.getVault(),
                    vaultFee
                );
            }
        }
        int256 penaltyToFund;
        bool isEmergency;
        if (penalty > 0) {
            penaltyToFund = penalty.wmul(perpetual.insuranceFundRate);
            penaltyToLiquidator = penalty.sub(penaltyToFund);
        } else {
            int256 totalInsuranceFund = liquidityPool.insuranceFund.add(
                liquidityPool.donatedInsuranceFund
            );
            if (totalInsuranceFund.add(penalty) < 0) {
                // ensure donatedInsuranceFund >= 0
                penalty = totalInsuranceFund.neg();
                isEmergency = true;
            }
            penaltyToFund = penalty;
            penaltyToLiquidator = 0;
        }
        int256 penaltyToLP = liquidityPool.updateInsuranceFund(penaltyToFund);
        perpetual.updateCash(address(this), penaltyToLP);
        perpetual.updateCash(liquidator, penaltyToLiquidator);
        perpetual.updateCash(trader, penalty.add(vaultFee).neg());
        if (penaltyToFund >= 0) {
            perpetual.decreaseTotalCollateral(penaltyToFund.sub(penaltyToLP));
        } else {
            // penaltyToLP = 0 when penaltyToFund < 0
            perpetual.increaseTotalCollateral(penaltyToFund.neg());
        }
        if (isEmergency) {
            liquidityPool.setEmergencyState(perpetual.id);
        }
    }

    /**
     * @dev     Get the max position amount of trader will be closed in the trade.
     * @param   position            Current position of trader.
     * @param   amount              The trading amount of position.
     * @return  maxPositionToClose  The max position amount of trader will be closed in the trade.
     */
    function getMaxPositionToClose(int256 position, int256 amount)
        internal
        pure
        returns (int256 maxPositionToClose)
    {
        require(position != 0, "trader has no position to close");
        require(!Utils.hasTheSameSign(position, amount), "trader must be close only");
        maxPositionToClose = amount.abs() > position.abs() ? position.neg() : amount;
    }

    /**
     * @dev     Check if the price is better than the limit price.
     * @param   isLong      True if the side is long.
     * @param   price       The price to be validate.
     * @param   priceLimit  The limit price.
     */
    function validatePrice(
        bool isLong,
        int256 price,
        int256 priceLimit
    ) internal pure {
        require(price > 0, "price must be positive");
        bool isPriceSatisfied = isLong ? price <= priceLimit : price >= priceLimit;
        require(isPriceSatisfied, "price exceeds limit");
    }

    /**
     * @dev     A readonly version of trade
     *
     *          This function was written post-audit. So there's a lot of repeated logic here.
     *          NOTE: max openInterest is NOT exact the same as trade(). In this function, poolMargin
     *                will be smaller, so that the openInterst limit is also smaller (more strict).
     * @param   liquidityPool   The reference of liquidity pool storage.
     * @param   perpetualIndex  The index of the perpetual in liquidity pool.
     * @param   trader          The address of trader.
     * @param   amount          The amount of position to trader, positive for buying and negative for selling.
     * @param   flags           The flags of the trade, contains extra config for trading.
     * @return  tradePrice      The average fill price.
     * @return  totalFee        The total fee collected from the trader after the trade.
     * @return  cost            Deposit or withdraw to let effective leverage == targetLeverage if flags contain USE_TARGET_LEVERAGE. > 0 if deposit, < 0 if withdraw.
     */
    function queryTrade(
        LiquidityPoolStorage storage liquidityPool,
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        address referrer,
        uint32 flags
    )
        public
        returns (
            int256 tradePrice,
            int256 totalFee,
            int256 cost
        )
    {
        PerpetualStorage storage perpetual = liquidityPool.perpetuals[perpetualIndex];
        MarginAccount memory account = perpetual.marginAccounts[trader]; // clone
        (int256 deltaCash, int256 deltaPosition) = preTrade(
            liquidityPool,
            perpetualIndex,
            trader,
            amount,
            amount > 0 ? type(int256).max : 0,
            flags
        );
        tradePrice = deltaCash.wdiv(deltaPosition).abs();
        readonlyDoTrade(liquidityPool, perpetual, account, deltaCash, deltaPosition);
        (totalFee, cost) = readonlyPostTrade(
            liquidityPool,
            perpetual,
            account,
            referrer,
            deltaCash,
            deltaPosition,
            flags
        );
    }

    // A readonly version of doTrade. This function was written post-audit. So there's a lot of repeated logic here.
    // NOTE: max openInterest is NOT exact the same as trade(). In this function, poolMargin
    //       will be smaller, so that the openInterst limit is also smaller (more strict).
    function readonlyDoTrade(
        LiquidityPoolStorage storage liquidityPool,
        PerpetualStorage storage perpetual,
        MarginAccount memory account,
        int256 deltaCash,
        int256 deltaPosition
    ) internal view {
        int256 deltaOpenInterest1;
        int256 deltaOpenInterest2;
        (, , deltaOpenInterest1) = readonlyUpdateMargin(
            perpetual,
            perpetual.marginAccounts[address(this)].cash,
            perpetual.marginAccounts[address(this)].position,
            deltaPosition,
            deltaCash
        );
        (account.cash, account.position, deltaOpenInterest2) = readonlyUpdateMargin(
            perpetual,
            account.cash,
            account.position,
            deltaPosition.neg(),
            deltaCash.neg()
        );
        int256 perpetualOpenInterest = perpetual.openInterest.add(deltaOpenInterest1).add(
            deltaOpenInterest2
        );
        require(perpetualOpenInterest >= 0, "negative open interest");
        if (deltaOpenInterest1.add(deltaOpenInterest2) > 0) {
            // open interest will increase, check limit
            (int256 poolMargin, ) = liquidityPool.getPoolMargin(); // NOTE: this is a slight different from trade()
            require(
                perpetualOpenInterest <=
                    perpetual.maxOpenInterestRate.wfrac(poolMargin, perpetual.getIndexPrice()),
                "open interest exceeds limit"
            );
        }
    }

    // A readonly version of postTrade. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyPostTrade(
        LiquidityPoolStorage storage liquidityPool,
        PerpetualStorage storage perpetual,
        MarginAccount memory account,
        address referrer,
        int256 deltaCash,
        int256 deltaPosition,
        uint32 flags
    ) internal view returns (int256 totalFee, int256 adjustCollateral) {
        // fees
        int256 lpFee;
        int256 operatorFee;
        int256 vaultFee;
        int256 referralRebate;
        {
            bool hasOpened = Utils.hasOpenedPosition(account.position, deltaPosition.neg());
            (lpFee, operatorFee, vaultFee, referralRebate) = readonlyGetFees(
                liquidityPool,
                perpetual,
                account,
                referrer,
                deltaCash.abs(),
                hasOpened
            );
        }
        totalFee = lpFee.add(operatorFee).add(vaultFee).add(referralRebate);
        // was updateCash
        account.cash = account.cash.add(totalFee.neg());
        // trader deposit/withdraw
        if (flags.useTargetLeverage()) {
            adjustCollateral = LiquidityPoolModule.readonlyAdjustMarginLeverage(
                perpetual,
                account,
                deltaPosition.neg(),
                deltaCash.neg(),
                totalFee,
                flags
            );
        }
        account.cash = account.cash.add(adjustCollateral);
    }

    // A readonly version of MarginAccountModule.updateMargin. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyUpdateMargin(
        PerpetualStorage storage perpetual,
        int256 oldCash,
        int256 oldPosition,
        int256 deltaPosition,
        int256 deltaCash
    )
        internal
        view
        returns (
            int256 newCash,
            int256 newPosition,
            int256 deltaOpenInterest
        )
    {
        newPosition = oldPosition.add(deltaPosition);
        newCash = oldCash.add(deltaCash).add(perpetual.unitAccumulativeFunding.wmul(deltaPosition));
        if (oldPosition > 0) {
            deltaOpenInterest = oldPosition.neg();
        }
        if (newPosition > 0) {
            deltaOpenInterest = deltaOpenInterest.add(newPosition);
        }
    }

    // A readonly version of getFees. This function was written post-audit. So there's a lot of repeated logic here.
    function readonlyGetFees(
        LiquidityPoolStorage storage liquidityPool,
        PerpetualStorage storage perpetual,
        MarginAccount memory trader,
        address referrer,
        int256 tradeValue,
        bool hasOpened
    )
        public
        view
        returns (
            int256 lpFee,
            int256 operatorFee,
            int256 vaultFee,
            int256 referralRebate
        )
    {
        require(tradeValue >= 0, "trade value is negative");
        vaultFee = tradeValue.wmul(liquidityPool.getVaultFeeRate());
        lpFee = tradeValue.wmul(perpetual.lpFeeRate);
        if (liquidityPool.getOperator() != address(0)) {
            operatorFee = tradeValue.wmul(perpetual.operatorFeeRate);
        }
        int256 totalFee = lpFee.add(operatorFee).add(vaultFee);
        int256 availableMargin = LiquidityPoolModule.readonlyGetAvailableMargin(
            perpetual,
            trader,
            perpetual.getMarkPrice()
        );
        if (!hasOpened) {
            if (availableMargin <= 0) {
                lpFee = 0;
                operatorFee = 0;
                vaultFee = 0;
                referralRebate = 0;
            } else if (totalFee > availableMargin) {
                // make sure the sum of fees < available margin
                int256 rate = availableMargin.wdiv(totalFee, Round.FLOOR);
                operatorFee = operatorFee.wmul(rate, Round.FLOOR);
                vaultFee = vaultFee.wmul(rate, Round.FLOOR);
                lpFee = availableMargin.sub(operatorFee).sub(vaultFee);
            }
        }
        if (
            referrer != address(0) && perpetual.referralRebateRate > 0 && lpFee.add(operatorFee) > 0
        ) {
            int256 lpFeeRebate = lpFee.wmul(perpetual.referralRebateRate);
            int256 operatorFeeRabate = operatorFee.wmul(perpetual.referralRebateRate);
            referralRebate = lpFeeRebate.add(operatorFeeRabate);
            lpFee = lpFee.sub(lpFeeRebate);
            operatorFee = operatorFee.sub(operatorFeeRabate);
        }
    }
}

File 41 of 49 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../utils/ContextUpgradeable.sol";

File 42 of 49 : 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 43 of 49 : Initializable.sol
// SPDX-License-Identifier: MIT

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

import "../utils/AddressUpgradeable.sol";

/**
 * @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) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 44 of 49 : ILiquidityPoolGovernance.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

interface ILiquidityPoolGovernance {
    function setEmergencyState(uint256 perpetualIndex) external;
}

File 45 of 49 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

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

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
    uint256[49] private __gap;
}

File 46 of 49 : IPerpetual.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.4;

import "../Type.sol";

interface IPerpetual {
    /**
     * @notice  Deposit collateral to the perpetual.
     *          Can only called when the perpetual's state is "NORMAL".
     *          This method will always increase `cash` amount in trader's margin account.
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     * @param   amount          The amount of collateral to deposit. The amount always use decimals 18.
     */
    function deposit(
        uint256 perpetualIndex,
        address trader,
        int256 amount
    ) external;

    /**
     * @notice  Withdraw collateral from the trader's account of the perpetual.
     *          After withdrawn, trader shall at least has maintenance margin left in account.
     *          Can only called when the perpetual's state is "NORMAL".
     *          Margin account must at least keep
     *          The trader's cash will decrease in the perpetual.
     *          Need to update the funding state and the oracle price of each perpetual before
     *          and update the funding rate of each perpetual after
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     * @param   trader          The address of the trader.
     * @param   amount          The amount of collateral to withdraw. The amount always use decimals 18.
     */
    function withdraw(
        uint256 perpetualIndex,
        address trader,
        int256 amount
    ) external;

    /**
     * @notice  If the state of the perpetual is "CLEARED", anyone authorized withdraw privilege by trader can settle
     *          trader's account in the perpetual. Which means to calculate how much the collateral should be returned
     *          to the trader, return it to trader's wallet and clear the trader's cash and position in the perpetual.
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool
     * @param   trader          The address of the trader.
     */
    function settle(uint256 perpetualIndex, address trader) external;

    /**
     * @notice  Clear the next active account of the perpetual which state is "EMERGENCY" and send gas reward of collateral
     *          to sender. If all active accounts are cleared, the clear progress is done and the perpetual's state will
     *          change to "CLEARED". Active means the trader's account is not empty in the perpetual.
     *          Empty means cash and position are zero
     *
     * @param   perpetualIndex  The index of the perpetual in the liquidity pool.
     */
    function clear(uint256 perpetualIndex) external;

    /**
     * @notice Trade with AMM in the perpetual, require sender is granted the trade privilege by the trader.
     *         The trading price is determined by the AMM based on the index price of the perpetual.
     *         Trader must be initial margin safe if opening position and margin safe if closing position
     * @param perpetualIndex The index of the perpetual in the liquidity pool
     * @param trader The address of trader
     * @param amount The position amount of the trade
     * @param limitPrice The worst price the trader accepts
     * @param deadline The deadline of the trade
     * @param referrer The referrer's address of the trade
     * @param flags The flags of the trade
     * @return int256 The update position amount of the trader after the trade
     */
    function trade(
        uint256 perpetualIndex,
        address trader,
        int256 amount,
        int256 limitPrice,
        uint256 deadline,
        address referrer,
        uint32 flags
    ) external returns (int256);

    /**
     * @notice Trade with AMM by the order, initiated by the broker.
     *         The trading price is determined by the AMM based on the index price of the perpetual.
     *         Trader must be initial margin safe if opening position and margin safe if closing position
     * @param orderData The order data object
     * @param amount The position amount of the trade
     * @return int256 The update position amount of the trader after the trade
     */
    function brokerTrade(bytes memory orderData, int256 amount) external returns (int256);

    /**
     * @notice  Liquidate the trader if the trader's margin balance is lower than maintenance margin (unsafe).
     *          Liquidate can be considered as a forced trading between AMM and unsafe margin account;
     *          Based on current liquidity of AMM, it may take positions up to an amount equal to all the position
     *          of the unsafe account. Besides the position, trader need to pay an extra penalty to AMM
     *          for taking the unsafe assets. See TradeModule.sol for ehe strategy of penalty.
     *
     *          The liquidate price will be determined by AMM.
     *          Caller of this method can be anyone, then get a reward to make up for transaction gas fee.
     *
     *          If a trader's margin balance is lower than 0 (bankrupt), insurance fund will be use to fill the loss
     *          to make the total profit and loss balanced. (first the `insuranceFund` then the `donatedInsuranceFund`)
     *
     *          If insurance funds are drained, the state of perpetual will turn to enter "EMERGENCY" than shutdown.
     *          Can only liquidate when the perpetual's state is "NORMAL".
     *
     * @param   perpetualIndex      The index of the perpetual in liquidity pool
     * @param   trader              The address of trader to be liquidated.
     * @return  liquidationAmount   The amount of positions actually liquidated in the transaction. The amount always use decimals 18.
     */
    function liquidateByAMM(uint256 perpetualIndex, address trader)
        external
        returns (int256 liquidationAmount);

    /**
     * @notice  This method is generally consistent with `liquidateByAMM` function, but there some difference:
     *           - The liquidation price is no longer determined by AMM, but the mark price;
     *           - The penalty is taken by trader who takes position but AMM;
     *
     * @param   perpetualIndex      The index of the perpetual in liquidity pool.
     * @param   liquidator          The address of liquidator to receive the liquidated position.
     * @param   trader              The address of trader to be liquidated.
     * @param   amount              The amount of position to be taken from liquidated trader. The amount always use decimals 18.
     * @param   limitPrice          The worst price liquidator accepts.
     * @param   deadline            The deadline of transaction.
     * @return  liquidationAmount   The amount of positions actually liquidated in the transaction.
     */
    function liquidateByTrader(
        uint256 perpetualIndex,
        address liquidator,
        address trader,
        int256 amount,
        int256 limitPrice,
        uint256 deadline
    ) external returns (int256 liquidationAmount);
}

File 47 of 49 : OrderModule.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

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

import "../interface/IAccessControl.sol";

import "../libraries/Utils.sol";
import "../libraries/OrderData.sol";
import "../libraries/SafeMathExt.sol";
import "../libraries/Signature.sol";

import "./MarginAccountModule.sol";
import "./PerpetualModule.sol";

import "../Type.sol";

library OrderModule {
    using SignedSafeMathUpgradeable for int256;
    using SafeCastUpgradeable for int256;
    using SafeMathExt for int256;
    using OrderData for Order;
    using OrderData for uint32;
    using MarginAccountModule for PerpetualStorage;
    using PerpetualModule for PerpetualStorage;

    /**
     * @notice Validate that order's signer is granted the trade privilege by order's trader
     * @param liquidityPool The liquidity pool object
     * @param order The order object
     * @param signature The signature
     */
    function validateSignature(
        LiquidityPoolStorage storage liquidityPool,
        Order memory order,
        bytes memory signature
    ) public view {
        bytes32 orderHash = order.getOrderHash();
        address signer = Signature.getSigner(orderHash, signature);
        if (signer != order.trader) {
            bool isAuthorized = IAccessControl(liquidityPool.accessController).isGranted(
                order.trader,
                signer,
                order.flags.useTargetLeverage()
                    ? Constant.PRIVILEGE_TRADE |
                        Constant.PRIVILEGE_DEPOSIT |
                        Constant.PRIVILEGE_WITHDRAW
                    : Constant.PRIVILEGE_TRADE
            );
            require(isAuthorized, "signer is unauthorized");
        }
    }

    /**
     * @notice Validate the order:
     *         1. broker of order = msg.sender
     *         2. relayer of order = tx.origin
     *         3. liquidity pool of order = address(this)
     *         4. perpetual index of order < count of perpetuals
     *         5. trading amount != 0 and has the same sign with amount of order
     *         6. amount of order != 0
     *         7. minimum trading amount of order <= abs(trading amount) <= abs(amount of order)
     *         8. order is not expire
     *         9. chain id of order is correct
     *         10. order is stop loss order and taker profit order at the same time
     * @param liquidityPool The liquidity pool
     * @param order The order
     * @param amount The trading amount of position
     */
    function validateOrder(
        LiquidityPoolStorage storage liquidityPool,
        Order memory order,
        int256 amount
    ) public view {
        // broker / relayer
        require(order.broker == msg.sender, "broker mismatch");
        require(order.relayer == tx.origin, "relayer mismatch");
        // pool / perpetual
        require(order.liquidityPool == address(this), "liquidity pool mismatch");
        require(
            order.perpetualIndex < liquidityPool.perpetualCount,
            "perpetual index out of range"
        );
        // amount
        require(amount != 0 && Utils.hasTheSameSign(amount, order.amount), "invalid amount");
        require(order.amount != 0, "order amount is 0");
        require(amount.abs() >= order.minTradeAmount, "amount is less than min trade amount");
        require(amount.abs() <= order.amount.abs(), "amount exceeds order amount");
        // expire
        require(order.expiredAt >= block.timestamp, "order is expired");
        // chain id
        require(order.chainID == Utils.chainID(), "chainid mismatch");
        // close only
        require(
            !(order.isStopLossOrder() && order.isTakeProfitOrder()),
            "stop-loss order cannot be take-profit"
        );
    }

    /**
     * @notice Validate the trigger price of the order
     *         When position > 0, if stop loss order: index price must <= trigger price,
     *                            if take profit order: index price must >= trigger price.
     *         When position < 0, if stop loss order: index price must >= trigger price,
     *                            if take profit order: index price must <= trigger price
     * @param liquidityPool The liquidity pool
     * @param order The order
     */
    function validateTriggerPrice(LiquidityPoolStorage storage liquidityPool, Order memory order)
        public
        view
    {
        int256 indexPrice = liquidityPool.perpetuals[order.perpetualIndex].getIndexPrice();
        if (
            (order.isStopLossOrder() && order.amount > 0) ||
            (order.isTakeProfitOrder() && order.amount < 0)
        ) {
            // stop-loss + long / take-profit + short
            require(indexPrice >= order.triggerPrice, "trigger price is not reached");
        } else if (
            (order.isStopLossOrder() && order.amount < 0) ||
            (order.isTakeProfitOrder() && order.amount > 0)
        ) {
            // stop-loss + long / take-profit + short
            require(indexPrice <= order.triggerPrice, "trigger price is not reached");
        }
    }
}

File 48 of 49 : Signature.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol";

import "../Type.sol";

library Signature {
    uint8 internal constant SIGN_TYPE_ETH = 0x0;
    uint8 internal constant SIGN_TYPE_EIP712 = 0x1;

    /*
     * @dev Get the signer of the transaction
     * @param signedHash The hash of the transaction
     * @param signature The signature of the transaction
     * @return signer The signer of the transaction
     */
    function getSigner(bytes32 digest, bytes memory signature)
        internal
        pure
        returns (address signer)
    {
        bytes32 r;
        bytes32 s;
        uint8 v;
        uint8 signType;
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
            signType := byte(1, mload(add(signature, 0x60)))
        }
        if (signType == SIGN_TYPE_ETH) {
            digest = ECDSAUpgradeable.toEthSignedMessageHash(digest);
        } else if (signType != SIGN_TYPE_EIP712) {
            revert("unsupported sign type");
        }
        signer = ECDSAUpgradeable.recover(digest, v, r, s);
    }
}

File 49 of 49 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/module/AMMModule.sol": {
      "AMMModule": "0xe594234baed1230cd1ab29544793ff0631b2fba7"
    },
    "contracts/module/LiquidityPoolModule.sol": {
      "LiquidityPoolModule": "0xd72345330e2da53e215daa3ffbad8f779cd4a7b4"
    },
    "contracts/module/OrderModule.sol": {
      "OrderModule": "0x0e535aa06e9e937f4837f43f1060545f0a4ed97c"
    },
    "contracts/module/TradeModule.sol": {
      "TradeModule": "0x5661ebd2dca2a5f57c9bae32faa53e439478fac2"
    }
  }
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"AddAMMKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"addedCash","type":"int256"},{"indexed":false,"internalType":"int256","name":"mintedShare","type":"int256"},{"indexed":false,"internalType":"int256","name":"addedPoolMargin","type":"int256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"AddTraderKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"ClaimOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"}],"name":"Clear","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"governor","type":"address"},{"indexed":false,"internalType":"address","name":"shareToken","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"int256[9]","name":"baseParams","type":"int256[9]"},{"indexed":false,"internalType":"int256[9]","name":"riskParams","type":"int256[9]"}],"name":"CreatePerpetual","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"DonateInsuranceFund","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"},{"indexed":false,"internalType":"int256","name":"penalty","type":"int256"},{"indexed":false,"internalType":"int256","name":"penaltyToLP","type":"int256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorCheckIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"RemoveAMMKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"returnedCash","type":"int256"},{"indexed":false,"internalType":"int256","name":"burnedShare","type":"int256"},{"indexed":false,"internalType":"int256","name":"removedPoolMargin","type":"int256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"keeper","type":"address"}],"name":"RemoveTraderKeeper","type":"event"},{"anonymous":false,"inputs":[],"name":"RevokeOperator","type":"event"},{"anonymous":false,"inputs":[],"name":"RunLiquidityPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"SetClearedState","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256","name":"settlementPrice","type":"int256"},{"indexed":false,"internalType":"uint256","name":"settlementTime","type":"uint256"}],"name":"SetEmergencyState","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256[4]","name":"value","type":"int256[4]"}],"name":"SetLiquidityPoolParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"SetNormalState","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":true,"internalType":"address","name":"newOracle","type":"address"}],"name":"SetOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256[9]","name":"baseParams","type":"int256[9]"}],"name":"SetPerpetualBaseParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256[9]","name":"riskParams","type":"int256[9]"},{"indexed":false,"internalType":"int256[9]","name":"minRiskParamValues","type":"int256[9]"},{"indexed":false,"internalType":"int256[9]","name":"maxRiskParamValues","type":"int256[9]"}],"name":"SetPerpetualRiskParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"targetLeverage","type":"int256"}],"name":"SetTargetLeverage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"Settle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"position","type":"int256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"},{"indexed":false,"internalType":"int256","name":"fee","type":"int256"},{"indexed":false,"internalType":"int256","name":"lpFee","type":"int256"}],"name":"Trade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"TransferExcessInsuranceFundToLP","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"int256","name":"operatorFee","type":"int256"}],"name":"TransferFeeToOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"int256","name":"referralRebate","type":"int256"}],"name":"TransferFeeToReferrer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"int256","name":"vaultFee","type":"int256"}],"name":"TransferFeeToVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"TransferOperatorTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256","name":"fundingRate","type":"int256"}],"name":"UpdateFundingRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256[9]","name":"riskParams","type":"int256[9]"}],"name":"UpdatePerpetualRiskParameter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"poolMargin","type":"int256"}],"name":"UpdatePoolMargin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"},{"indexed":false,"internalType":"int256","name":"markPrice","type":"int256"},{"indexed":false,"internalType":"uint256","name":"markPriceUpdateTime","type":"uint256"},{"indexed":false,"internalType":"int256","name":"indexPrice","type":"int256"},{"indexed":false,"internalType":"uint256","name":"indexPriceUpdateTime","type":"uint256"}],"name":"UpdatePrice","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":false,"internalType":"int256","name":"unitAccumulativeFunding","type":"int256"}],"name":"UpdateUnitAccumulativeFunding","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"int256","name":"amount","type":"int256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"keeper","type":"address"}],"name":"addAMMKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"cashToAdd","type":"int256"}],"name":"addLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"orderData","type":"bytes"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"brokerTrade","outputs":[{"internalType":"int256","name":"tradeAmount","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"clear","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"int256[9]","name":"baseParams","type":"int256[9]"},{"internalType":"int256[9]","name":"riskParams","type":"int256[9]"},{"internalType":"int256[9]","name":"minRiskParamValues","type":"int256[9]"},{"internalType":"int256[9]","name":"maxRiskParamValues","type":"int256[9]"}],"name":"createPerpetual","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount","type":"int256"}],"name":"donateInsuranceFund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"cashToAdd","type":"int256"}],"name":"donateLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"int256","name":"settlementPrice","type":"int256"}],"name":"forceToSetEmergencyState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"forceToSyncState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"getActiveAccountCount","outputs":[{"internalType":"uint256","name":"activeAccountCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"getClearProgress","outputs":[{"internalType":"uint256","name":"left","type":"uint256"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityPoolInfo","outputs":[{"internalType":"bool","name":"isRunning","type":"bool"},{"internalType":"bool","name":"isFastCreationEnabled","type":"bool"},{"internalType":"address[7]","name":"addresses","type":"address[7]"},{"internalType":"int256[5]","name":"intNums","type":"int256[5]"},{"internalType":"uint256[6]","name":"uintNums","type":"uint256[6]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"}],"name":"getMarginAccount","outputs":[{"internalType":"int256","name":"cash","type":"int256"},{"internalType":"int256","name":"position","type":"int256"},{"internalType":"int256","name":"availableMargin","type":"int256"},{"internalType":"int256","name":"margin","type":"int256"},{"internalType":"int256","name":"settleableMargin","type":"int256"},{"internalType":"bool","name":"isInitialMarginSafe","type":"bool"},{"internalType":"bool","name":"isMaintenanceMarginSafe","type":"bool"},{"internalType":"bool","name":"isMarginSafe","type":"bool"},{"internalType":"int256","name":"targetLeverage","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"getPerpetualInfo","outputs":[{"internalType":"enum PerpetualState","name":"state","type":"uint8"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"int256[42]","name":"nums","type":"int256[42]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolMargin","outputs":[{"internalType":"int256","name":"poolMargin","type":"int256"},{"internalType":"bool","name":"isAMMSafe","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"collateralDecimals","type":"uint256"},{"internalType":"address","name":"governor","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"}],"name":"liquidateByAMM","outputs":[{"internalType":"int256","name":"liquidationAmount","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"amount","type":"int256"},{"internalType":"int256","name":"limitPrice","type":"int256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"liquidateByTrader","outputs":[{"internalType":"int256","name":"liquidationAmount","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"listActiveAccounts","outputs":[{"internalType":"address[]","name":"result","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"listByAMMKeepers","outputs":[{"internalType":"address[]","name":"result","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"cashToAdd","type":"int256"},{"internalType":"int256","name":"shareToMint","type":"int256"}],"name":"queryAddLiquidity","outputs":[{"internalType":"int256","name":"cashToAddResult","type":"int256"},{"internalType":"int256","name":"shareToMintResult","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"shareToRemove","type":"int256"},{"internalType":"int256","name":"cashToReturn","type":"int256"}],"name":"queryRemoveLiquidity","outputs":[{"internalType":"int256","name":"shareToRemoveResult","type":"int256"},{"internalType":"int256","name":"cashToReturnResult","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"amount","type":"int256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint32","name":"flags","type":"uint32"}],"name":"queryTrade","outputs":[{"internalType":"int256","name":"tradePrice","type":"int256"},{"internalType":"int256","name":"totalFee","type":"int256"},{"internalType":"int256","name":"cost","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"keeper","type":"address"}],"name":"removeAMMKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"shareToRemove","type":"int256"},{"internalType":"int256","name":"cashToReturn","type":"int256"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"runLiquidityPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"}],"name":"setEmergencyState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256[4]","name":"params","type":"int256[4]"}],"name":"setLiquidityPoolParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"int256[9]","name":"baseParams","type":"int256[9]"}],"name":"setPerpetualBaseParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"int256[9]","name":"riskParams","type":"int256[9]"},{"internalType":"int256[9]","name":"minRiskParamValues","type":"int256[9]"},{"internalType":"int256[9]","name":"maxRiskParamValues","type":"int256[9]"}],"name":"setPerpetualRiskParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"targetLeverage","type":"int256"}],"name":"setTargetLeverage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"}],"name":"settle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"amount","type":"int256"},{"internalType":"int256","name":"limitPrice","type":"int256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint32","name":"flags","type":"uint32"}],"name":"trade","outputs":[{"internalType":"int256","name":"tradeAmount","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"transferOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"int256[9]","name":"riskParams","type":"int256[9]"}],"name":"updatePerpetualRiskParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"perpetualIndex","type":"uint256"},{"internalType":"address","name":"trader","type":"address"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b506173c280620000216000396000f3fe6080604052600436106102c85760003560e01c80636ef05a401161017957806390bbd3e4116100d6578063bd5b0a091161008a578063d73b5e4211610064578063d73b5e42146107e8578063dbabbf7714610808578063ed3574571461082b576102ee565b8063bd5b0a0914610793578063c0fe1af8146107b3578063d54e65fb146107d3576102ee565b80639db316b8116100bb5780639db316b814610749578063b080445614610769578063b674759c1461077e576102ee565b806390bbd3e414610709578063962d193814610729576102ee565b806378f140ea1161012d57806385dcd60a1161011257806385dcd60a1461069a57806389664990146106c95780638ceef981146106e9576102ee565b806378f140ea146106455780637d3ba80f14610665576102ee565b80637074020e1161015e5780637074020e146105f05780637086fd3214610610578063709240c414610625576102ee565b80636ef05a40146105b05780636fca8b99146105d0576102ee565b806329605e7711610227578063580818e3116101db5780635d0f1248116101c05780635d0f1248146105505780636282a56814610570578063675a0aa514610590576102ee565b8063580818e3146105105780635cfb7f8f14610530576102ee565b8063385975301161020c57806338597530146104b057806349a60713146104d05780634fababb3146104f0576102ee565b806329605e77146104705780632d47ba5714610490576102ee565b806313f07f451161027e57806317c675691161026357806317c675691461041b578063183ff0851461043b5780631bc5aa2014610450576102ee565b806313f07f45146103c057806316b3dd0e146103ed576102ee565b80630cdc105a116102af5780630cdc105a1461034d5780630f28c8b11461037357806310ec4beb146103a0576102ee565b8062092cce146102f35780629f22ae1461032b576102ee565b366102ee5760405162461bcd60e51b81526004016102e590616dae565b60405180910390fd5b600080fd5b3480156102ff57600080fd5b5061031361030e366004616545565b61084b565b60405161032293929190616a30565b60405180910390f35b34801561033757600080fd5b5061034b610346366004616596565b610ab5565b005b34801561035957600080fd5b50610362610c25565b6040516103229594939291906169b4565b34801561037f57600080fd5b5061039361038e366004616545565b610f02565b6040516103229190616a83565b3480156103ac57600080fd5b5061034b6103bb366004616545565b610f75565b3480156103cc57600080fd5b506103e06103db366004616775565b61106f565b6040516103229190616967565b3480156103f957600080fd5b5061040d610408366004616575565b6110e7565b604051610322929190616a9c565b34801561042757600080fd5b5061034b610436366004616596565b6113a1565b34801561044757600080fd5b5061034b6114ad565b34801561045c57600080fd5b506103e061046b366004616775565b6115a8565b34801561047c57600080fd5b5061034b61048b366004616335565b61161f565b34801561049c57600080fd5b5061034b6104ab366004616618565b6117c6565b3480156104bc57600080fd5b5061034b6104cb366004616545565b6119b9565b3480156104dc57600080fd5b506103936104eb3660046165c1565b611cea565b3480156104fc57600080fd5b5061039361050b3660046164a9565b61220b565b34801561051c57600080fd5b5061034b61052b366004616596565b612543565b34801561053c57600080fd5b5061034b61054b366004616545565b612696565b34801561055c57600080fd5b5061034b61056b3660046163f7565b612989565b34801561057c57600080fd5b5061034b61058b3660046166ff565b612b44565b34801561059c57600080fd5b5061034b6105ab3660046166ff565b612c51565b3480156105bc57600080fd5b5061034b6105cb366004616618565b612d57565b3480156105dc57600080fd5b5061034b6105eb366004616545565b6131de565b3480156105fc57600080fd5b5061034b61060b36600461634f565b613281565b34801561061c57600080fd5b5061034b6133a5565b34801561063157600080fd5b50610393610640366004616696565b6134df565b34801561065157600080fd5b5061034b610660366004616618565b6137c7565b34801561067157600080fd5b50610685610680366004616596565b613ad4565b60405161032299989796959493929190616ac0565b3480156106a657600080fd5b506106ba6106b536600461664c565b613bdc565b60405161032293929190616aaa565b3480156106d557600080fd5b5061034b6106e4366004616575565b613dcf565b3480156106f557600080fd5b5061040d610704366004616575565b6140d8565b34801561071557600080fd5b5061040d610724366004616545565b6142f8565b34801561073557600080fd5b5061034b610744366004616596565b6143a0565b34801561075557600080fd5b5061034b610764366004616463565b6145c6565b34801561077557600080fd5b5061034b6146db565b34801561078a57600080fd5b5061034b6147dd565b34801561079f57600080fd5b506103936107ae366004616596565b6148be565b3480156107bf57600080fd5b5061034b6107ce366004616545565b614c94565b3480156107df57600080fd5b5061034b614d73565b3480156107f457600080fd5b5061034b610803366004616575565b614ddb565b34801561081457600080fd5b5061081d6150b9565b604051610322929190616a8c565b34801561083757600080fd5b5061034b610846366004616723565b615149565b6000806108566161e9565b604254849081106108a4576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b6000858152604360209081526040918290206001810154835161054081019094526002820154845260ff8116975061010090046001600160a01b03169550919081016108ef8361526d565b81526020016108fd8361529f565b8152600a830154602080830191909152600b8401546040830152600c8401546060830152600d8401546080830152600e84015460a0830152600f84015460c0830152601084015460e08301526011840154610100830152601284015461012083015260138401546101408301526016840154610160830152601784015461018083015260188401546101a083015260198401546101c0830152601a8401546101e0830152601b840154610200830152601c840154610220830152601d840154610240830152601e840154610260830152601f8401546102808301528301546102a082015260218301546102c082015260258301546102e08201526026830154610300820152602783015461032082015260288301546103408201526029830154610360820152602a83015461038082015260038301546103a082015260158301546103c082015260228301546103e082015260238301546104008201526024830154610420820152603583015461044082015260368301546104608201526037830154610480820152603d8301546104a0820152603e8301546104c0820152603f909201546104e09092019190915293959294505050565b6000610ac160336152cd565b90506001600160a01b03811615610b2e57806001600160a01b0316610ae46152f7565b6001600160a01b031614610b295760405162461bcd60e51b81526004018080602001828103825260218152602001806172236021913960400191505060405180910390fd5b610b87565b6036546001600160a01b0316610b426152f7565b6001600160a01b031614610b875760405162461bcd60e51b81526004018080602001828103825260218152602001806172aa6021913960400191505060405180910390fd5b604080517f48ea454c00000000000000000000000000000000000000000000000000000000815260336004820152602481018590526001600160a01b0384166044820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4916348ea454c916064808301926000929190829003018186803b158015610c0857600080fd5b505af4158015610c1c573d6000803e3d6000fd5b50505050505050565b600080610c30616208565b610c38616226565b610c40616244565b603380546040805160e081019091526001600160a01b0362010000830416815260ff80831698506101009092049091169550906020820190610c81906152cd565b6001600160a01b03168152602001610c9960336152fb565b6001600160a01b03908116825260365481166020808401919091526037548216604080850191909152603b54909216606084015281517fe1858ce900000000000000000000000000000000000000000000000000000000815260336004820152915160809093019273d72345330e2da53e215daa3ffbad8f779cd4a7b49263e1858ce9926024808301939192829003018186803b158015610d3957600080fd5b505af4158015610d4d573d6000803e3d6000fd5b505050506040513d6020811015610d6357600080fd5b50516001600160a01b03169052604080517fb1a4182a00000000000000000000000000000000000000000000000000000000815260336004820152905191945073d72345330e2da53e215daa3ffbad8f779cd4a7b49163b1a4182a91602480820192602092909190829003018186803b158015610ddf57600080fd5b505af4158015610df3573d6000803e3d6000fd5b505050506040513d6020811015610e0957600080fd5b50518252603c54602080840191909152604454604080850191909152604554606080860191909152604654608080870191909152603a54855260425485850152603d5485840152603f54918501919091526048549084015280517f374331ff00000000000000000000000000000000000000000000000000000000815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49263374331ff9260248082019391829003018186803b158015610ec657600080fd5b505af4158015610eda573d6000803e3d6000fd5b505050506040513d6020811015610ef057600080fd5b505160a0820152939492939192909190565b6000816033600f01548110610f54576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b6000838152604360205260409020610f6e90603001615321565b9392505050565b60026074541415610fcd576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260745560335460ff16610ff45760405162461bcd60e51b81526004016102e590616de5565b73d72345330e2da53e215daa3ffbad8f779cd4a7b46383a1eaa860336110186152f7565b846040518463ffffffff1660e01b81526004016110379392919061700b565b60006040518083038186803b15801561104f57600080fd5b505af4158015611063573d6000803e3d6000fd5b50506001607455505050565b6060836033600f015481106110c1576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b60008581526043602052604090206110dd60308201868661532c565b9695505050505050565b603354600090819060ff16611143576040805162461bcd60e51b815260206004820152601360248201527f706f6f6c206973206e6f742072756e6e696e6700000000000000000000000000604482015290519081900360640190fd5b60006111ca603360040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561119957600080fd5b505afa1580156111ad573d6000803e3d6000fd5b505050506040513d60208110156111c357600080fd5b505161544a565b90506000851380156111da575083155b1561128a57604080517ff72ee9c8000000000000000000000000000000000000000000000000000000008152603360048201526024810183905260448101879052815173e594234baed1230cd1ab29544793ff0631b2fba79263f72ee9c89260648082019391829003018186803b15801561125457600080fd5b505af4158015611268573d6000803e3d6000fd5b505050506040513d604081101561127e57600080fd5b50518593509150611399565b841580156112985750600084135b1561134c57604080517f89127d52000000000000000000000000000000000000000000000000000000008152603360048201526024810183905260448101869052905173e594234baed1230cd1ab29544793ff0631b2fba7916389127d52916064808301926020929190829003018186803b15801561131657600080fd5b505af415801561132a573d6000803e3d6000fd5b505050506040513d602081101561134057600080fd5b50519250839150611399565b6040805162461bcd60e51b815260206004820152601160248201527f696e76616c696420706172616d65746572000000000000000000000000000000604482015290519081900360640190fd5b509250929050565b6036546001600160a01b03166113b56152f7565b6001600160a01b031614611410576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c7920676f7665726e6f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b604080517f38dbaf7100000000000000000000000000000000000000000000000000000000815260336004820152602481018490526001600160a01b0383166044820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4916338dbaf71916064808301926000929190829003018186803b15801561149157600080fd5b505af41580156114a5573d6000803e3d6000fd5b505050505050565b6114b760336152cd565b6001600160a01b03166114c86152f7565b6001600160a01b031614611523576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c79206f70657261746f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b604080517f5980576b00000000000000000000000000000000000000000000000000000000815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b491635980576b916024808301926000929190829003018186803b15801561158e57600080fd5b505af41580156115a2573d6000803e3d6000fd5b50505050565b6060836033600f015481106115fa576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b600085815260436020526040902061161690603901858561532c565b95945050505050565b600061162b60336152cd565b90506001600160a01b0381161561169857806001600160a01b031661164e6152f7565b6001600160a01b0316146116935760405162461bcd60e51b81526004018080602001828103825260218152602001806172236021913960400191505060405180910390fd5b6116f1565b6036546001600160a01b03166116ac6152f7565b6001600160a01b0316146116f15760405162461bcd60e51b81526004018080602001828103825260218152602001806172aa6021913960400191505060405180910390fd5b6001600160a01b03821661174c576040805162461bcd60e51b815260206004820152601c60248201527f6e6577206f70657261746f72206973207a65726f206164647265737300000000604482015290519081900360640190fd5b604080517f4f28feeb000000000000000000000000000000000000000000000000000000008152603360048201526001600160a01b0384166024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b491634f28feeb916044808301926000929190829003018186803b15801561149157600080fd5b81600773d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a86033846117ee6152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b15801561184a57600080fd5b505af415801561185e573d6000803e3d6000fd5b505050506040513d602081101561187457600080fd5b50516118bd576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b6001600160a01b0384166118e35760405162461bcd60e51b81526004016102e590616f0e565b670de0b6b3a764000083071561190b5760405162461bcd60e51b81526004016102e590616b73565b6000831361192b5760405162461bcd60e51b81526004016102e590616cac565b6040517fdaad7f6900000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b49063daad7f6990611982906033908990899089906004016171bf565b60006040518083038186803b15801561199a57600080fd5b505af41580156119ae573d6000803e3d6000fd5b505050505050505050565b60408051634b071c5760e11b815260336004820152426024820181905291516001929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae91604480820192600092909190829003018186803b158015611a1857600080fd5b505af4158015611a2c573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015611a8a57600080fd5b505af4158015611a9e573d6000803e3d6000fd5b50505050600019831415611b3457604080517fa99d455a00000000000000000000000000000000000000000000000000000000815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49163a99d455a916024808301926000929190829003018186803b158015611b1757600080fd5b505af4158015611b2b573d6000803e3d6000fd5b50505050611c98565b6000838152604360209081526040808320600181015482517fd1cc9976000000000000000000000000000000000000000000000000000000008152925191946101009091046001600160a01b03169363d1cc9976936004808201949293918390030190829087803b158015611ba857600080fd5b505af1158015611bbc573d6000803e3d6000fd5b505050506040513d6020811015611bd257600080fd5b5051611c25576040805162461bcd60e51b815260206004820152601460248201527f707265726571756973697465206e6f74206d6574000000000000000000000000604482015290519081900360640190fd5b604080516301ec95cd60e51b81526033600482015260248101869052905173d72345330e2da53e215daa3ffbad8f779cd4a7b491633d92b9a0916044808301926000929190829003018186803b158015611c7e57600080fd5b505af4158015611c92573d6000803e3d6000fd5b50505050505b60408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49163990d2052916024808301926000929190829003018186803b158015610c0857600080fd5b600060026074541415611d44576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002607481905550603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d9d57600080fd5b505afa158015611db1573d6000803e3d6000fd5b505050506040513d6020811015611dc757600080fd5b505115611e0e576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b85600873d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a8603384611e366152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b158015611e9257600080fd5b505af4158015611ea6573d6000803e3d6000fd5b505050506040513d6020811015611ebc57600080fd5b5051611f05576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b158015611f6357600080fd5b505af4158015611f77573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015611fd557600080fd5b505af4158015611fe9573d6000803e3d6000fd5b5060029250611ff6915050565b60008c81526043602052604090206001015460ff16600481111561201657fe5b146120335760405162461bcd60e51b81526004016102e590616c18565b6001600160a01b0389166120595760405162461bcd60e51b81526004016102e590616f0e565b6001600160a01b0389163014156120825760405162461bcd60e51b81526004016102e590616b3c565b8761209f5760405162461bcd60e51b81526004016102e590616be1565b60008712156120c05760405162461bcd60e51b81526004016102e590616b05565b428610156120e05760405162461bcd60e51b81526004016102e590616d77565b6040517f15e47827000000000000000000000000000000000000000000000000000000008152735661ebd2dca2a5f57c9bae32faa53e439478fac2906315e478279061213b906033908f908f908f908f908f9060040161718d565b60206040518083038186803b15801561215357600080fd5b505af4158015612167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218b919061655d565b9450603373d72345330e2da53e215daa3ffbad8f779cd4a7b463990d205290916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b1580156121df57600080fd5b505af41580156121f3573d6000803e3d6000fd5b5050600160745550949b9a5050505050505050505050565b60408051634b071c5760e11b815260336004820152426024820181905291516000928392909173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae9160448083019287929190829003018186803b15801561226b57600080fd5b505af415801561227f573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b1580156122dd57600080fd5b505af41580156122f1573d6000803e3d6000fd5b505050506122fd616262565b61230686615492565b905060606123138761557e565b6040517f8120e3d1000000000000000000000000000000000000000000000000000000008152909150730e535aa06e9e937f4837f43f1060545f0a4ed97c90638120e3d19061236b90603390869086906004016170b1565b60006040518083038186803b15801561238357600080fd5b505af4158015612397573d6000803e3d6000fd5b50506040517ff2ab44c2000000000000000000000000000000000000000000000000000000008152730e535aa06e9e937f4837f43f1060545f0a4ed97c925063f2ab44c291506123f09060339086908b90600401617123565b60006040518083038186803b15801561240857600080fd5b505af415801561241c573d6000803e3d6000fd5b50506040517f4af2e327000000000000000000000000000000000000000000000000000000008152730e535aa06e9e937f4837f43f1060545f0a4ed97c9250634af2e327915061247390603390869060040161709c565b60006040518083038186803b15801561248b57600080fd5b505af415801561249f573d6000803e3d6000fd5b505050506124cd82610160015163ffffffff168360000151888560e001518660600151876101a001516155f0565b94505050603373d72345330e2da53e215daa3ffbad8f779cd4a7b463990d205290916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b15801561252357600080fd5b505af4158015612537573d6000803e3d6000fd5b50505050505092915050565b600061254f60336152cd565b90506001600160a01b038116156125bc57806001600160a01b03166125726152f7565b6001600160a01b0316146125b75760405162461bcd60e51b81526004018080602001828103825260218152602001806172236021913960400191505060405180910390fd5b612615565b6036546001600160a01b03166125d06152f7565b6001600160a01b0316146126155760405162461bcd60e51b81526004018080602001828103825260218152602001806172aa6021913960400191505060405180910390fd5b604080517fc01fa80800000000000000000000000000000000000000000000000000000000815260336004820152602481018590526001600160a01b0384166044820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49163c01fa808916064808301926000929190829003018186803b158015610c0857600080fd5b603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156126e757600080fd5b505afa1580156126fb573d6000803e3d6000fd5b505050506040513d602081101561271157600080fd5b505115612758576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b1580156127b657600080fd5b505af41580156127ca573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b15801561282857600080fd5b505af415801561283c573d6000803e3d6000fd5b5050505060026074541415612898576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260745560335460ff166128bf5760405162461bcd60e51b81526004016102e590616de5565b73d72345330e2da53e215daa3ffbad8f779cd4a7b463dcaf674960336128e36152f7565b866040518463ffffffff1660e01b81526004016129029392919061700b565b60006040518083038186803b15801561291a57600080fd5b505af415801561292e573d6000803e3d6000fd5b50506001607455505060408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49163990d2052916024808301926000929190829003018186803b158015610c0857600080fd5b603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b1580156129da57600080fd5b505afa1580156129ee573d6000803e3d6000fd5b505050506040513d6020811015612a0457600080fd5b505115612a4b576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b60335460ff161580612a645750603354610100900460ff165b15612aaf57612a7360336152cd565b6001600160a01b0316612a846152f7565b6001600160a01b031614612aaa5760405162461bcd60e51b81526004016102e590616e95565b612ae9565b6036546001600160a01b0316612ac36152f7565b6001600160a01b031614612ae95760405162461bcd60e51b81526004016102e590616e53565b6040517fbb89ac2400000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b49063bb89ac2490611982906033908990899089908990899060040161704e565b612b4e60336152cd565b6001600160a01b0316612b5f6152f7565b6001600160a01b031614612bba576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c79206f70657261746f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b6040517febf1f2680000000000000000000000000000000000000000000000000000000081526033600482018181526024830185905273d72345330e2da53e215daa3ffbad8f779cd4a7b49263ebf1f268929186918691906044018261012080828437600081840152601f19601f820116905080830192505050935050505060006040518083038186803b15801561149157600080fd5b6036546001600160a01b0316612c656152f7565b6001600160a01b031614612cc0576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c7920676f7665726e6f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b6040517ff20d68960000000000000000000000000000000000000000000000000000000081526033600482018181526024830185905273d72345330e2da53e215daa3ffbad8f779cd4a7b49263f20d6896929186918691906044018261012080828437600081840152601f19601f820116905080830192505050935050505060006040518083038186803b15801561149157600080fd5b60026074541415612daf576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002607481905550603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e0857600080fd5b505afa158015612e1c573d6000803e3d6000fd5b505050506040513d6020811015612e3257600080fd5b505115612e79576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b158015612ed757600080fd5b505af4158015612eeb573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015612f4957600080fd5b505af4158015612f5d573d6000803e3d6000fd5b50505050836002603373d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a8909184612f8b6152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b158015612fe757600080fd5b505af4158015612ffb573d6000803e3d6000fd5b505050506040513d602081101561301157600080fd5b505161305a576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b600260008881526043602052604090206001015460ff16600481111561307c57fe5b146130995760405162461bcd60e51b81526004016102e590616c18565b6001600160a01b0386166130bf5760405162461bcd60e51b81526004016102e590616f0e565b600085136130df5760405162461bcd60e51b81526004016102e590616be1565b6040517fa54b4d7800000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b49063a54b4d7890613136906033908b908b908b906004016171bf565b60006040518083038186803b15801561314e57600080fd5b505af4158015613162573d6000803e3d6000fd5b505060408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4955063990d2052945060248083019450600093509091829003018186803b1580156131ba57600080fd5b505af41580156131ce573d6000803e3d6000fd5b5050600160745550505050505050565b60026074541415613236576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260745560335460ff1661325d5760405162461bcd60e51b81526004016102e590616de5565b73d72345330e2da53e215daa3ffbad8f779cd4a7b463d767897e60336110186152f7565b600054610100900460ff168061329a575061329a6157ab565b806132a8575060005460ff16155b6132e35760405162461bcd60e51b815260040180806020018281038252602e8152602001806172cb602e913960400191505060405180910390fd5b600054610100900460ff1615801561330e576000805460ff1961ff0019909116610100171660011790555b73d72345330e2da53e215daa3ffbad8f779cd4a7b463c9ab757160336133326152f7565b89898c8a8a8a6040518963ffffffff1660e01b815260040161335b989796959493929190616fa2565b60006040518083038186803b15801561337357600080fd5b505af4158015613387573d6000803e3d6000fd5b505050508015610c1c576000805461ff001916905550505050505050565b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b15801561340357600080fd5b505af4158015613417573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b15801561347557600080fd5b505af4158015613489573d6000803e3d6000fd5b505060408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4935063990d205292506024808301926000929190829003018186803b15801561149157600080fd5b6000866134f18363ffffffff166157bc565b6134fc5760046134ff565b60075b73d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a86033846135246152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b15801561358057600080fd5b505af4158015613594573d6000803e3d6000fd5b505050506040513d60208110156135aa57600080fd5b50516135f3576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b15801561365157600080fd5b505af4158015613665573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b1580156136c357600080fd5b505af41580156136d7573d6000803e3d6000fd5b505050506001600160a01b038b166137015760405162461bcd60e51b81526004016102e590616f0e565b8961371e5760405162461bcd60e51b81526004016102e590616be1565b4288101561373e5760405162461bcd60e51b81526004016102e590616d77565b61374c8c8c8c8c8b8b6155f0565b9450603373d72345330e2da53e215daa3ffbad8f779cd4a7b463990d205290916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b1580156137a057600080fd5b505af41580156137b4573d6000803e3d6000fd5b5050505050505050979650505050505050565b6002607454141561381f576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002607481905550603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561387857600080fd5b505afa15801561388c573d6000803e3d6000fd5b505050506040513d60208110156138a257600080fd5b5051156138e9576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b81600173d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a86033846139116152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b15801561396d57600080fd5b505af4158015613981573d6000803e3d6000fd5b505050506040513d602081101561399757600080fd5b50516139e0576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b600260008681526043602052604090206001015460ff166004811115613a0257fe5b14613a1f5760405162461bcd60e51b81526004016102e590616c18565b6001600160a01b038416613a455760405162461bcd60e51b81526004016102e590616f0e565b60008313613a655760405162461bcd60e51b81526004016102e590616be1565b6040517f028c4ff600000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b49063028c4ff690613abc906033908990899089906004016171bf565b60006040518083038186803b1580156131ba57600080fd5b60008060008060008060008060008a6033600f01548110613b32576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b60008c81526043602090815260408083206001600160a01b038f1684526034810190925282209091613b638361526d565b82546001840154909e509c509050613b7c838f8361580f565b9a50613b89838f83615859565b9950613b96838f83615890565b9850613ba3838f836158e9565b9750613bb0838f83615901565b9650613bbd838f83615947565b9550613bc9838f615963565b9450505050509295985092959850929598565b600080806001600160a01b038716613c3b576040805162461bcd60e51b815260206004820152600e60248201527f696e76616c696420747261646572000000000000000000000000000000000000604482015290519081900360640190fd5b85613c8d576040805162461bcd60e51b815260206004820152600e60248201527f696e76616c696420616d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b600260008981526043602052604090206001015460ff166004811115613caf57fe5b14613ceb5760405162461bcd60e51b81526004018080602001828103825260238152602001806172876023913960400191505060405180910390fd5b604080517f1c137ad100000000000000000000000000000000000000000000000000000000815260336004820152602481018a90526001600160a01b03808a166044830152606482018990528716608482015263ffffffff861660a48201529051735661ebd2dca2a5f57c9bae32faa53e439478fac291631c137ad19160c4808301926060929190829003018186803b158015613d8757600080fd5b505af4158015613d9b573d6000803e3d6000fd5b505050506040513d6060811015613db157600080fd5b5080516020820151604090920151909a919950975095505050505050565b60026074541415613e27576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b600260745560408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b158015613e8a57600080fd5b505af4158015613e9e573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015613efc57600080fd5b505af4158015613f10573d6000803e3d6000fd5b505060335460ff169150613f3890505760405162461bcd60e51b81526004016102e590616de5565b603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b158015613f8957600080fd5b505afa158015613f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fc19190616489565b15613fee57613fd260336004615a1f565b613fee5760405162461bcd60e51b81526004016102e590616e1c565b73d72345330e2da53e215daa3ffbad8f779cd4a7b4632d7966d860336140126152f7565b87876040518563ffffffff1660e01b8152600401614033949392919061702a565b60006040518083038186803b15801561404b57600080fd5b505af415801561405f573d6000803e3d6000fd5b505060408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4935063990d205292506024808301926000929190829003018186803b1580156140b557600080fd5b505af41580156140c9573d6000803e3d6000fd5b50506001607455505050505050565b603354600090819060ff16614134576040805162461bcd60e51b815260206004820152601360248201527f706f6f6c206973206e6f742072756e6e696e6700000000000000000000000000604482015290519081900360640190fd5b600061418a603360040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561119957600080fd5b905060008513801561419a575083155b1561424257604080517feeffc15d000000000000000000000000000000000000000000000000000000008152603360048201526024810183905260448101879052905173e594234baed1230cd1ab29544793ff0631b2fba79163eeffc15d916064808301926080929190829003018186803b15801561421857600080fd5b505af415801561422c573d6000803e3d6000fd5b505050506040513d608081101561127e57600080fd5b841580156142505750600084135b1561134c57604080517f68dddaa6000000000000000000000000000000000000000000000000000000008152603360048201526024810183905260448101869052905173e594234baed1230cd1ab29544793ff0631b2fba7916368dddaa6916064808301926080929190829003018186803b1580156142ce57600080fd5b505af41580156142e2573d6000803e3d6000fd5b505050506040513d608081101561134057600080fd5b600080826033600f0154811061434b576040805162461bcd60e51b81526020600482015260136024820152721c195c9c195d1d585b081b9bdd08195e1a5cdd606a1b604482015290519081900360640190fd5b600084815260436020526040902061436560308201615321565b93506002600182015460ff16600481111561437c57fe5b1461438b5780602b0154614397565b61439781603001615321565b92505050915091565b80600273d72345330e2da53e215daa3ffbad8f779cd4a7b463e57d06a86033846143c86152f7565b856040518563ffffffff1660e01b815260040180858152602001846001600160a01b03168152602001836001600160a01b0316815260200182815260200194505050505060206040518083038186803b15801561442457600080fd5b505af4158015614438573d6000803e3d6000fd5b505050506040513d602081101561444e57600080fd5b5051614497576040805162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b604482015290519081900360640190fd5b600260745414156144ef576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b60026074556001600160a01b03831661451a5760405162461bcd60e51b81526004016102e590616f0e565b600460008581526043602052604090206001015460ff16600481111561453c57fe5b146145595760405162461bcd60e51b81526004016102e590616f45565b6040517fb2f6decc00000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b49063b2f6decc906145ae9060339088908890600401617147565b60006040518083038186803b1580156140b557600080fd5b6036546001600160a01b03166145da6152f7565b6001600160a01b031614614635576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c7920676f7665726e6f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b6040517fe3e2898600000000000000000000000000000000000000000000000000000000815260336004820181815273d72345330e2da53e215daa3ffbad8f779cd4a7b49263e3e289869291859160240182608080828437600081840152601f19601f8201169050808301925050509250505060006040518083038186803b1580156146c057600080fd5b505af41580156146d4573d6000803e3d6000fd5b5050505050565b6146e560336152cd565b6001600160a01b03166146f66152f7565b6001600160a01b031614614751576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c79206f70657261746f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b60335460ff16156147745760405162461bcd60e51b81526004016102e590616c75565b6040517f4e74d93000000000000000000000000000000000000000000000000000000000815273d72345330e2da53e215daa3ffbad8f779cd4a7b490634e74d930906147c590603390600401616a83565b60006040518083038186803b15801561158e57600080fd5b6147e760336152cd565b6001600160a01b03166147f86152f7565b6001600160a01b031614614853576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c79206f70657261746f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b604080517fbdc5e84900000000000000000000000000000000000000000000000000000000815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49163bdc5e849916024808301926000929190829003018186803b15801561158e57600080fd5b600060026074541415614918576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002607481905550603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561497157600080fd5b505afa158015614985573d6000803e3d6000fd5b505050506040513d602081101561499b57600080fd5b5051156149e2576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b60408051634b071c5760e11b815260336004820152426024820181905291516000929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae916044808201928792909190829003018186803b158015614a4057600080fd5b505af4158015614a54573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015614ab257600080fd5b505af4158015614ac6573d6000803e3d6000fd5b50505050614adb85614ad66152f7565b615a82565b614af75760405162461bcd60e51b81526004016102e590616ed7565b600260008681526043602052604090206001015460ff166004811115614b1957fe5b14614b365760405162461bcd60e51b81526004016102e590616c18565b6001600160a01b038416614b5c5760405162461bcd60e51b81526004016102e590616f0e565b6001600160a01b038416301415614b855760405162461bcd60e51b81526004016102e590616b3c565b735661ebd2dca2a5f57c9bae32faa53e439478fac263e810d619603387614baa6152f7565b886040518563ffffffff1660e01b8152600401614bca9493929190617166565b60206040518083038186803b158015614be257600080fd5b505af4158015614bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c1a919061655d565b9250603373d72345330e2da53e215daa3ffbad8f779cd4a7b463990d205290916040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b158015614c6e57600080fd5b505af4158015614c82573d6000803e3d6000fd5b50506001607455509295945050505050565b60026074541415614cec576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002607455600360008281526043602052604090206001015460ff166004811115614d1357fe5b14614d305760405162461bcd60e51b81526004016102e590616d1a565b73d72345330e2da53e215daa3ffbad8f779cd4a7b4632ef9dab9603383614d556152f7565b6040518463ffffffff1660e01b815260040161103793929190617147565b73d72345330e2da53e215daa3ffbad8f779cd4a7b463299cbfda6033614d976152f7565b6040518363ffffffff1660e01b815260040180838152602001826001600160a01b031681526020019250505060006040518083038186803b15801561158e57600080fd5b60408051634b071c5760e11b815260336004820152426024820181905291516001929173d72345330e2da53e215daa3ffbad8f779cd4a7b49163960e38ae91604480820192600092909190829003018186803b158015614e3a57600080fd5b505af4158015614e4e573d6000803e3d6000fd5b5050604080516304c43a9960e31b8152603360048201528515156024820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b49350632621d4c892506044808301926000929190829003018186803b158015614eac57600080fd5b505af4158015614ec0573d6000803e3d6000fd5b50506036546001600160a01b03169150614eda90506152f7565b6001600160a01b031614614f35576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c7920676f7665726e6f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b6000831215614f8b576040805162461bcd60e51b815260206004820152601960248201527f6e6567617469766520736574746c656d656e7420707269636500000000000000604482015290519081900360640190fd5b614f936162de565b5060408051808201825284815242602080830191825260008881526043909152838120835160068201819055925160078201819055600480830194909455600582015584516301ec95cd60e51b815260339381019390935260248301899052935192939273d72345330e2da53e215daa3ffbad8f779cd4a7b492633d92b9a0926044808301939192829003018186803b15801561502f57600080fd5b505af4158015615043573d6000803e3d6000fd5b505060408051634c86902960e11b815260336004820152905173d72345330e2da53e215daa3ffbad8f779cd4a7b4955063990d2052945060248083019450600093509091829003018186803b15801561509b57600080fd5b505af41580156150af573d6000803e3d6000fd5b5050505050505050565b600080603373e594234baed1230cd1ab29544793ff0631b2fba763c12b64d590916040518263ffffffff1660e01b815260040180828152602001915050604080518083038186803b15801561510d57600080fd5b505af4158015615121573d6000803e3d6000fd5b505050506040513d604081101561513757600080fd5b50805160209091015190939092509050565b6036546001600160a01b031661515d6152f7565b6001600160a01b0316146151b8576040805162461bcd60e51b815260206004820152601860248201527f6f6e6c7920676f7665726e6f7220697320616c6c6f7765640000000000000000604482015290519081900360640190fd5b603373d72345330e2da53e215daa3ffbad8f779cd4a7b4633d792b6e9091868686866040518663ffffffff1660e01b81526004018086815260200185815260200184600960200280828437600083820152601f01601f191690910190508361012080828437600083820152601f01601f191690910190508261012080828437600081840152601f19601f8201169050808301925050509550505050505060006040518083038186803b15801561509b57600080fd5b60006002600183015460ff16600481111561528457fe5b14615293576008820154615299565b60068201545b92915050565b60006002600183015460ff1660048111156152b657fe5b146152c5576008820154615299565b506004015490565b600081600c01544211156152e2576000615299565b5060018101546001600160a01b03165b919050565b3390565b600081600c0154421115615310576000615299565b50600201546001600160a01b031690565b600061529982615b50565b6060828211615382576040805162461bcd60e51b815260206004820152601e60248201527f626567696e2073686f756c64206265206c6f776572207468616e20656e640000604482015290519081900360640190fd5b600061538d85615321565b905080841061539c5750610f6e565b60006153a88483615b54565b90506153b48186615b6a565b67ffffffffffffffff811180156153ca57600080fd5b506040519080825280602002602001820160405280156153f4578160200160208202803683370190505b509250845b818110156154405761540b8782615bc7565b846154168389615b6a565b8151811061542057fe5b6001600160a01b03909216602092830291909101909101526001016153f9565b5050509392505050565b6000600160ff1b821061548e5760405162461bcd60e51b81526004018080602001828103825260288152602001806173416028913960400191505060405180910390fd5b5090565b61549a616262565b610100825110156154bd5760405162461bcd60e51b81526004016102e590616baa565b6014820151815260288201516020820152603c82015160408083019190915260508301516060808401919091526064840151608080850191909152608485015160a08086019190915260a486015160c08087019190915260c487015160e087015260e4870151610100870152610104870151610120870152610124909601519586901c61014086015263ffffffff9086901c81166101608601529085901c81166101808501529084901c81166101a084015292901c9091166101c082015290565b606061015e825110156155a35760405162461bcd60e51b81526004016102e590616baa565b61013e82015161015e830151610124840151604051601882901a9160191a906155d690859085908590859060200161690c565b604051602081830303815290604052945050505050919050565b6000603360000160029054906101000a90046001600160a01b03166001600160a01b0316634831d32f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561564357600080fd5b505afa158015615657573d6000803e3d6000fd5b505050506040513d602081101561566d57600080fd5b5051156156b4576040805162461bcd60e51b815260206004820152601060248201526f1d5b9a5d995c9cd9481cd95d1d1b195960821b604482015290519081900360640190fd5b600260008881526043602052604090206001015460ff1660048111156156d657fe5b146156f35760405162461bcd60e51b81526004016102e590616c18565b6040517f443d6cae000000000000000000000000000000000000000000000000000000008152735661ebd2dca2a5f57c9bae32faa53e439478fac29063443d6cae90615750906033908b908b908b908b908b908b906004016171e3565b60206040518083038186803b15801561576857600080fd5b505af415801561577c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906157a0919061655d565b979650505050505050565b60006157b630615bd3565b15905090565b6000806157c883615bd9565b905060006157d584615be4565b90508180156157e15750805b156157fe5760405162461bcd60e51b81526004016102e590616ce3565b81806158075750805b949350505050565b60008061581c8585615bf7565b1561583f5761583a8560120154615834878787615c18565b90615c5b565b615842565b60005b905061161681615853878787615859565b90615cc0565b60006158076158688585615d25565b6001600160a01b03851660009081526034870160205260409020600101546158349085615d5f565b600061589d848484615859565b905060008113156158df5760006158b48585615bf7565b156158c35784602f01546158c9565b84602e01545b90506158d782826001615d94565b915050610f6e565b5060009392505050565b6000806158f785858561580f565b1215949350505050565b60008061590e8585615bf7565b1561592b576159268560120154615834878787615db2565b61592e565b60005b90508061593c868686615859565b121595945050505050565b6000806159548585615bf7565b1561592b57846012015461592e565b600082600c0154600014156159bf576040805162461bcd60e51b815260206004820152601c60248201527f696e697469616c4d617267696e52617465206973206e6f742073657400000000604482015290519081900360640190fd5b60006159e084600c0154670de0b6b3a7640000615dea90919063ffffffff16565b6001600160a01b03841660009081526034860160205260409020600201549091508015615a0d5780615a13565b60358501545b90506116168183615e30565b600f820154600090815b81811015615a7757836004811115615a3d57fe5b600082815260108701602052604090206001015460ff166004811115615a5f57fe5b14615a6f57600092505050615299565b600101615a29565b506001949350505050565b6000828152604360205260408120603901615a9c81615321565b615b46576033546040517f6ba42aaa000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b031690636ba42aaa90615aee908690600401616953565b60206040518083038186803b158015615b0657600080fd5b505afa158015615b1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190615b3e9190616489565b915050615299565b615b3e8184615e3f565b5490565b6000818310615b635781610f6e565b5090919050565b600082821115615bc1576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6000610f6e8383615e54565b3b151590565b630800000016151590565b600080615bf083615eb8565b1392915050565b6001600160a01b031660009081526034909101602052604090206001015490565b600c8301546001600160a01b0383166000908152603485016020526040812060010154909161580791615c569190615c509086615d5f565b90615d5f565b615ecc565b6000828201818312801590615c705750838112155b80615c855750600083128015615c8557508381125b610f6e5760405162461bcd60e51b81526004018080602001828103825260218152602001806172666021913960400191505060405180910390fd5b6000818303818312801590615cd55750838113155b80615cea5750600083128015615cea57508381135b610f6e5760405162461bcd60e51b81526004018080602001828103825260248152602001806173696024913960400191505060405180910390fd5b6001600160a01b03811660009081526034830160205260408120600b840154600182015461580791615d579190615d5f565b825490615cc0565b6000670de0b6b3a7640000615d85615d778585615ee4565b670de0b6b3a7640000615f8d565b81615d8c57fe5b059392505050565b6000615807615da38585615ee4565b670de0b6b3a76400008461600c565b600d8301546001600160a01b0383166000908152603485016020526040812060010154909161580791615c569190615c509086615d5f565b600080821215615e0b57615dfd826160e9565b9150615e08836160e9565b92505b610f6e82615e2a615e2486670de0b6b3a7640000615ee4565b85615f8d565b906160f6565b6000818312615b635781610f6e565b6000610f6e836001600160a01b0384166161ae565b81546000908210615e965760405162461bcd60e51b81526004018080602001828103825260228152602001806172446022913960400191505060405180910390fd5b826000018281548110615ea557fe5b9060005260206000200154905092915050565b60071c620fffff16662386f26fc100000290565b60008082121561548e57615edf826160e9565b615299565b600082615ef357506000615299565b82600019148015615f075750600160ff1b82145b15615f435760405162461bcd60e51b815260040180806020018281038252602781526020018061731a6027913960400191505060405180910390fd5b82820282848281615f5057fe5b0514610f6e5760405162461bcd60e51b815260040180806020018281038252602781526020018061731a6027913960400191505060405180910390fd5b6000808213615fe3576040805162461bcd60e51b815260206004820152601f60248201527f726f756e6448616c665570206f6e6c7920737570706f7274732079203e203000604482015290519081900360640190fd5b60008312615fff57615ff88360028405615c5b565b9050615299565b610f6e8360028405615cc0565b600082616060576040805162461bcd60e51b815260206004820152601060248201527f6469766973696f6e206279207a65726f00000000000000000000000000000000604482015290519081900360640190fd5b61606a84846160f6565b905082848161607557fe5b0761607f57610f6e565b600061608b85856161c6565b9050600083600181111561609b57fe5b1480156160a55750805b156160b8576160b5826001615c5b565b91505b60018360018111156160c657fe5b1480156160d1575080155b156160e157611616826001615cc0565b509392505050565b6000615299600083615cc0565b60008161614a576040805162461bcd60e51b815260206004820181905260248201527f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f604482015290519081900360640190fd5b8160001914801561615e5750600160ff1b83145b1561619a5760405162461bcd60e51b81526004018080602001828103825260218152602001806172f96021913960400191505060405180910390fd5b60008284816161a557fe5b05949350505050565b60009081526001919091016020526040902054151590565b60008215806161d3575081155b156161e057506001615299565b501860ff1d1590565b604051806105400160405280602a906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b6040518060a001604052806005906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b604080516101e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915290565b604051806040016040528060008152602001600081525090565b80356001600160a01b03811681146152f257600080fd5b80610120810183101561529957600080fd5b803563ffffffff811681146152f257600080fd5b600060208284031215616346578081fd5b610f6e826162f8565b60008060008060008060a08789031215616367578182fd5b616370876162f8565b955061637e602088016162f8565b945060408701359350616393606088016162f8565b9250608087013567ffffffffffffffff808211156163af578384fd5b818901915089601f8301126163c2578384fd5b8135818111156163d0578485fd5b8a60208285010111156163e1578485fd5b6020830194508093505050509295509295509295565b60008060008060006104a0868803121561640f578081fd5b616418866162f8565b9450616427876020880161630f565b935061643787610140880161630f565b925061644787610260880161630f565b915061645787610380880161630f565b90509295509295909350565b600060808284031215616474578081fd5b82608083011115616483578081fd5b50919050565b60006020828403121561649a578081fd5b81518015158114610f6e578182fd5b600080604083850312156164bb578182fd5b823567ffffffffffffffff808211156164d2578384fd5b818501915085601f8301126164e5578384fd5b8135818111156164f157fe5b6040516020601f8301601f191682018101848111838210171561651057fe5b6040528282528483018101891015616526578687fd5b8281860182840137918101820195909552939694909301359450505050565b600060208284031215616556578081fd5b5035919050565b60006020828403121561656e578081fd5b5051919050565b60008060408385031215616587578182fd5b50508035926020909101359150565b600080604083850312156165a8578182fd5b823591506165b8602084016162f8565b90509250929050565b60008060008060008060c087890312156165d9578182fd5b863595506165e9602088016162f8565b94506165f7604088016162f8565b9350606087013592506080870135915060a087013590509295509295509295565b60008060006060848603121561662c578081fd5b8335925061663c602085016162f8565b9150604084013590509250925092565b600080600080600060a08688031215616663578283fd5b85359450616673602087016162f8565b935060408601359250616688606087016162f8565b915061645760808701616321565b600080600080600080600060e0888a0312156166b0578485fd5b873596506166c0602089016162f8565b95506040880135945060608801359350608088013592506166e360a089016162f8565b91506166f160c08901616321565b905092959891949750929550565b6000806101408385031215616712578182fd5b823591506165b8846020850161630f565b6000806000806103808587031215616739578182fd5b8435935061674a866020870161630f565b925061675a86610140870161630f565b915061676a86610260870161630f565b905092959194509250565b600080600060608486031215616789578081fd5b505081359360208301359350604090920135919050565b6001600160a01b0316815260200190565b6001600160a01b03169052565b8060005b60098110156115a25781358452602093840193909101906001016167c2565b8060005b60068110156115a25781518452602093840193909101906001016167e5565b61680f8282516167b1565b602081015161682160208401826167b1565b50604081015161683460408401826167b1565b50606081015161684760608401826167b1565b50608081015161685a60808401826167b1565b5060a081015160a083015260c081015160c083015260e081015160e0830152610100808201518184015250610120808201518184015250610140808201516168a4828501826168fe565b5050610160808201516168b9828501826168f4565b5050610180808201516168ce828501826168f4565b50506101a0808201516168e3828501826168f4565b50506101c0808201516115a2828501825b63ffffffff169052565b67ffffffffffffffff169052565b93845260208401929092527fff0000000000000000000000000000000000000000000000000000000000000060f891821b8116604085015291901b16604182015260420190565b6001600160a01b0391909116815260200190565b6020808252825182820181905260009190848201906040850190845b818110156169a85783516001600160a01b031683529284019291840191600101616983565b50909695505050505050565b6000610280820190508615158252602086151581840152604083018660005b60078110156169f5576169e78383516167a0565b9250908301906001016169d3565b50505061012083018560005b6005811015616a1e57815183529183019190830190600101616a01565b505050506110dd6101c08301846167e1565b610580810160058510616a3f57fe5b84825260206001600160a01b03851681840152604083018460005b602a811015616a7757815183529183019190830190600101616a5a565b50505050949350505050565b90815260200190565b9182521515602082015260400190565b918252602082015260400190565b9283526020830191909152604082015260600190565b9889526020890197909752604088019590955260608701939093526080860191909152151560a0850152151560c0840152151560e08301526101008201526101200190565b60208082526013908201527f696e76616c6964206c696d697420707269636500000000000000000000000000604082015260600190565b60208082526014908201527f63616e6e6f74206c697175696461746520414d4d000000000000000000000000604082015260600190565b6020808252601e908201527f7461726765744c65766572616765206d75737420626520696e74656765720000604082015260600190565b6020808252600b908201527f62726f6b656e2064617461000000000000000000000000000000000000000000604082015260600190565b6020808252600e908201527f696e76616c696420616d6f756e74000000000000000000000000000000000000604082015260600190565b60208082526023908201527f70657270657475616c2073686f756c6420626520696e204e4f524d414c20737460408201527f6174650000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252600f908201527f616c72656164792072756e6e696e670000000000000000000000000000000000604082015260600190565b6020808252601a908201527f7461726765744c65766572616765206973206e65676174697665000000000000604082015260600190565b6020808252600d908201527f696e76616c696420666c61677300000000000000000000000000000000000000604082015260600190565b60208082526026908201527f70657270657475616c2073686f756c6420626520696e20454d455247454e435960408201527f2073746174650000000000000000000000000000000000000000000000000000606082015260800190565b60208082526011908201527f646561646c696e65206578636565646564000000000000000000000000000000604082015260600190565b6020808252601e908201527f636f6e747261637420646f6573206e6f74206163636570742065746865720000604082015260600190565b60208082526013908201527f706f6f6c206973206e6f742072756e6e696e6700000000000000000000000000604082015260600190565b6020808252601d908201527f616c6c2070657270657475616c206d75737420626520636c6561726564000000604082015260600190565b60208082526022908201527f6f6e6c7920676f7665726e6f722063616e206372656174652070657270657475604082015261185b60f21b606082015260800190565b60208082526022908201527f6f6e6c79206f70657261746f722063616e206372656174652070657270657475604082015261185b60f21b606082015260800190565b60208082526015908201527f63616c6c6572206d757374206265206b65657065720000000000000000000000604082015260600190565b6020808252600e908201527f696e76616c696420747261646572000000000000000000000000000000000000604082015260600190565b60208082526024908201527f70657270657475616c2073686f756c6420626520696e20434c4541524544207360408201527f7461746500000000000000000000000000000000000000000000000000000000606082015260800190565b60008982526001600160a01b03808a1660208401528089166040840152876060840152808716608084015280861660a08401525060e060c08301528260e0830152610100838582850137828401810191909152601f909201601f19160101979650505050505050565b9283526001600160a01b03919091166020830152604082015260600190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b8681526001600160a01b03861660208201526104c0810161707260408301876167be565b6170806101608301866167be565b61708e6102808301856167be565b6157a06103a08301846167be565b8281526102008101610f6e6020830184616804565b600061022085835260206170c781850187616804565b81610200850152845180838601528392505b808310156170f8578583018201518584016102400152918101916170d9565b8083111561710a578361024082870101525b601f01601f191693909301610240019695505050505050565b83815261022081016171386020830185616804565b82610200830152949350505050565b92835260208301919091526001600160a01b0316604082015260600190565b93845260208401929092526001600160a01b03908116604084015216606082015260800190565b95865260208601949094526001600160a01b03928316604086015291166060840152608083015260a082015260c00190565b93845260208401929092526001600160a01b03166040830152606082015260800190565b96875260208701959095526001600160a01b039384166040870152606086019290925260808501521660a083015263ffffffff1660c082015260e0019056fe63616e206f6e6c7920626520696e69746961746564206279206f70657261746f72456e756d657261626c655365743a20696e646578206f7574206f6620626f756e64735369676e6564536166654d6174683a206164646974696f6e206f766572666c6f7770657270657475616c2073686f756c6420626520696e204e4f524d414c20737461746563616e206f6e6c7920626520696e6974696174656420627920676f7665726e6f72496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65645369676e6564536166654d6174683a206469766973696f6e206f766572666c6f775369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7753616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f77a2646970667358221220a6f3f86a018a8e08362ff80974f6b184d56ee1dcb5a1c82ec38cf113c8cdf1cf64736f6c63430007040033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.