FTM Price: $0.68 (-7.79%)
Gas: 20 GWei

Contract

0xE6F4e843E15Ca0a96DB77FAA31EC5e5E297Cd8C3
 

Overview

FTM Balance

Fantom LogoFantom LogoFantom Logo0 FTM

FTM Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
Value
0x60806040670938042023-08-17 13:38:06242 days ago1692279486IN
 Create: LiquidityPoolHop2
0 FTM3.46665085654.42067446

Latest 1 internal transaction

Parent Txn Hash Block From To Value
670938042023-08-17 13:38:06242 days ago1692279486  Contract Creation0 FTM
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LiquidityPoolHop2

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 25 : LiquidityPoolHop2.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "./Storage.sol";
import "./Liquidity.sol";
import "./Getter.sol";
import "./Admin.sol";

contract LiquidityPoolHop2 is Storage, Liquidity, Admin {}

File 1 of 25 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

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

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 1 of 25 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^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 proxied contracts do not make use of 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 {ERC1967Proxy-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.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
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() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

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

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 1 of 25 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        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 1 of 25 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.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 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'
        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) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 1 of 25 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 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");

        (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");

        (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");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 1 of 25 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/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 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 onlyInitializing {
    }

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 1 of 25 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
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) {
        unchecked {
            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) {
        unchecked {
            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) {
        unchecked {
            // 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) {
        unchecked {
            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) {
        unchecked {
            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) {
        return a + b;
    }

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

    /**
     * @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.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        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) {
        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) {
        unchecked {
            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.
     *
     * 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) {
        unchecked {
            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) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 1 of 25 : SafeOwnableUpgradeable.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract SafeOwnableUpgradeable is OwnableUpgradeable {
    address internal _pendingOwner;

    event PrepareToTransferOwnership(address indexed pendingOwner);

    function __SafeOwnable_init() internal onlyInitializing {
        __Ownable_init();
    }

    function transferOwnership(address newOwner) public virtual override onlyOwner {
        require(newOwner != address(0), "O=0"); // Owner Is Zero
        require(newOwner != owner(), "O=O"); // Owner is the same as the old Owner
        _pendingOwner = newOwner;
        emit PrepareToTransferOwnership(_pendingOwner);
    }

    function takeOwnership() public virtual {
        require(_msgSender() == _pendingOwner, "SND"); // SeNDer is not authorized
        _transferOwnership(_pendingOwner);
        _pendingOwner = address(0);
    }

    function renounceOwnership() public virtual override onlyOwner {
        _pendingOwner = address(0);
        _transferOwnership(address(0));
    }
}

File 1 of 25 : Account.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

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

import "../libraries/LibSubAccount.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibAsset.sol";
import "../libraries/LibReferenceOracle.sol";
import "./Storage.sol";

contract Account is Storage {
    using LibMath for uint256;
    using LibSubAccount for bytes32;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;
    using LibAsset for Asset;

    event DepositCollateral(
        bytes32 indexed subAccountId,
        address indexed trader,
        uint8 collateralId,
        uint256 rawAmount,
        uint96 wadAmount
    );
    event WithdrawCollateral(
        bytes32 indexed subAccountId,
        address indexed trader,
        uint8 collateralId,
        uint256 rawAmount,
        uint96 wadAmount
    );

    function depositCollateral(
        bytes32 subAccountId,
        uint256 rawAmount // NOTE: OrderBook SHOULD transfer rawAmount collateral to LiquidityPool
    ) external onlyOrderBook {
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        require(decoded.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(decoded.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(decoded.assetId), "LST"); // the asset is not LiSTed
        require(rawAmount != 0, "A=0"); // Amount Is Zero

        SubAccount storage subAccount = _storage.accounts[subAccountId];
        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        uint96 wadAmount = collateral.toWad(rawAmount);
        subAccount.collateral += wadAmount;

        emit DepositCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function withdrawCollateral(
        bytes32 subAccountId,
        uint256 rawAmount,
        uint96 collateralPrice,
        uint96 assetPrice
    ) external onlyOrderBook {
        require(rawAmount != 0, "A=0"); // Amount Is Zero
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        require(decoded.account != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(decoded.collateralId), "LST"); // the asset is not LiSTed
        require(_hasAsset(decoded.assetId), "LST"); // the asset is not LiSTed

        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        assetPrice = LibReferenceOracle.checkPrice(_storage, asset, assetPrice);
        collateralPrice = LibReferenceOracle.checkPrice(_storage, collateral, collateralPrice);

        // fee & funding
        uint96 feeUsd = _getFundingFeeUsd(subAccount, asset, decoded.isLong, assetPrice);
        if (subAccount.size > 0) {
            _updateEntryFunding(subAccount, asset, decoded.isLong);
        }
        {
            uint96 feeCollateral = uint256(feeUsd).wdiv(collateralPrice).safeUint96();
            require(subAccount.collateral >= feeCollateral, "FEE"); // remaining collateral can not pay FEE
            subAccount.collateral -= feeCollateral;
            collateral.collectedFee += feeCollateral;
            collateral.spotLiquidity += feeCollateral;
            emit CollectedFee(decoded.collateralId, feeCollateral);
        }
        // withdraw
        uint96 wadAmount = collateral.toWad(rawAmount);
        require(subAccount.collateral >= wadAmount, "C<W"); // Collateral can not pay fee or is less than the amount requested for Withdrawal
        subAccount.collateral = subAccount.collateral - wadAmount;
        collateral.transferOut(decoded.account, rawAmount, _storage.weth, _storage.nativeUnwrapper);
        require(_isAccountImSafe(subAccount, decoded.assetId, decoded.isLong, collateralPrice, assetPrice), "!IM");

        emit WithdrawCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function withdrawAllCollateral(bytes32 subAccountId) external onlyOrderBook {
        LibSubAccount.DecodedSubAccountId memory decoded = subAccountId.decodeSubAccountId();
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        require(subAccount.size == 0, "S>0"); // position Size should be Zero
        require(subAccount.collateral > 0, "C=0"); // Collateral Is Zero

        Asset storage asset = _storage.assets[decoded.assetId];
        Asset storage collateral = _storage.assets[decoded.collateralId];
        require(asset.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(collateral.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        uint96 wadAmount = subAccount.collateral;
        uint256 rawAmount = collateral.toRaw(wadAmount);
        subAccount.collateral = 0;
        collateral.transferOut(decoded.account, rawAmount, _storage.weth, _storage.nativeUnwrapper);
        emit WithdrawCollateral(subAccountId, decoded.account, decoded.collateralId, rawAmount, wadAmount);
        _updateSequence();
    }

    function _positionPnlUsd(
        Asset storage asset,
        SubAccount storage subAccount,
        bool isLong,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (bool hasProfit, uint96 pnlUsd) {
        if (amount == 0) {
            return (false, 0);
        }
        require(assetPrice > 0, "P=0"); // Price Is Zero
        hasProfit = isLong ? assetPrice > subAccount.entryPrice : assetPrice < subAccount.entryPrice;
        uint96 priceDelta = assetPrice >= subAccount.entryPrice
            ? assetPrice - subAccount.entryPrice
            : subAccount.entryPrice - assetPrice;
        if (
            hasProfit &&
            _blockTimestamp() < subAccount.lastIncreasedTime + asset.minProfitTime &&
            priceDelta < uint256(subAccount.entryPrice).rmul(asset.minProfitRate).safeUint96()
        ) {
            hasProfit = false;
            return (false, 0);
        }
        pnlUsd = uint256(priceDelta).wmul(amount).safeUint96();
    }

    // NOTE: settle funding by modify subAccount.collateral before this function
    function _isAccountImSafe(
        SubAccount storage subAccount,
        uint32 assetId,
        bool isLong,
        uint96 collateralPrice,
        uint96 assetPrice
    ) internal view returns (bool) {
        Asset storage asset = _storage.assets[assetId];
        (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, isLong, subAccount.size, assetPrice);
        return _isAccountSafe(subAccount, collateralPrice, assetPrice, asset.initialMarginRate, hasProfit, pnlUsd, 0);
    }

    // NOTE: settle funding by modify subAccount.collateral before this function
    function _isAccountMmSafe(
        SubAccount storage subAccount,
        uint32 assetId,
        bool isLong,
        uint96 collateralPrice,
        uint96 assetPrice
    ) internal view returns (bool) {
        Asset storage asset = _storage.assets[assetId];
        (bool hasProfit, uint96 pnlUsd) = _positionPnlUsd(asset, subAccount, isLong, subAccount.size, assetPrice);
        return
            _isAccountSafe(subAccount, collateralPrice, assetPrice, asset.maintenanceMarginRate, hasProfit, pnlUsd, 0);
    }

    function _isAccountSafe(
        SubAccount storage subAccount,
        uint96 collateralPrice,
        uint96 assetPrice,
        uint32 marginRate,
        bool hasProfit,
        uint96 pnlUsd,
        uint96 fundingFee // fundingFee = 0 if subAccount.collateral was modified
    ) internal view returns (bool) {
        uint256 thresholdUsd = (uint256(subAccount.size) * uint256(assetPrice) * uint256(marginRate)) / 1e18 / 1e5;
        thresholdUsd += fundingFee;
        uint256 collateralUsd = uint256(subAccount.collateral).wmul(collateralPrice);
        // break down "collateralUsd +/- pnlUsd >= thresholdUsd >= 0"
        if (hasProfit) {
            return collateralUsd + pnlUsd >= thresholdUsd;
        } else {
            return collateralUsd >= thresholdUsd + pnlUsd;
        }
    }

    function _getFeeUsd(
        SubAccount storage subAccount,
        Asset storage asset,
        bool isLong,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (uint96) {
        return _getFundingFeeUsd(subAccount, asset, isLong, assetPrice) + _getPositionFeeUsd(asset, amount, assetPrice);
    }

    function _getFundingFeeUsd(
        SubAccount storage subAccount,
        Asset storage asset,
        bool isLong,
        uint96 assetPrice
    ) internal view returns (uint96) {
        if (subAccount.size == 0) {
            return 0;
        }
        uint256 cumulativeFunding;
        if (isLong) {
            cumulativeFunding = asset.longCumulativeFundingRate - subAccount.entryFunding;
            cumulativeFunding = cumulativeFunding.wmul(assetPrice);
        } else {
            cumulativeFunding = asset.shortCumulativeFunding - subAccount.entryFunding;
        }
        return cumulativeFunding.wmul(subAccount.size).safeUint96();
    }

    function _getPositionFeeUsd(Asset storage asset, uint96 amount, uint96 assetPrice) internal view returns (uint96) {
        uint256 feeUsd = ((uint256(assetPrice) * uint256(asset.positionFeeRate)) * uint256(amount)) / 1e5 / 1e18;
        return feeUsd.safeUint96();
    }

    function _getLiquidationFeeUsd(
        Asset storage asset,
        uint96 amount,
        uint96 assetPrice
    ) internal view returns (uint96) {
        uint256 feeUsd = ((uint256(assetPrice) * uint256(asset.liquidationFeeRate)) * uint256(amount)) / 1e5 / 1e18;
        return feeUsd.safeUint96();
    }

    // note: you can skip this function if newPositionSize > 0
    function _updateEntryFunding(SubAccount storage subAccount, Asset storage asset, bool isLong) internal {
        if (isLong) {
            subAccount.entryFunding = asset.longCumulativeFundingRate;
        } else {
            subAccount.entryFunding = asset.shortCumulativeFunding;
        }
    }
}

File 1 of 25 : Admin.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

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

import "../interfaces/IVotesUpgradeable.sol";

import "./Storage.sol";
import "../libraries/LibAsset.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibReferenceOracle.sol";
import "../core/Types.sol";

contract Admin is Storage {
    using LibAsset for Asset;
    using LibMath for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    address constant ARBITRUM_TOKEN = 0x912CE59144191C1204E64559FE8253a0e49E6548;

    function setMaintainer(address newMaintainer) external onlyOwner {
        require(_storage.maintainer != newMaintainer, "CHG"); // not CHanGed
        _storage.maintainer = newMaintainer;
        emit SetMaintainer(newMaintainer);
    }

    function setLiquidityManager(address newLiquidityManager, bool isAdd) external onlyOwner {
        _storage.liquidityManager[newLiquidityManager] = isAdd;
        emit SetLiquidityManager(newLiquidityManager, isAdd);
    }

    function delegateVoting(address to) external onlyMaintainer {
        IVotesUpgradeable(ARBITRUM_TOKEN).delegate(to);
    }

    function addAsset(
        uint8 assetId,
        bytes32 symbol,
        uint8 decimals,
        bool isStable,
        address tokenAddress,
        address muxTokenAddress
    ) external onlyOwner {
        require(decimals <= 18, "DCM"); // invalid DeCiMals
        require(assetId == _storage.assets.length, "AID"); // invalid AssetID
        require(assetId < 0xFF, "FLL"); // assets list is FuLL
        require(symbol != "", "SYM"); // invalid SYMbol

        _storage.assets.push();
        Asset storage asset = _storage.assets[assetId];
        asset.symbol = symbol;
        asset.id = assetId;
        asset.decimals = decimals;
        asset.flags = (asset.flags & (~ASSET_IS_STABLE)) | (isStable ? ASSET_IS_STABLE : 0);
        asset.tokenAddress = tokenAddress;
        asset.muxTokenAddress = muxTokenAddress;
        emit AddAsset(assetId, symbol, decimals, isStable, tokenAddress, muxTokenAddress);
        _updateSequence();
    }

    function setAssetParams(
        uint8 assetId,
        bytes32 symbol,
        uint32 newInitialMarginRate, // 1e5
        uint32 newMaintenanceMarginRate, // 1e5
        uint32 newPositionFeeRate, // 1e5
        uint32 newLiquidationFeeRate, // 1e5
        uint32 newMinProfitRate, // 1e5
        uint32 newMinProfitTime, // 1e0
        uint32 newSpotWeight
    ) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        require(symbol != "", "SYM"); // invalid SYMbol
        Asset storage asset = _storage.assets[assetId];
        asset.symbol = symbol;
        asset.initialMarginRate = newInitialMarginRate;
        asset.maintenanceMarginRate = newMaintenanceMarginRate;
        asset.positionFeeRate = newPositionFeeRate;
        asset.liquidationFeeRate = newLiquidationFeeRate;
        asset.minProfitRate = newMinProfitRate;
        asset.minProfitTime = newMinProfitTime;
        asset.spotWeight = newSpotWeight;
        emit SetAssetParams(
            assetId,
            symbol,
            newInitialMarginRate,
            newMaintenanceMarginRate,
            newPositionFeeRate,
            newLiquidationFeeRate,
            newMinProfitRate,
            newMinProfitTime,
            newSpotWeight
        );
        _updateSequence();
    }

    function setAssetFlags(
        uint8 assetId,
        bool isTradable,
        bool isOpenable,
        bool isShortable,
        bool useStableTokenForProfit,
        bool isEnabled,
        bool isStrictStable,
        bool canAddRemoveLiquidity,
        uint32 newHalfSpread,
        uint96 newMaxLongPositionSize,
        uint96 newMaxShortPositionSize
    ) external onlyMaintainer {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        if (!asset.isStable()) {
            require(!isStrictStable, "STB"); // the asset is impossible to be a strict STaBle coin
        }
        uint56 newFlags = asset.flags;
        newFlags = (newFlags & (~ASSET_IS_TRADABLE)) | (isTradable ? ASSET_IS_TRADABLE : 0);
        newFlags = (newFlags & (~ASSET_IS_OPENABLE)) | (isOpenable ? ASSET_IS_OPENABLE : 0);
        newFlags = (newFlags & (~ASSET_IS_SHORTABLE)) | (isShortable ? ASSET_IS_SHORTABLE : 0);
        newFlags =
            (newFlags & (~ASSET_USE_STABLE_TOKEN_FOR_PROFIT)) |
            (useStableTokenForProfit ? ASSET_USE_STABLE_TOKEN_FOR_PROFIT : 0);
        newFlags = (newFlags & (~ASSET_IS_ENABLED)) | (isEnabled ? ASSET_IS_ENABLED : 0);
        newFlags = (newFlags & (~ASSET_IS_STRICT_STABLE)) | (isStrictStable ? ASSET_IS_STRICT_STABLE : 0);
        newFlags =
            (newFlags & (~ASSET_CAN_ADD_REMOVE_LIQUIDITY)) |
            (canAddRemoveLiquidity ? ASSET_CAN_ADD_REMOVE_LIQUIDITY : 0);
        asset.flags = newFlags;
        asset.halfSpread = newHalfSpread;
        asset.maxLongPositionSize = newMaxLongPositionSize;
        asset.maxShortPositionSize = newMaxShortPositionSize;
        emit SetAssetFlags(assetId, newFlags, newHalfSpread, newMaxLongPositionSize, newMaxShortPositionSize);
        _updateSequence();
    }

    function setFundingParams(uint8 assetId, uint32 newBaseRate8H, uint32 newLimitRate8H) external onlyOwner {
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        if (_storage.assets[assetId].isStable()) {
            _storage.shortFundingBaseRate8H = newBaseRate8H;
            _storage.shortFundingLimitRate8H = newLimitRate8H;
        } else {
            Asset storage asset = _storage.assets[assetId];
            asset.longFundingBaseRate8H = newBaseRate8H;
            asset.longFundingLimitRate8H = newLimitRate8H;
        }
        emit SetFundingParams(assetId, newBaseRate8H, newLimitRate8H);
        _updateSequence();
    }

    function setReferenceOracle(
        uint8 assetId,
        ReferenceOracleType referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation // 1e5
    ) external onlyOwner {
        LibReferenceOracle.checkParameters(referenceOracleType, referenceOracle, referenceDeviation);
        require(_hasAsset(assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        asset.referenceOracleType = uint8(referenceOracleType);
        asset.referenceOracle = referenceOracle;
        asset.referenceDeviation = referenceDeviation;
        emit SetReferenceOracle(assetId, uint8(referenceOracleType), referenceOracle, referenceDeviation);
        _updateSequence();
    }

    function setEmergencyNumbers(uint96 newMlpPriceLowerBound, uint96 newMlpPriceUpperBound) external onlyMaintainer {
        if (
            _storage.mlpPriceLowerBound != newMlpPriceLowerBound || _storage.mlpPriceUpperBound != newMlpPriceUpperBound
        ) {
            _storage.mlpPriceLowerBound = newMlpPriceLowerBound;
            _storage.mlpPriceUpperBound = newMlpPriceUpperBound;
            emit SetMlpPriceRange(newMlpPriceLowerBound, newMlpPriceUpperBound);
        }
        _updateSequence();
    }

    function setNumbers(
        uint32 newFundingInterval,
        uint32 newLiquidityBaseFeeRate, // 1e5
        uint32 newLiquidityDynamicFeeRate, // 1e5
        uint32 newStrictStableDeviation, // 1e5
        uint96 newBrokerGasRebate
    ) external onlyOwner {
        require(newLiquidityBaseFeeRate < 1e5, "F>1"); // %fee > 100%
        require(newLiquidityDynamicFeeRate < 1e5, "F>1"); // %fee > 100%
        require(newStrictStableDeviation < 1e5, "D>1"); // %deviation > 100%
        if (_storage.fundingInterval != newFundingInterval) {
            emit SetFundingInterval(_storage.fundingInterval, newFundingInterval);
            _storage.fundingInterval = newFundingInterval;
        }
        if (
            _storage.liquidityBaseFeeRate != newLiquidityBaseFeeRate ||
            _storage.liquidityDynamicFeeRate != newLiquidityDynamicFeeRate
        ) {
            _storage.liquidityBaseFeeRate = newLiquidityBaseFeeRate;
            _storage.liquidityDynamicFeeRate = newLiquidityDynamicFeeRate;
            emit SetLiquidityFee(newLiquidityBaseFeeRate, newLiquidityDynamicFeeRate);
        }
        if (_storage.strictStableDeviation != newStrictStableDeviation) {
            _storage.strictStableDeviation = newStrictStableDeviation;
            emit SetStrictStableDeviation(newStrictStableDeviation);
        }
        if (_storage.brokerGasRebate != newBrokerGasRebate) {
            _storage.brokerGasRebate = newBrokerGasRebate;
            emit SetBrokerGasRebate(newBrokerGasRebate);
        }
        _updateSequence();
    }

    function transferLiquidityOut(uint8[] memory assetIds, uint256[] memory rawAmounts) external onlyLiquidityManager {
        uint256 length = assetIds.length;
        require(length > 0, "MTY"); // argument array is eMpTY
        require(assetIds.length == rawAmounts.length, "LEN"); // LENgth of 2 arguments does not match
        for (uint256 i = 0; i < length; i++) {
            Asset storage asset = _storage.assets[assetIds[i]];
            IERC20Upgradeable(asset.tokenAddress).safeTransfer(msg.sender, rawAmounts[i]);
            uint96 wadAmount = asset.toWad(rawAmounts[i]);
            require(asset.spotLiquidity >= wadAmount, "NLT"); // not enough liquidity
            asset.spotLiquidity -= wadAmount;
            emit TransferLiquidity(address(this), msg.sender, assetIds[i], rawAmounts[i]);
        }
        _updateSequence();
    }

    // NOTE: LiquidityManager SHOULD transfer rawAmount collateral to LiquidityPool
    function transferLiquidityIn(uint8[] memory assetIds, uint256[] memory rawAmounts) external onlyLiquidityManager {
        uint256 length = assetIds.length;
        require(length > 0, "MTY"); // argument array is eMpTY
        require(assetIds.length == rawAmounts.length, "LEN"); // LENgth of 2 arguments does not match
        for (uint256 i = 0; i < length; i++) {
            Asset storage asset = _storage.assets[assetIds[i]];
            asset.spotLiquidity += asset.toWad(rawAmounts[i]);
            emit TransferLiquidity(msg.sender, address(this), assetIds[i], rawAmounts[i]);
        }
        _updateSequence();
    }

    function borrowAsset(
        address borrower,
        uint8 assetId,
        uint256 rawBorrowAmount, // token.decimals
        uint256 rawFee // token.decimals
    ) external onlyLiquidityManager returns (uint256) {
        Asset storage collateral = _storage.assets[assetId];
        uint96 wadBorrowed = collateral.toWad(rawBorrowAmount);
        uint96 wadFee = collateral.toWad(rawFee);
        uint256 rawTransferOut = rawBorrowAmount - rawFee;
        uint96 wadTransferOut = wadBorrowed - wadFee;
        require(collateral.spotLiquidity >= wadTransferOut, "LIQ"); // insufficient LIQuidity
        collateral.collectedFee += wadFee;
        collateral.spotLiquidity -= wadTransferOut;
        collateral.credit += wadBorrowed;
        emit CollectedFee(assetId, wadFee);
        IERC20Upgradeable(collateral.tokenAddress).safeTransfer(borrower, rawTransferOut);
        emit BorrowAsset(assetId, msg.sender, borrower, rawBorrowAmount, rawFee);
        return rawTransferOut;
    }

    // NOTE: LiquidityManager SHOULD transfer rawRepayAmount + rawFee collateral to LiquidityPool
    function repayAsset(
        address repayer,
        uint8 assetId,
        uint256 rawRepayAmount, // token.decimals
        uint256 rawFee, // token.decimals
        uint256 rawBadDebt // debt amount that cannot be recovered
    ) external onlyLiquidityManager {
        Asset storage collateral = _storage.assets[assetId];
        uint96 wadRepay = collateral.toWad(rawRepayAmount);
        uint96 wadFee = collateral.toWad(rawFee);
        uint96 wadBadDebt = collateral.toWad(rawBadDebt);
        uint96 wadTransferIn = wadRepay + wadFee;
        collateral.collectedFee += wadFee;
        collateral.spotLiquidity += wadTransferIn;
        collateral.credit -= wadRepay + wadBadDebt;
        emit CollectedFee(assetId, wadFee);
        emit RepayAsset(assetId, msg.sender, repayer, rawRepayAmount, rawFee, rawBadDebt);
    }
}

File 1 of 25 : Events.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

contract Events {
    event UpdateSequence(uint32 sequence);

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   trade
    struct OpenPositionArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 newEntryPrice;
        uint96 feeUsd;
        uint96 remainPosition;
        uint96 remainCollateral;
    }
    event OpenPosition(address indexed trader, uint8 indexed assetId, OpenPositionArgs args);
    struct ClosePositionArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 feeUsd;
        bool hasProfit;
        uint96 pnlUsd;
        uint96 remainPosition;
        uint96 remainCollateral;
    }
    event ClosePosition(address indexed trader, uint8 indexed assetId, ClosePositionArgs args);
    struct LiquidateArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint96 amount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 feeUsd;
        bool hasProfit;
        uint96 pnlUsd;
        uint96 remainCollateral;
    }
    event Liquidate(address indexed trader, uint8 indexed assetId, LiquidateArgs args);
    struct WithdrawProfitArgs {
        bytes32 subAccountId;
        uint8 collateralId;
        uint8 profitAssetId;
        bool isLong;
        uint256 withdrawRawAmount;
        uint96 assetPrice;
        uint96 collateralPrice;
        uint96 profitAssetPrice;
        uint96 entryPrice;
        uint96 feeUsd;
    }
    event WithdrawProfit(address indexed trader, uint8 indexed assetId, WithdrawProfitArgs args);
    event CollectedFee(uint8 tokenId, uint96 fee);
    event ClaimBrokerGasRebate(address indexed receiver, uint32 transactions, uint256 rawAmount);

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   liquidity
    event AddLiquidity(
        address indexed trader,
        uint8 indexed tokenId,
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 mlpAmount,
        uint96 fee
    );
    event RemoveLiquidity(
        address indexed trader,
        uint8 indexed tokenId,
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 mlpAmount,
        uint96 fee
    );
    event UpdateFundingRate(
        uint8 indexed tokenId,
        uint32 longFundingRate, // 1e5
        uint128 longCumulativeFundingRate, // Σ_t fundingRate_t
        uint32 shortFundingRate, // 1e5
        uint128 shortCumulativeFunding // Σ_t fundingRate_t * indexPrice_t
    );
    event IssueMuxToken(
        uint8 indexed tokenId, // if isStable, tokenId will always be 0
        bool isStable,
        uint96 muxTokenAmount
    );
    event RedeemMuxToken(address trader, uint8 tokenId, uint96 muxTokenAmount);
    event Rebalance(
        address indexed rebalancer,
        uint8 tokenId0,
        uint8 tokenId1,
        uint96 price0,
        uint96 price1,
        uint96 rawAmount0,
        uint96 rawAmount1
    );

    //////////////////////////////////////////////////////////////////////////////////////
    //                                   admin
    event AddAsset(
        uint8 indexed id,
        bytes32 symbol,
        uint8 decimals,
        bool isStable,
        address tokenAddress,
        address muxTokenAddress
    );
    event SetAssetParams(
        uint8 indexed assetId,
        bytes32 symbol,
        uint32 newInitialMarginRate,
        uint32 newMaintenanceMarginRate,
        uint32 newPositionFeeRate,
        uint32 newLiquidationFeeRate,
        uint32 newMinProfitRate,
        uint32 newMinProfitTime,
        uint32 newSpotWeight
    );
    event SetAssetFlags(
        uint8 indexed assetId,
        uint56 newFlags,
        uint32 newHalfSpread,
        uint96 newMaxLongPositionSize,
        uint96 newMaxShortPositionSize
    );
    event SetReferenceOracle(
        uint8 indexed assetId,
        uint8 referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation
    );
    event SetFundingParams(uint8 indexed assetId, uint32 newBaseRate8H, uint32 newLimitRate8H);
    event SetFundingInterval(uint32 oldFundingInterval, uint32 newFundingInterval);
    event SetMlpPriceRange(uint96 newLowerBound, uint96 newUpperBound);
    event SetLiquidityFee(uint32 newLiquidityBaseFeeRate, uint32 newLiquidityDynamicFeeRate);
    event SetStrictStableDeviation(uint32 newStrictStableDeviation);
    event SetBrokerGasRebate(uint96 newBrokerGasRebate);
    event SetMaintainer(address indexed newMaintainer);
    event SetLiquidityManager(address indexed newLiquidityManager, bool isAdd);
    event WithdrawCollectedFee(uint8 indexed assetId, uint96 collectedFee);
    event TransferLiquidity(address indexed sender, address indexed recipient, uint8 assetId, uint256 amount);
    event BorrowAsset(
        uint8 indexed assetId,
        address indexed project,
        address indexed borrower,
        uint256 rawBorrowAmount,
        uint256 rawFee
    );
    event RepayAsset(
        uint8 indexed assetId,
        address indexed project,
        address indexed repayer,
        uint256 rawRepayAmount,
        uint256 rawFee,
        uint256 badDebt
    );
}

File 1 of 25 : Getter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../libraries/LibSubAccount.sol";
import "./Storage.sol";

contract Getter is Storage {
    using LibSubAccount for bytes32;

    function getAssetInfo(uint8 assetId) external view returns (Asset memory) {
        require(assetId < _storage.assets.length, "LST"); // the asset is not LiSTed
        return _storage.assets[assetId];
    }

    function getAllAssetInfo() external view returns (Asset[] memory) {
        return _storage.assets;
    }

    function getAssetAddress(uint8 assetId) external view returns (address) {
        require(assetId < _storage.assets.length, "LST"); // the asset is not LiSTed
        return _storage.assets[assetId].tokenAddress;
    }

    function getLiquidityPoolStorage()
        external
        view
        returns (
            // [0] shortFundingBaseRate8H
            // [1] shortFundingLimitRate8H
            // [2] lastFundingTime
            // [3] fundingInterval
            // [4] liquidityBaseFeeRate
            // [5] liquidityDynamicFeeRate
            // [6] sequence. note: will be 0 after 0xffffffff
            // [7] strictStableDeviation
            uint32[8] memory u32s,
            // [0] mlpPriceLowerBound
            // [1] mlpPriceUpperBound
            uint96[2] memory u96s
        )
    {
        u32s[0] = _storage.shortFundingBaseRate8H;
        u32s[1] = _storage.shortFundingLimitRate8H;
        u32s[2] = _storage.lastFundingTime;
        u32s[3] = _storage.fundingInterval;
        u32s[4] = _storage.liquidityBaseFeeRate;
        u32s[5] = _storage.liquidityDynamicFeeRate;
        u32s[6] = _storage.sequence;
        u32s[7] = _storage.strictStableDeviation;
        u96s[0] = _storage.mlpPriceLowerBound;
        u96s[1] = _storage.mlpPriceUpperBound;
    }

    function getSubAccount(
        bytes32 subAccountId
    )
        external
        view
        returns (uint96 collateral, uint96 size, uint32 lastIncreasedTime, uint96 entryPrice, uint128 entryFunding)
    {
        SubAccount storage subAccount = _storage.accounts[subAccountId];
        collateral = subAccount.collateral;
        size = subAccount.size;
        lastIncreasedTime = subAccount.lastIncreasedTime;
        entryPrice = subAccount.entryPrice;
        entryFunding = subAccount.entryFunding;
    }
}

File 1 of 25 : Liquidity.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../libraries/LibAsset.sol";
import "../libraries/LibSubAccount.sol";
import "../libraries/LibMath.sol";
import "../libraries/LibLiquidity.sol";
import "../interfaces/IMuxRebalancerCallback.sol";
import "./Account.sol";
import "./Storage.sol";

contract Liquidity is Storage, Account {
    using LibAsset for Asset;
    using LibMath for uint256;
    using LibSubAccount for bytes32;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    /**
     * @dev   Add liquidity.
     *
     * @param trader            liquidity provider address.
     * @param tokenId           asset.id that added.
     * @param rawAmount         asset token amount. decimals = erc20.decimals.
     * @param tokenPrice        token price.
     * @param mlpPrice          mlp price.
     * @param currentAssetValue liquidity USD value of a single asset in all chains (even if tokenId is a stable asset).
     * @param targetAssetValue  weight / Σ weight * total liquidity USD value in all chains.
     */
    function addLiquidity(
        address trader,
        uint8 tokenId,
        uint256 rawAmount, // NOTE: OrderBook SHOULD transfer rawAmount collateral to LiquidityPool
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 currentAssetValue,
        uint96 targetAssetValue
    ) external onlyOrderBook returns (uint96 mlpAmount) {
        require(trader != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(tokenId), "LST"); // the asset is not LiSTed
        require(rawAmount != 0, "A=0"); // Amount Is Zero
        require(mlpPrice != 0, "P=0"); // Price Is Zero
        require(mlpPrice <= _storage.mlpPriceUpperBound, "MPO"); // Mlp Price is Out of range
        require(mlpPrice >= _storage.mlpPriceLowerBound, "MPO"); // Mlp Price is Out of range
        Asset storage token = _storage.assets[tokenId];
        require(token.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(token.canAddRemoveLiquidity(), "TUL"); // the Token cannot be Used to add Liquidity
        tokenPrice = LibReferenceOracle.checkPriceWithSpread(_storage, token, tokenPrice, SpreadType.Bid);

        // token amount
        uint96 wadAmount = token.toWad(rawAmount);
        token.spotLiquidity += wadAmount; // already reserved fee
        // fee
        uint32 mlpFeeRate = _getLiquidityFeeRate(
            currentAssetValue,
            targetAssetValue,
            true,
            uint256(wadAmount).wmul(tokenPrice).safeUint96(),
            _storage.liquidityBaseFeeRate,
            _storage.liquidityDynamicFeeRate
        );
        uint96 feeCollateral = uint256(wadAmount).rmul(mlpFeeRate).safeUint96();
        token.collectedFee += feeCollateral; // spotLiquidity was modified above
        emit CollectedFee(tokenId, feeCollateral);
        wadAmount -= feeCollateral;
        // mlp
        mlpAmount = ((uint256(wadAmount) * uint256(tokenPrice)) / uint256(mlpPrice)).safeUint96();
        IERC20Upgradeable(_storage.mlp).transfer(trader, mlpAmount);
        emit AddLiquidity(trader, tokenId, tokenPrice, mlpPrice, mlpAmount, feeCollateral);
        _updateSequence();
        _updateBrokerTransactions();
    }

    /**
     * @dev   Remove liquidity.
     *
     * @param trader            liquidity provider address.
     * @param mlpAmount         mlp amount.
     * @param tokenId           asset.id that removed to.
     * @param tokenPrice        token price.
     * @param mlpPrice          mlp price.
     * @param currentAssetValue liquidity USD value of a single asset in all chains (even if tokenId is a stable asset).
     * @param targetAssetValue  weight / Σ weight * total liquidity USD value in all chains.
     */
    function removeLiquidity(
        address trader,
        uint96 mlpAmount, // NOTE: OrderBook SHOULD transfer mlpAmount mlp to LiquidityPool
        uint8 tokenId,
        uint96 tokenPrice,
        uint96 mlpPrice,
        uint96 currentAssetValue,
        uint96 targetAssetValue
    ) external onlyOrderBook returns (uint256 rawAmount) {
        require(trader != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(tokenId), "LST"); // the asset is not LiSTed
        require(mlpPrice != 0, "P=0"); // Price Is Zero
        require(mlpPrice <= _storage.mlpPriceUpperBound, "MPO"); // Mlp Price is Out of range
        require(mlpPrice >= _storage.mlpPriceLowerBound, "MPO"); // Mlp Price is Out of range
        require(mlpAmount != 0, "A=0"); // Amount Is Zero
        Asset storage token = _storage.assets[tokenId];
        require(token.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(token.canAddRemoveLiquidity(), "TUL"); // the Token cannot be Used to remove Liquidity
        tokenPrice = LibReferenceOracle.checkPriceWithSpread(_storage, token, tokenPrice, SpreadType.Ask);

        // amount
        uint96 wadAmount = ((uint256(mlpAmount) * uint256(mlpPrice)) / uint256(tokenPrice)).safeUint96();
        // fee
        uint96 feeCollateral;
        {
            uint32 mlpFeeRate = _getLiquidityFeeRate(
                currentAssetValue,
                targetAssetValue,
                false,
                uint256(wadAmount).wmul(tokenPrice).safeUint96(),
                _storage.liquidityBaseFeeRate,
                _storage.liquidityDynamicFeeRate
            );
            feeCollateral = uint256(wadAmount).rmul(mlpFeeRate).safeUint96();
        }
        token.collectedFee += feeCollateral; // spotLiquidity will be modified below
        emit CollectedFee(tokenId, feeCollateral);
        wadAmount -= feeCollateral;
        // send token
        require(wadAmount <= token.spotLiquidity, "LIQ"); // insufficient LIQuidity
        token.spotLiquidity -= wadAmount; // already deduct fee
        rawAmount = token.toRaw(wadAmount);
        token.transferOut(trader, rawAmount, _storage.weth, _storage.nativeUnwrapper);
        emit RemoveLiquidity(trader, tokenId, tokenPrice, mlpPrice, mlpAmount, feeCollateral);
        _updateSequence();
        _updateBrokerTransactions();
    }

    /**
     * @notice Redeem mux token into original tokens.
     *
     *         Only strict stable coins and un-stable coins are supported.
     */
    function redeemMuxToken(
        address trader,
        uint8 tokenId,
        uint96 muxTokenAmount // NOTE: OrderBook SHOULD transfer muxTokenAmount to LiquidityPool
    ) external onlyOrderBook {
        LibLiquidity.redeemMuxToken(_storage, trader, tokenId, muxTokenAmount);
        emit RedeemMuxToken(trader, tokenId, muxTokenAmount);
        _updateSequence();
    }

    /**
     * @notice Broker can update funding each [fundingInterval] seconds by specifying utilizations.
     *
     *         Check _getFundingRate in Liquidity.sol on how to calculate funding rate.
     * @param  stableUtilization    Stable coin utilization in all chains.
     * @param  unstableTokenIds     All unstable Asset id(s) MUST be passed in order. ex: 1, 2, 5, 6, ...
     * @param  unstableUtilizations Unstable Asset utilizations in all chains.
     * @param  unstablePrices       Unstable Asset prices.
     */
    function updateFundingState(
        uint32 stableUtilization, // 1e5
        uint8[] calldata unstableTokenIds,
        uint32[] calldata unstableUtilizations, // 1e5
        uint96[] calldata unstablePrices
    ) external onlyOrderBook {
        uint32 nextFundingTime = (_blockTimestamp() / _storage.fundingInterval) * _storage.fundingInterval;
        if (_storage.lastFundingTime == 0) {
            // init state. just update lastFundingTime
            _storage.lastFundingTime = nextFundingTime;
        } else if (_storage.lastFundingTime + _storage.fundingInterval >= _blockTimestamp()) {
            // do nothing
        } else {
            uint32 timeSpan = nextFundingTime - _storage.lastFundingTime;
            _updateFundingState(stableUtilization, unstableTokenIds, unstableUtilizations, unstablePrices, timeSpan);
            _storage.lastFundingTime = nextFundingTime;
        }
        _updateSequence();
    }

    /**
     * @dev  Rebalance pool liquidity. Swap token 0 for token 1.
     *
     *       rebalancer must implement IMuxRebalancerCallback.
     */
    function rebalance(
        address rebalancer,
        uint8 tokenId0,
        uint8 tokenId1,
        uint96 rawAmount0,
        uint96 maxRawAmount1,
        bytes32 userData,
        uint96 price0,
        uint96 price1
    ) external onlyOrderBook {
        uint96 rawAmount1 = LibLiquidity.rebalance(
            _storage,
            rebalancer,
            tokenId0,
            tokenId1,
            rawAmount0,
            maxRawAmount1,
            userData,
            price0,
            price1
        );
        emit Rebalance(rebalancer, tokenId0, tokenId1, price0, price1, rawAmount0, rawAmount1);
        _updateSequence();
    }

    /**
     * @dev Anyone can withdraw collectedFee into Vault.
     */
    function withdrawCollectedFee(uint8[] memory assetIds) external {
        require(_storage.vault != address(0), "VLT"); // bad VauLT
        for (uint256 i = 0; i < assetIds.length; i++) {
            uint8 assetId = assetIds[i];
            uint96 collectedFee = LibLiquidity.withdrawCollectedFee(_storage, assetId);
            emit WithdrawCollectedFee(assetId, collectedFee);
        }
        _updateSequence();
    }

    /**
     * @dev Broker can withdraw brokerGasRebate.
     */
    function claimBrokerGasRebate(address receiver) external onlyOrderBook returns (uint256 rawAmount) {
        require(receiver != address(0), "RCV"); // bad ReCeiVer
        uint256 assetCount = _storage.assets.length;
        for (uint256 assetId = 0; assetId < assetCount; assetId++) {
            Asset storage asset = _storage.assets[assetId];
            if (asset.tokenAddress == _storage.weth) {
                uint96 rebate = (uint256(_storage.brokerGasRebate) * uint256(_storage.brokerTransactions)).safeUint96();
                require(asset.spotLiquidity >= rebate, "LIQ"); // insufficient LIQuidity
                asset.spotLiquidity -= rebate;
                rawAmount = asset.toRaw(rebate);
                emit ClaimBrokerGasRebate(receiver, _storage.brokerTransactions, rawAmount);
                _storage.brokerTransactions = 0;
                asset.transferOut(receiver, rawAmount, _storage.weth, _storage.nativeUnwrapper);
                _updateSequence();
                return rawAmount;
            }
        }
    }

    function _updateFundingState(
        uint32 stableUtilization, // 1e5
        uint8[] calldata unstableTokenIds,
        uint32[] calldata unstableUtilizations, // 1e5
        uint96[] calldata unstablePrices,
        uint32 timeSpan
    ) internal {
        require(unstableTokenIds.length == unstableUtilizations.length, "LEN"); // LENgth of 2 arguments does not match
        require(unstableTokenIds.length == unstablePrices.length, "LEN"); // LENgth of 2 arguments does not match
        // stable
        uint32 shortFundingRate;
        uint128 shortCumulativeFundingRate;
        (shortFundingRate, shortCumulativeFundingRate) = _getFundingRate(
            _storage.shortFundingBaseRate8H,
            _storage.shortFundingLimitRate8H,
            stableUtilization,
            timeSpan
        );
        // unstable
        uint8 tokenLen = uint8(_storage.assets.length);
        uint8 i = 0;
        for (uint8 tokenId = 0; tokenId < tokenLen; tokenId++) {
            Asset storage asset = _storage.assets[tokenId];
            if (asset.isStable()) {
                continue;
            }
            require(i < unstableTokenIds.length, "LEN"); // invalid LENgth of unstableTokenIds
            require(unstableTokenIds[i] == tokenId, "AID"); // AssetID mismatched
            (uint32 longFundingRate, uint128 longCumulativeFundingRate) = _getFundingRate(
                asset.longFundingBaseRate8H,
                asset.longFundingLimitRate8H,
                unstableUtilizations[i],
                timeSpan
            );
            asset.longCumulativeFundingRate += longCumulativeFundingRate;
            {
                uint96 price = LibReferenceOracle.checkPrice(_storage, asset, unstablePrices[i]);
                asset.shortCumulativeFunding += uint256(shortCumulativeFundingRate).wmul(price).safeUint128();
            }
            emit UpdateFundingRate(
                tokenId,
                longFundingRate,
                asset.longCumulativeFundingRate,
                shortFundingRate,
                asset.shortCumulativeFunding
            );
            i += 1;
        }
    }

    /**
     * @dev   Liquidity fee rate.
     *
     *        Lower rates indicate liquidity is closer to target.
     *
     *                                                  targetLiquidity
     *                     <------------------------------------+--------------------------------------> liquidity
     *
     * case 1: high rebate   * currentLiq * newLiq
     *                       * currentLiq                                                    * newLiq
     *
     * case 2: low rebate                 * currentLiq * newLiq
     *                                    * currentLiq                          * newLiq
     *
     * case 3: higher fee                                          * currentLiq * newLiq
     *
     * case 4: max fee                                             * currentLiq              * newLiq
     *                                                                          * currentLiq * newLiq
     *
     * @param currentAssetValue liquidity USD value of a single asset in all chains (even if tokenId is a stable asset). decimals = 18.
     * @param targetAssetValue  weight / Σ weight * total liquidity USD value in all chains. decimals = 18.
     * @param isAdd             true if add liquidity, false if remove liquidity.
     * @param deltaValue        add/remove liquidity USD value. decimals = 18.
     * @param baseFeeRate       base fee.
     * @param dynamicFeeRate    dynamic fee.
     */
    function _getLiquidityFeeRate(
        uint96 currentAssetValue,
        uint96 targetAssetValue,
        bool isAdd,
        uint96 deltaValue,
        uint32 baseFeeRate, // 1e5
        uint32 dynamicFeeRate // 1e5
    ) internal pure returns (uint32) {
        uint96 newAssetValue;
        if (isAdd) {
            newAssetValue = currentAssetValue + deltaValue;
        } else {
            require(currentAssetValue >= deltaValue, "LIQ"); // insufficient LIQuidity
            newAssetValue = currentAssetValue - deltaValue;
        }
        // | x - target |
        uint96 oldDiff = currentAssetValue > targetAssetValue
            ? currentAssetValue - targetAssetValue
            : targetAssetValue - currentAssetValue;
        uint96 newDiff = newAssetValue > targetAssetValue
            ? newAssetValue - targetAssetValue
            : targetAssetValue - newAssetValue;
        if (targetAssetValue == 0) {
            // avoid division by 0
            return baseFeeRate;
        } else if (newDiff < oldDiff) {
            // improves
            uint32 rebate = ((uint256(dynamicFeeRate) * uint256(oldDiff)) / uint256(targetAssetValue)).safeUint32();
            return baseFeeRate > rebate ? baseFeeRate - rebate : 0;
        } else {
            // worsen
            uint96 avgDiff = (oldDiff + newDiff) / 2;
            avgDiff = LibMath.min(avgDiff, targetAssetValue);
            uint32 dynamic = ((uint256(dynamicFeeRate) * uint256(avgDiff)) / uint256(targetAssetValue)).safeUint32();
            return baseFeeRate + dynamic;
        }
    }

    /**
     * @dev Funding rate formula.
     *
     * ^ fr           / limit
     * |            /
     * |          /
     * |        /
     * |______/ base
     * |    .
     * |  .
     * |.
     * +-------------------> %util
     */
    function _getFundingRate(
        uint32 baseRate8H, // 1e5
        uint32 limitRate8H, // 1e5
        uint32 utilization, // 1e5
        uint32 timeSpan // 1e0
    ) internal pure returns (uint32 newFundingRate, uint128 cumulativeFundingRate) {
        require(utilization <= 1e5, "U>1"); // %utilization > 100%
        newFundingRate = uint256(utilization).rmul(limitRate8H).safeUint32();
        newFundingRate = LibMath.max32(newFundingRate, baseRate8H);
        cumulativeFundingRate = ((uint256(newFundingRate) * uint256(timeSpan) * 1e13) / FUNDING_PERIOD).safeUint128();
    }
}

File 2 of 25 : Storage.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

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

import "../components/SafeOwnableUpgradeable.sol";
import "../libraries/LibSubAccount.sol";
import "../libraries/LibAsset.sol";
import "./Types.sol";
import "./Events.sol";

contract Storage is Initializable, SafeOwnableUpgradeable, Events {
    using LibAsset for Asset;

    LiquidityPoolStorage internal _storage;

    modifier onlyOrderBook() {
        require(_msgSender() == _storage.orderBook, "BOK"); // can only be called by order BOoK
        _;
    }

    modifier onlyLiquidityManager() {
        require(_storage.liquidityManager[_msgSender()], "LQM"); // can only be called by LiQuidity Manager
        _;
    }

    modifier onlyMaintainer() {
        require(_msgSender() == _storage.maintainer || _msgSender() == owner(), "S!M"); // Sender is Not MaiNTainer
        _;
    }

    function _updateSequence() internal {
        unchecked {
            _storage.sequence += 1;
        }
        emit UpdateSequence(_storage.sequence);
    }

    function _updateBrokerTransactions() internal {
        unchecked {
            _storage.brokerTransactions += 1;
        }
    }

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

    function _hasAsset(uint8 assetId) internal view returns (bool) {
        return assetId < _storage.assets.length;
    }

    function _isStable(uint8 tokenId) internal view returns (bool) {
        return _storage.assets[tokenId].isStable();
    }

    bytes32[50] internal _gap;
}

File 2 of 25 : Types.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

struct LiquidityPoolStorage {
    // slot
    address orderBook;
    // slot
    address mlp;
    // slot
    address _reserved6; // was liquidityManager
    // slot
    address weth;
    // slot
    uint128 _reserved1;
    uint32 shortFundingBaseRate8H; // 1e5
    uint32 shortFundingLimitRate8H; // 1e5
    uint32 fundingInterval; // 1e0
    uint32 lastFundingTime; // 1e0
    // slot
    uint32 _reserved2;
    // slot
    Asset[] assets;
    // slot
    mapping(bytes32 => SubAccount) accounts;
    // slot
    mapping(address => bytes32) _reserved3;
    // slot
    address _reserved4;
    uint96 _reserved5;
    // slot
    uint96 mlpPriceLowerBound; // safeguard against mlp price attacks
    uint96 mlpPriceUpperBound; // safeguard against mlp price attacks
    uint32 liquidityBaseFeeRate; // 1e5
    uint32 liquidityDynamicFeeRate; // 1e5
    // slot
    address nativeUnwrapper;
    // a sequence number that changes when LiquidityPoolStorage updated. this helps to keep track the state of LiquidityPool.
    uint32 sequence; // 1e0. note: will be 0 after 0xffffffff
    uint32 strictStableDeviation; // 1e5. strictStable price is 1.0 if in this damping range
    uint32 brokerTransactions; // transaction count for broker gas rebates
    // slot
    address vault;
    uint96 brokerGasRebate; // the number of native tokens for broker gas rebates per transaction
    // slot
    address maintainer;
    // slot
    mapping(address => bool) liquidityManager;
    bytes32[50] _gap;
}

struct Asset {
    // slot
    // assets with the same symbol in different chains are the same asset. they shares the same muxToken. so debts of the same symbol
    // can be accumulated across chains (see Reader.AssetState.deduct). ex: ERC20(fBNB).symbol should be "BNB", so that BNBs of
    // different chains are the same.
    // since muxToken of all stable coins is the same and is calculated separately (see Reader.ChainState.stableDeduct), stable coin
    // symbol can be different (ex: "USDT", "USDT.e" and "fUSDT").
    bytes32 symbol;
    // slot
    address tokenAddress; // erc20.address
    uint8 id;
    uint8 decimals; // erc20.decimals
    uint56 flags; // a bitset of ASSET_*
    uint24 _flagsPadding;
    // slot
    uint32 initialMarginRate; // 1e5
    uint32 maintenanceMarginRate; // 1e5
    uint32 minProfitRate; // 1e5
    uint32 minProfitTime; // 1e0
    uint32 positionFeeRate; // 1e5
    // note: 96 bits remaining
    // slot
    address referenceOracle;
    uint32 referenceDeviation; // 1e5
    uint8 referenceOracleType;
    uint32 halfSpread; // 1e5
    // note: 24 bits remaining
    // slot
    uint96 credit;
    uint128 _reserved2;
    // slot
    uint96 collectedFee;
    uint32 liquidationFeeRate; // 1e5
    uint96 spotLiquidity;
    // note: 32 bits remaining
    // slot
    uint96 maxLongPositionSize;
    uint96 totalLongPosition;
    // note: 64 bits remaining
    // slot
    uint96 averageLongPrice;
    uint96 maxShortPositionSize;
    // note: 64 bits remaining
    // slot
    uint96 totalShortPosition;
    uint96 averageShortPrice;
    // note: 64 bits remaining
    // slot, less used
    address muxTokenAddress; // muxToken.address. all stable coins share the same muxTokenAddress
    uint32 spotWeight; // 1e0
    uint32 longFundingBaseRate8H; // 1e5
    uint32 longFundingLimitRate8H; // 1e5
    // slot
    uint128 longCumulativeFundingRate; // Σ_t fundingRate_t
    uint128 shortCumulativeFunding; // Σ_t fundingRate_t * indexPrice_t
}

uint32 constant FUNDING_PERIOD = 3600 * 8;

uint56 constant ASSET_IS_STABLE = 0x00000000000001; // is a usdt, usdc, ...
uint56 constant ASSET_CAN_ADD_REMOVE_LIQUIDITY = 0x00000000000002; // can call addLiquidity and removeLiquidity with this token
uint56 constant ASSET_IS_TRADABLE = 0x00000000000100; // allowed to be assetId
uint56 constant ASSET_IS_OPENABLE = 0x00000000010000; // can open position
uint56 constant ASSET_IS_SHORTABLE = 0x00000001000000; // allow shorting this asset
uint56 constant ASSET_USE_STABLE_TOKEN_FOR_PROFIT = 0x00000100000000; // take profit will get stable coin
uint56 constant ASSET_IS_ENABLED = 0x00010000000000; // allowed to be assetId and collateralId
uint56 constant ASSET_IS_STRICT_STABLE = 0x01000000000000; // assetPrice is always 1 unless volatility exceeds strictStableDeviation

struct SubAccount {
    // slot
    uint96 collateral;
    uint96 size;
    uint32 lastIncreasedTime;
    // slot
    uint96 entryPrice;
    uint128 entryFunding; // entry longCumulativeFundingRate for long position. entry shortCumulativeFunding for short position
}

enum ReferenceOracleType {
    None,
    Chainlink
}

File 2 of 25 : IMuxRebalancerCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

/**
 * @notice Any contract that calls IOrderBook#placeRebalanceOrder must implement this interface
 */
interface IMuxRebalancerCallback {
    /**
     * @notice Rebalancer.muxRebalanceCallback is called when Brokers calls IOrderBook#fillRebalanceOrder, where
     *         Rebalancer is `msg.sender` of IOrderBook#placeRebalanceOrder.
     *
     *         Rebalancer will get token0 and send token1 back to `msg.sender`.
     */
    function muxRebalanceCallback(
        address token0,
        address token1,
        uint256 rawAmount0,
        uint256 minRawAmount1,
        bytes32 data
    ) external;
}

File 2 of 25 : INativeUnwrapper.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

interface INativeUnwrapper {
    function unwrap(address payable to, uint256 rawAmount) external;
}

File 2 of 25 : IVotesUpgradeable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;

interface IVotesUpgradeable {
    function delegate(address delegatee) external;
}

File 2 of 25 : IWETH9.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

interface IWETH {
    function deposit() external payable;

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

    function withdraw(uint256) external;
}

File 2 of 25 : LibAsset.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

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

import "../interfaces/IWETH9.sol";
import "../interfaces/INativeUnwrapper.sol";
import "../libraries/LibMath.sol";
import "../core/Types.sol";

library LibAsset {
    using LibMath for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    function transferOut(
        Asset storage token,
        address recipient,
        uint256 rawAmount,
        address weth,
        address nativeUnwrapper
    ) internal {
        if (token.tokenAddress == weth) {
            IWETH(weth).transfer(nativeUnwrapper, rawAmount);
            INativeUnwrapper(nativeUnwrapper).unwrap(payable(recipient), rawAmount);
        } else {
            IERC20Upgradeable(token.tokenAddress).safeTransfer(recipient, rawAmount);
        }
    }

    function issueMuxToken(Asset storage token, address recipient, uint256 muxTokenAmount) internal {
        IERC20Upgradeable(token.muxTokenAddress).safeTransfer(recipient, muxTokenAmount);
    }

    function toWad(Asset storage token, uint256 rawAmount) internal view returns (uint96) {
        return (rawAmount * (10 ** (18 - token.decimals))).safeUint96();
    }

    function toRaw(Asset storage token, uint96 wadAmount) internal view returns (uint256) {
        return uint256(wadAmount) / 10 ** (18 - token.decimals);
    }

    // is a usdt, usdc, ...
    function isStable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_STABLE) != 0;
    }

    // can call addLiquidity and removeLiquidity with this token
    function canAddRemoveLiquidity(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_CAN_ADD_REMOVE_LIQUIDITY) != 0;
    }

    // allowed to be assetId
    function isTradable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_TRADABLE) != 0;
    }

    // can open position
    function isOpenable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_OPENABLE) != 0;
    }

    // allow shorting this asset
    function isShortable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_SHORTABLE) != 0;
    }

    // take profit will get stable coin
    function useStableTokenForProfit(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_USE_STABLE_TOKEN_FOR_PROFIT) != 0;
    }

    // allowed to be assetId and collateralId
    function isEnabled(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_ENABLED) != 0;
    }

    // assetPrice is always 1 unless volatility exceeds strictStableDeviation
    function isStrictStable(Asset storage asset) internal view returns (bool) {
        return (asset.flags & ASSET_IS_STRICT_STABLE) != 0;
    }
}

File 2 of 25 : LibLiquidity.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "./LibAsset.sol";
import "./LibSubAccount.sol";
import "./LibMath.sol";
import "../interfaces/IMuxRebalancerCallback.sol";
import "../core/Account.sol";
import "../core/Storage.sol";

/**
 * Low frequency operations of Liquidity
 */
library LibLiquidity {
    using LibAsset for Asset;
    using LibMath for uint256;
    using LibSubAccount for bytes32;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    /**
     * @notice Redeem mux token into original tokens.
     *
     *         Only strict stable coins and un-stable coins are supported.
     */
    function redeemMuxToken(
        LiquidityPoolStorage storage _storage,
        address trader,
        uint8 tokenId,
        uint96 muxTokenAmount // NOTE: OrderBook SHOULD transfer muxTokenAmount to LiquidityPool
    ) external {
        require(trader != address(0), "T=0"); // Trader address is zero
        require(_hasAsset(_storage, tokenId), "LST"); // the asset is not LiSTed
        require(muxTokenAmount != 0, "A=0"); // Amount Is Zero
        Asset storage token = _storage.assets[tokenId];
        require(token.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        if (token.isStable()) {
            require(token.isStrictStable(), "STR"); // only STRict stable coins and un-stable coins are supported
        }
        require(token.spotLiquidity >= muxTokenAmount, "LIQ"); // insufficient LIQuidity
        uint256 rawAmount = token.toRaw(muxTokenAmount);
        token.spotLiquidity -= muxTokenAmount;
        token.transferOut(trader, rawAmount, _storage.weth, _storage.nativeUnwrapper);
    }

    /**
     * @dev  Rebalance pool liquidity. Swap token 0 for token 1.
     *
     *       rebalancer must implement IMuxRebalancerCallback.
     * @return rawAmount1 received amount of token 1.
     */
    function rebalance(
        LiquidityPoolStorage storage _storage,
        address rebalancer,
        uint8 tokenId0,
        uint8 tokenId1,
        uint96 rawAmount0,
        uint96 maxRawAmount1,
        bytes32 userData,
        uint96 price0,
        uint96 price1
    ) external returns (uint96) {
        require(rebalancer != address(0), "R=0"); // Rebalancer address is zero
        require(_hasAsset(_storage, tokenId0), "LST"); // the asset is not LiSTed
        require(_hasAsset(_storage, tokenId1), "LST"); // the asset is not LiSTed
        require(rawAmount0 != 0, "A=0"); // Amount Is Zero
        Asset storage token0 = _storage.assets[tokenId0];
        Asset storage token1 = _storage.assets[tokenId1];
        price0 = LibReferenceOracle.checkPrice(_storage, token0, price0);
        price1 = LibReferenceOracle.checkPrice(_storage, token1, price1);
        require(token0.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        require(token1.isEnabled(), "ENA"); // the token is temporarily not ENAbled
        // send token 0. get amount 1
        uint256 expectedRawAmount1;
        {
            uint96 amount0 = token0.toWad(rawAmount0);
            require(token0.spotLiquidity >= amount0, "LIQ"); // insufficient LIQuidity
            token0.spotLiquidity -= amount0;

            uint96 expectedAmount1 = ((uint256(amount0) * uint256(price0)) / uint256(price1)).safeUint96();
            expectedRawAmount1 = token1.toRaw(expectedAmount1);
        }
        require(expectedRawAmount1 <= maxRawAmount1, "LMT"); // LiMiTed by limitPrice
        // swap. check amount 1
        uint96 rawAmount1;
        {
            IERC20Upgradeable(token0.tokenAddress).safeTransfer(rebalancer, rawAmount0);
            uint256 rawAmount1Old = IERC20Upgradeable(token1.tokenAddress).balanceOf(address(this));
            IMuxRebalancerCallback(rebalancer).muxRebalanceCallback(
                token0.tokenAddress,
                token1.tokenAddress,
                rawAmount0,
                expectedRawAmount1,
                userData
            );
            uint256 rawAmount1New = IERC20Upgradeable(token1.tokenAddress).balanceOf(address(this));
            require(rawAmount1Old <= rawAmount1New, "T1A"); // Token 1 Amount mismatched
            rawAmount1 = (rawAmount1New - rawAmount1Old).safeUint96();
        }
        require(rawAmount1 >= expectedRawAmount1, "T1A"); // Token 1 Amount mismatched
        token1.spotLiquidity += token1.toWad(rawAmount1);
        return rawAmount1;
    }

    /**
     * @dev Anyone can withdraw collectedFee into Vault.
     * @return collectedFee decimals = 18.
     */
    function withdrawCollectedFee(LiquidityPoolStorage storage _storage, uint8 assetId) external returns (uint96) {
        require(_storage.vault != address(0), "VLT"); // bad VauLT
        require(_hasAsset(_storage, assetId), "LST"); // the asset is not LiSTed
        Asset storage asset = _storage.assets[assetId];
        uint96 collectedFee = asset.collectedFee;
        require(collectedFee <= asset.spotLiquidity, "LIQ"); // insufficient LIQuidity
        asset.collectedFee = 0;
        asset.spotLiquidity -= collectedFee;
        uint256 rawAmount = asset.toRaw(collectedFee);
        IERC20Upgradeable(asset.tokenAddress).safeTransfer(_storage.vault, rawAmount);
        return collectedFee;
    }

    function _hasAsset(LiquidityPoolStorage storage _storage, uint8 assetId) internal view returns (bool) {
        return assetId < _storage.assets.length;
    }
}

File 2 of 25 : LibMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

library LibMath {
    function min(uint96 a, uint96 b) internal pure returns (uint96) {
        return a <= b ? a : b;
    }

    function min32(uint32 a, uint32 b) internal pure returns (uint32) {
        return a <= b ? a : b;
    }

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

    function wmul(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / 1e18;
    }

    function rmul(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * b) / 1e5;
    }

    function wdiv(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a * 1e18) / b;
    }

    function safeUint32(uint256 n) internal pure returns (uint32) {
        require(n <= type(uint32).max, "O32"); // uint32 Overflow
        return uint32(n);
    }

    function safeUint96(uint256 n) internal pure returns (uint96) {
        require(n <= type(uint96).max, "O96"); // uint96 Overflow
        return uint96(n);
    }

    function safeUint128(uint256 n) internal pure returns (uint128) {
        require(n <= type(uint128).max, "O12"); // uint128 Overflow
        return uint128(n);
    }
}

File 2 of 25 : LibReferenceOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../core/Types.sol";
import "./LibMath.sol";
import "./LibAsset.sol";

interface IChainlink {
    function latestAnswer() external view returns (int256);

    function latestTimestamp() external view returns (uint256);

    function latestRound() external view returns (uint256);

    function getAnswer(uint256 roundId) external view returns (int256);

    function getTimestamp(uint256 roundId) external view returns (uint256);

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

interface IChainlinkV3 {
    function decimals() external view returns (uint8);

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

    function version() external view returns (uint256);

    function getRoundData(
        uint80 _roundId
    )
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

interface IChainlinkV2V3 is IChainlink, IChainlinkV3 {}

enum SpreadType {
    Ask,
    Bid
}

library LibReferenceOracle {
    using LibMath for uint256;
    using LibMath for uint96;
    using LibAsset for Asset;

    // indicate that the asset price is too far away from reference oracle
    event AssetPriceOutOfRange(uint8 assetId, uint96 price, uint96 referencePrice, uint32 deviation);

    /**
     * @dev Check oracle parameters before set.
     */
    function checkParameters(
        ReferenceOracleType referenceOracleType,
        address referenceOracle,
        uint32 referenceDeviation
    ) internal view {
        require(referenceDeviation <= 1e5, "D>1"); // %deviation > 100%
        if (referenceOracleType == ReferenceOracleType.Chainlink) {
            IChainlinkV2V3 o = IChainlinkV2V3(referenceOracle);
            require(o.decimals() == 8, "!D8"); // we only support decimals = 8
            require(o.latestAnswer() > 0, "P=0"); // oracle Price <= 0
        }
    }

    /**
     * @dev Truncate price if the error is too large.
     */
    function checkPrice(
        LiquidityPoolStorage storage pool,
        Asset storage asset,
        uint96 price
    ) internal returns (uint96) {
        require(price != 0, "P=0"); // broker price = 0

        // truncate price if the error is too large
        if (ReferenceOracleType(asset.referenceOracleType) == ReferenceOracleType.Chainlink) {
            uint96 ref = _readChainlink(asset.referenceOracle);
            price = _truncatePrice(asset, price, ref);
        }

        // strict stable dampener
        if (asset.isStrictStable()) {
            uint256 delta = price > 1e18 ? price - 1e18 : 1e18 - price;
            uint256 dampener = uint256(pool.strictStableDeviation) * 1e13; // 1e5 => 1e18
            if (delta <= dampener) {
                price = 1e18;
            }
        }

        return price;
    }

    /**
     * @dev check price and add spread, where spreadType should be:
     *
     *      subAccount.isLong   openPosition   closePosition   addLiquidity   removeLiquidity
     *      long                ask            bid
     *      short               bid            ask
     *      N/A                                                bid            ask
     */
    function checkPriceWithSpread(
        LiquidityPoolStorage storage pool,
        Asset storage asset,
        uint96 price,
        SpreadType spreadType
    ) internal returns (uint96) {
        price = checkPrice(pool, asset, price);
        price = _addSpread(asset, price, spreadType);
        return price;
    }

    function _readChainlink(address referenceOracle) internal view returns (uint96) {
        int256 ref = IChainlinkV2V3(referenceOracle).latestAnswer();
        require(ref > 0, "P=0"); // oracle Price <= 0
        ref *= 1e10; // decimals 8 => 18
        return uint256(ref).safeUint96();
    }

    function _truncatePrice(Asset storage asset, uint96 price, uint96 ref) private returns (uint96) {
        if (asset.referenceDeviation == 0) {
            return ref;
        }
        uint256 deviation = uint256(ref).rmul(asset.referenceDeviation);
        uint96 bound = (uint256(ref) - deviation).safeUint96();
        if (price < bound) {
            emit AssetPriceOutOfRange(asset.id, price, ref, asset.referenceDeviation);
            price = bound;
        }
        bound = (uint256(ref) + deviation).safeUint96();
        if (price > bound) {
            emit AssetPriceOutOfRange(asset.id, price, ref, asset.referenceDeviation);
            price = bound;
        }
        return price;
    }

    function _addSpread(Asset storage asset, uint96 price, SpreadType spreadType) private view returns (uint96) {
        if (asset.halfSpread == 0) {
            return price;
        }
        uint96 halfSpread = uint256(price).rmul(asset.halfSpread).safeUint96();
        if (spreadType == SpreadType.Bid) {
            require(price > halfSpread, "P=0"); // Price - halfSpread = 0. impossible
            return price - halfSpread;
        } else {
            return price + halfSpread;
        }
    }
}

File 2 of 25 : LibSubAccount.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.10;

import "../core/Types.sol";

/**
 * SubAccountId
 *         96             88        80       72        0
 * +---------+--------------+---------+--------+--------+
 * | Account | collateralId | assetId | isLong | unused |
 * +---------+--------------+---------+--------+--------+
 */
library LibSubAccount {
    bytes32 constant SUB_ACCOUNT_ID_FORBIDDEN_BITS = bytes32(uint256(0xffffffffffffffffff));

    function getSubAccountOwner(bytes32 subAccountId) internal pure returns (address account) {
        account = address(uint160(uint256(subAccountId) >> 96));
    }

    function getSubAccountCollateralId(bytes32 subAccountId) internal pure returns (uint8) {
        return uint8(uint256(subAccountId) >> 88);
    }

    function getSubAccountAssetId(bytes32 subAccountId) internal pure returns (uint8) {
        return uint8(uint256(subAccountId) >> 80);
    }

    function isLong(bytes32 subAccountId) internal pure returns (bool) {
        return uint8((uint256(subAccountId) >> 72)) > 0;
    }

    struct DecodedSubAccountId {
        address account;
        uint8 collateralId;
        uint8 assetId;
        bool isLong;
    }

    function decodeSubAccountId(bytes32 subAccountId) internal pure returns (DecodedSubAccountId memory decoded) {
        require((subAccountId & SUB_ACCOUNT_ID_FORBIDDEN_BITS) == 0, "AID"); // bad subAccount ID
        decoded.account = address(uint160(uint256(subAccountId) >> 96));
        decoded.collateralId = uint8(uint256(subAccountId) >> 88);
        decoded.assetId = uint8(uint256(subAccountId) >> 80);
        decoded.isLong = uint8((uint256(subAccountId) >> 72)) > 0;
    }
}

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

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"id","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isStable","type":"bool"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"address","name":"muxTokenAddress","type":"address"}],"name":"AddAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpAmount","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":true,"internalType":"address","name":"project","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"rawBorrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rawFee","type":"uint256"}],"name":"BorrowAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint32","name":"transactions","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"}],"name":"ClaimBrokerGasRebate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"bool","name":"hasProfit","type":"bool"},{"internalType":"uint96","name":"pnlUsd","type":"uint96"},{"internalType":"uint96","name":"remainPosition","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.ClosePositionArgs","name":"args","type":"tuple"}],"name":"ClosePosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"CollectedFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"collateralId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"wadAmount","type":"uint96"}],"name":"DepositCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"bool","name":"isStable","type":"bool"},{"indexed":false,"internalType":"uint96","name":"muxTokenAmount","type":"uint96"}],"name":"IssueMuxToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"bool","name":"hasProfit","type":"bool"},{"internalType":"uint96","name":"pnlUsd","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.LiquidateArgs","name":"args","type":"tuple"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"newEntryPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"},{"internalType":"uint96","name":"remainPosition","type":"uint96"},{"internalType":"uint96","name":"remainCollateral","type":"uint96"}],"indexed":false,"internalType":"struct Events.OpenPositionArgs","name":"args","type":"tuple"}],"name":"OpenPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"PrepareToTransferOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenId0","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"tokenId1","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"price0","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"price1","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"rawAmount0","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"rawAmount1","type":"uint96"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"muxTokenAmount","type":"uint96"}],"name":"RedeemMuxToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"mlpAmount","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"fee","type":"uint96"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":true,"internalType":"address","name":"project","type":"address"},{"indexed":true,"internalType":"address","name":"repayer","type":"address"},{"indexed":false,"internalType":"uint256","name":"rawRepayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rawFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"badDebt","type":"uint256"}],"name":"RepayAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint56","name":"newFlags","type":"uint56"},{"indexed":false,"internalType":"uint32","name":"newHalfSpread","type":"uint32"},{"indexed":false,"internalType":"uint96","name":"newMaxLongPositionSize","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"newMaxShortPositionSize","type":"uint96"}],"name":"SetAssetFlags","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"symbol","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"newInitialMarginRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMaintenanceMarginRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newPositionFeeRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newLiquidationFeeRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMinProfitRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newMinProfitTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newSpotWeight","type":"uint32"}],"name":"SetAssetParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newBrokerGasRebate","type":"uint96"}],"name":"SetBrokerGasRebate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"oldFundingInterval","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newFundingInterval","type":"uint32"}],"name":"SetFundingInterval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"newBaseRate8H","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newLimitRate8H","type":"uint32"}],"name":"SetFundingParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newLiquidityBaseFeeRate","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newLiquidityDynamicFeeRate","type":"uint32"}],"name":"SetLiquidityFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newLiquidityManager","type":"address"},{"indexed":false,"internalType":"bool","name":"isAdd","type":"bool"}],"name":"SetLiquidityManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newMaintainer","type":"address"}],"name":"SetMaintainer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"newLowerBound","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"newUpperBound","type":"uint96"}],"name":"SetMlpPriceRange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"referenceOracleType","type":"uint8"},{"indexed":false,"internalType":"address","name":"referenceOracle","type":"address"},{"indexed":false,"internalType":"uint32","name":"referenceDeviation","type":"uint32"}],"name":"SetReferenceOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"newStrictStableDeviation","type":"uint32"}],"name":"SetStrictStableDeviation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"tokenId","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"longFundingRate","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"longCumulativeFundingRate","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"shortFundingRate","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"shortCumulativeFunding","type":"uint128"}],"name":"UpdateFundingRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"sequence","type":"uint32"}],"name":"UpdateSequence","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint8","name":"collateralId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"rawAmount","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"wadAmount","type":"uint96"}],"name":"WithdrawCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"collectedFee","type":"uint96"}],"name":"WithdrawCollectedFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint8","name":"assetId","type":"uint8"},{"components":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint8","name":"collateralId","type":"uint8"},{"internalType":"uint8","name":"profitAssetId","type":"uint8"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint256","name":"withdrawRawAmount","type":"uint256"},{"internalType":"uint96","name":"assetPrice","type":"uint96"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"profitAssetPrice","type":"uint96"},{"internalType":"uint96","name":"entryPrice","type":"uint96"},{"internalType":"uint96","name":"feeUsd","type":"uint96"}],"indexed":false,"internalType":"struct Events.WithdrawProfitArgs","name":"args","type":"tuple"}],"name":"WithdrawProfit","type":"event"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"bytes32","name":"symbol","type":"bytes32"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"bool","name":"isStable","type":"bool"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"address","name":"muxTokenAddress","type":"address"}],"name":"addAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"internalType":"uint96","name":"currentAssetValue","type":"uint96"},{"internalType":"uint96","name":"targetAssetValue","type":"uint96"}],"name":"addLiquidity","outputs":[{"internalType":"uint96","name":"mlpAmount","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"uint256","name":"rawBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"rawFee","type":"uint256"}],"name":"borrowAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"claimBrokerGasRebate","outputs":[{"internalType":"uint256","name":"rawAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"delegateVoting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint256","name":"rawAmount","type":"uint256"}],"name":"depositCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancer","type":"address"},{"internalType":"uint8","name":"tokenId0","type":"uint8"},{"internalType":"uint8","name":"tokenId1","type":"uint8"},{"internalType":"uint96","name":"rawAmount0","type":"uint96"},{"internalType":"uint96","name":"maxRawAmount1","type":"uint96"},{"internalType":"bytes32","name":"userData","type":"bytes32"},{"internalType":"uint96","name":"price0","type":"uint96"},{"internalType":"uint96","name":"price1","type":"uint96"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"uint96","name":"muxTokenAmount","type":"uint96"}],"name":"redeemMuxToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint96","name":"mlpAmount","type":"uint96"},{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"uint96","name":"tokenPrice","type":"uint96"},{"internalType":"uint96","name":"mlpPrice","type":"uint96"},{"internalType":"uint96","name":"currentAssetValue","type":"uint96"},{"internalType":"uint96","name":"targetAssetValue","type":"uint96"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"rawAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"repayer","type":"address"},{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"uint256","name":"rawRepayAmount","type":"uint256"},{"internalType":"uint256","name":"rawFee","type":"uint256"},{"internalType":"uint256","name":"rawBadDebt","type":"uint256"}],"name":"repayAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"bool","name":"isTradable","type":"bool"},{"internalType":"bool","name":"isOpenable","type":"bool"},{"internalType":"bool","name":"isShortable","type":"bool"},{"internalType":"bool","name":"useStableTokenForProfit","type":"bool"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"bool","name":"isStrictStable","type":"bool"},{"internalType":"bool","name":"canAddRemoveLiquidity","type":"bool"},{"internalType":"uint32","name":"newHalfSpread","type":"uint32"},{"internalType":"uint96","name":"newMaxLongPositionSize","type":"uint96"},{"internalType":"uint96","name":"newMaxShortPositionSize","type":"uint96"}],"name":"setAssetFlags","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"bytes32","name":"symbol","type":"bytes32"},{"internalType":"uint32","name":"newInitialMarginRate","type":"uint32"},{"internalType":"uint32","name":"newMaintenanceMarginRate","type":"uint32"},{"internalType":"uint32","name":"newPositionFeeRate","type":"uint32"},{"internalType":"uint32","name":"newLiquidationFeeRate","type":"uint32"},{"internalType":"uint32","name":"newMinProfitRate","type":"uint32"},{"internalType":"uint32","name":"newMinProfitTime","type":"uint32"},{"internalType":"uint32","name":"newSpotWeight","type":"uint32"}],"name":"setAssetParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"newMlpPriceLowerBound","type":"uint96"},{"internalType":"uint96","name":"newMlpPriceUpperBound","type":"uint96"}],"name":"setEmergencyNumbers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"uint32","name":"newBaseRate8H","type":"uint32"},{"internalType":"uint32","name":"newLimitRate8H","type":"uint32"}],"name":"setFundingParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLiquidityManager","type":"address"},{"internalType":"bool","name":"isAdd","type":"bool"}],"name":"setLiquidityManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMaintainer","type":"address"}],"name":"setMaintainer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"newFundingInterval","type":"uint32"},{"internalType":"uint32","name":"newLiquidityBaseFeeRate","type":"uint32"},{"internalType":"uint32","name":"newLiquidityDynamicFeeRate","type":"uint32"},{"internalType":"uint32","name":"newStrictStableDeviation","type":"uint32"},{"internalType":"uint96","name":"newBrokerGasRebate","type":"uint96"}],"name":"setNumbers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"assetId","type":"uint8"},{"internalType":"enum ReferenceOracleType","name":"referenceOracleType","type":"uint8"},{"internalType":"address","name":"referenceOracle","type":"address"},{"internalType":"uint32","name":"referenceDeviation","type":"uint32"}],"name":"setReferenceOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"takeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"assetIds","type":"uint8[]"},{"internalType":"uint256[]","name":"rawAmounts","type":"uint256[]"}],"name":"transferLiquidityIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"assetIds","type":"uint8[]"},{"internalType":"uint256[]","name":"rawAmounts","type":"uint256[]"}],"name":"transferLiquidityOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"stableUtilization","type":"uint32"},{"internalType":"uint8[]","name":"unstableTokenIds","type":"uint8[]"},{"internalType":"uint32[]","name":"unstableUtilizations","type":"uint32[]"},{"internalType":"uint96[]","name":"unstablePrices","type":"uint96[]"}],"name":"updateFundingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"}],"name":"withdrawAllCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"subAccountId","type":"bytes32"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"uint96","name":"collateralPrice","type":"uint96"},{"internalType":"uint96","name":"assetPrice","type":"uint96"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"assetIds","type":"uint8[]"}],"name":"withdrawCollectedFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615ed780620000216000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80638e010652116100f9578063dce674ed11610097578063ecca2a5511610071578063ecca2a55146103b0578063f2fde38b146103c3578063f54de365146103d6578063fdd25cda146103e957600080fd5b8063dce674ed14610377578063de19a8bc1461038a578063dea9b4641461039d57600080fd5b80639fe587a6116100d35780639fe587a61461032b578063bddb01d11461033e578063bdf2e8c614610351578063d78e5f5e1461036457600080fd5b80638e010652146102f25780639c93336c146103055780639eee96df1461031857600080fd5b80636b811a3c1161016657806375eace641161014057806375eace641461029e578063765cb83a146102b15780638461d8f7146102c45780638da5cb5b146102d757600080fd5b80636b811a3c146102705780636d67fd4214610283578063715018a61461029657600080fd5b8063166e1527116101a2578063166e15271461022157806331b58b23146102425780633d0ebb8914610255578063605361721461026857600080fd5b80630248b34d146101c95780630422dfca146101f957806313ea5d291461020e575b600080fd5b6101dc6101d7366004614e7d565b6103fc565b6040516001600160601b0390911681526020015b60405180910390f35b61020c610207366004614fe7565b61086f565b005b61020c61021c3660046150a2565b610a65565b61023461022f3660046150bd565b610b1d565b6040519081526020016101f0565b61020c610250366004615115565b610d6f565b61020c6102633660046151bd565b610ebc565b61020c610fe6565b61020c61027e366004615268565b611056565b61020c610291366004615281565b611299565b61020c61138b565b61020c6102ac3660046152ba565b6113d1565b61020c6102bf3660046150a2565b6114ea565b61020c6102d2366004615303565b61159b565b6033546040516001600160a01b0390911681526020016101f0565b61020c610300366004615348565b611723565b61020c610313366004615392565b611bf4565b61020c610326366004615435565b611e2d565b61020c610339366004614fe7565b611fac565b61020c61034c3660046154d3565b612220565b61020c61035f36600461554a565b612522565b6102346103723660046150a2565b6125ab565b61020c610385366004615576565b6127ef565b61020c6103983660046155c5565b6129f8565b61020c6103ab3660046155fa565b612b48565b61020c6103be36600461561c565b612d8d565b61020c6103d13660046150a2565b612fcd565b61020c6103e436600461568f565b6130c1565b6102346103f736600461576d565b61338f565b6066546000906001600160a01b0316336001600160a01b03161461043b5760405162461bcd60e51b8152600401610432906157c1565b60405180910390fd5b6001600160a01b0388166104615760405162461bcd60e51b8152600401610432906157de565b606c5460ff8816106104855760405162461bcd60e51b8152600401610432906157fb565b856104a25760405162461bcd60e51b815260040161043290615818565b6001600160601b0384166104c85760405162461bcd60e51b815260040161043290615835565b6070546001600160601b03600160601b909104811690851611156104fe5760405162461bcd60e51b815260040161043290615852565b6070546001600160601b03908116908516101561052d5760405162461bcd60e51b815260040161043290615852565b600060666006018860ff16815481106105485761054861586f565b90600052602060002090600b02019050610561816137b9565b61057d5760405162461bcd60e51b815260040161043290615885565b6105948160010154600160b01b9004600216151590565b6105c65760405162461bcd60e51b815260206004820152600360248201526215155360ea1b6044820152606401610432565b6105d46066828860016137d1565b955060006105e282896137f4565b9050808260050160108282829054906101000a90046001600160601b031661060a91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555060006106838686600161066461065f8d6001600160601b0316886001600160601b031661383390919063ffffffff16565b613852565b60705463ffffffff600160c01b8204811691600160e01b900416613895565b905060006106a661065f6001600160601b03851663ffffffff80861690613a6516565b60058501805491925082916000906106c89084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e828339815191528b8260405161072292919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a161073481846158e3565b925061076b886001600160601b03168a6001600160601b0316856001600160601b0316610761919061590b565b61065f9190615940565b60675460405163a9059cbb60e01b81526001600160a01b038f811660048301526001600160601b038416602483015292975091169063a9059cbb906044016020604051808303816000875af11580156107c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ec9190615954565b50604080516001600160601b038b811682528a811660208301528781168284015283166060820152905160ff8d16916001600160a01b038f16917f4b834bda1e32273014eaae217dcc631b391d25d7d056066e2f008aecd86083e89181900360800190a3610858613a75565b610860613ade565b50505050979650505050505050565b3360009081526074602052604090205460ff1661089e5760405162461bcd60e51b815260040161043290615971565b8151806108d35760405162461bcd60e51b81526020600482015260036024820152624d545960e81b6044820152606401610432565b81518351146108f45760405162461bcd60e51b81526004016104329061598e565b60005b81811015610a5757600060666006018583815181106109185761091861586f565b602002602001015160ff16815481106109335761093361586f565b90600052602060002090600b0201905061096f8483815181106109585761095861586f565b6020026020010151826137f490919063ffffffff16565b600582018054601090610993908490600160801b90046001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550306001600160a01b0316336001600160a01b03167fb487f4e81f33139d200ec61f4269ce6361842282f43bd990d8c7f2dba2c942c88785815181106109fe576109fe61586f565b6020026020010151878681518110610a1857610a1861586f565b6020026020010151604051610a3c92919060ff929092168252602082015260400190565b60405180910390a35080610a4f816159ab565b9150506108f7565b50610a60613a75565b505050565b6033546001600160a01b03163314610a8f5760405162461bcd60e51b8152600401610432906159c6565b6073546001600160a01b0382811691161415610ad35760405162461bcd60e51b815260206004820152600360248201526243484760e81b6044820152606401610432565b607380546001600160a01b0319166001600160a01b0383169081179091556040517f20495ad1c3942659031bc9602fe224bb567644c05306fe5656f04f0c5cd09ef590600090a250565b3360009081526074602052604081205460ff16610b4c5760405162461bcd60e51b815260040161043290615971565b600060666006018560ff1681548110610b6757610b6761586f565b600091825260208220600b909102019150610b8282866137f4565b90506000610b9083866137f4565b90506000610b9e86886159fb565b90506000610bac83856158e3565b60058601549091506001600160601b03808316600160801b909204161015610be65760405162461bcd60e51b815260040161043290615a12565b600585018054849190600090610c069084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b0316610c5091906158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550838560040160008282829054906101000a90046001600160601b0316610c9a91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e828339815191528984604051610cf492919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a16001850154610d15906001600160a01b03168b84613b08565b60408051898152602081018990526001600160a01b038c1691339160ff8d16917f79f5e17a0fd20a621a90272196d71907e6c55c9d9d8dadb013e04c7687d9907b910160405180910390a45093505050505b949350505050565b6033546001600160a01b03163314610d995760405162461bcd60e51b8152600401610432906159c6565b610da4838383613b5a565b606c5460ff851610610dc85760405162461bcd60e51b8152600401610432906157fb565b600060666006018560ff1681548110610de357610de361586f565b90600052602060002090600b02019050836001811115610e0557610e05615a2f565b6003820180546001600160a01b0386166001600160c81b0319909116600160c01b60ff948516026001600160c01b0319161717600160a01b63ffffffff86160217905585167f6f7ad294d40e361770177ff88ae3b62bdab34547e1b530060fe80fcad0fd5953856001811115610e7d57610e7d615a2f565b6040805160ff90921682526001600160a01b038716602083015263ffffffff86169082015260600160405180910390a2610eb5613a75565b5050505050565b6066546001600160a01b0316336001600160a01b031614610eef5760405162461bcd60e51b8152600401610432906157c1565b606a54600090600160c01b900463ffffffff16610f0c8142615a45565b610f169190615a68565b606a54909150600160e01b900463ffffffff16610f4f57606a80546001600160e01b0316600160e01b63ffffffff841602179055610fd4565b606a5463ffffffff42811691610f7691600160c01b8204811691600160e01b900416615a94565b63ffffffff1610610f8657610fd4565b606a54600090610fa390600160e01b900463ffffffff1683615ab3565b9050610fb58989898989898988613cd9565b50606a80546001600160e01b0316600160e01b63ffffffff8416021790555b610fdc613a75565b5050505050505050565b6065546001600160a01b0316336001600160a01b03161461102f5760405162461bcd60e51b815260206004820152600360248201526214d39160ea1b6044820152606401610432565b606554611044906001600160a01b0316613fe2565b606580546001600160a01b0319169055565b6066546001600160a01b0316336001600160a01b0316146110895760405162461bcd60e51b8152600401610432906157c1565b600061109482614034565b6000838152606d60205260409020805491925090600160601b90046001600160601b0316156110eb5760405162461bcd60e51b81526020600482015260036024820152620533e360ec1b6044820152606401610432565b80546001600160601b03166111285760405162461bcd60e51b81526020600482015260036024820152620433d360ec1b6044820152606401610432565b60006066600601836040015160ff16815481106111475761114761586f565b90600052602060002090600b0201905060006066600601846020015160ff16815481106111765761117661586f565b90600052602060002090600b0201905061118f826137b9565b6111ab5760405162461bcd60e51b815260040161043290615885565b6111b4816137b9565b6111d05760405162461bcd60e51b815260040161043290615885565b82546001600160601b031660006111e783836140b7565b85546001600160601b0319168655865160695460715492935061121b9286929185916001600160a01b0391821691166140f2565b85600001516001600160a01b0316877f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f886020015184866040516112809392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a3611290613a75565b50505050505050565b6073546001600160a01b0316336001600160a01b031614806112c557506033546001600160a01b031633145b6112e15760405162461bcd60e51b815260040161043290615ad0565b6070546001600160601b03838116911614158061131357506070546001600160601b03828116600160601b9092041614155b1561137f57607080546001600160601b038481166001600160c01b03199092168217600160601b918516918202179092556040805191825260208201929092527f715d9f598cbf93067f06e0db8773770d07a5f09b2c8f15ebbe8a4ed971ac1362910160405180910390a15b611387613a75565b5050565b6033546001600160a01b031633146113b55760405162461bcd60e51b8152600401610432906159c6565b606580546001600160a01b03191690556113cf6000613fe2565b565b6066546001600160a01b0316336001600160a01b0316146114045760405162461bcd60e51b8152600401610432906157c1565b60405163015e626960e51b8152606660048201526001600160a01b038416602482015260ff831660448201526001600160601b03821660648201527303636b99aaf33e30acca596cc6de68ef3fabd64990632bcc4d209060840160006040518083038186803b15801561147657600080fd5b505af415801561148a573d6000803e3d6000fd5b5050604080516001600160a01b038716815260ff861660208201526001600160601b0385168183015290517fa76ab15d99fce78af68594e15ef49d09c577827bb14729563a5746fe0e6ba4039350908190036060019150a1610a60613a75565b6073546001600160a01b0316336001600160a01b0316148061151657506033546001600160a01b031633145b6115325760405162461bcd60e51b815260040161043290615ad0565b6040516317066a5760e21b81526001600160a01b038216600482015273912ce59144191c1204e64559fe8253a0e49e654890635c19a95c90602401600060405180830381600087803b15801561158757600080fd5b505af1158015610eb5573d6000803e3d6000fd5b6033546001600160a01b031633146115c55760405162461bcd60e51b8152600401610432906159c6565b606c5460ff8416106115e95760405162461bcd60e51b8152600401610432906157fb565b61162660666006018460ff16815481106116055761160561586f565b90600052602060002090600b0201600190810154600160b01b900416151590565b1561166f57606a805463ffffffff838116600160a01b0263ffffffff60a01b19918616600160801b029190911667ffffffffffffffff60801b19909216919091171790556116d6565b600060666006018460ff168154811061168a5761168a61586f565b600091825260209091206009600b9092020101805463ffffffff848116600160e01b026001600160e01b03918716600160c01b02919091166001600160c01b0390921691909117179055505b6040805163ffffffff80851682528316602082015260ff8516917f318cd38ce4d3f61a5e60de4f51d143ee426378b241efbe076180870b3394405b910160405180910390a2610a60613a75565b6066546001600160a01b0316336001600160a01b0316146117565760405162461bcd60e51b8152600401610432906157c1565b826117735760405162461bcd60e51b815260040161043290615818565b600061177e85614034565b80519091506001600160a01b03166117a85760405162461bcd60e51b8152600401610432906157de565b6020810151606c5460ff909116106117d25760405162461bcd60e51b8152600401610432906157fb565b6040810151606c5460ff909116106117fc5760405162461bcd60e51b8152600401610432906157fb565b60006066600601826040015160ff168154811061181b5761181b61586f565b90600052602060002090600b0201905060006066600601836020015160ff168154811061184a5761184a61586f565b90600052602060002090600b02019050611863826137b9565b61187f5760405162461bcd60e51b815260040161043290615885565b611888816137b9565b6118a45760405162461bcd60e51b815260040161043290615885565b6000878152606d602052604090206118be606684876141fe565b94506118cc606683886141fe565b955060006118e08285876060015189614344565b8254909150600160601b90046001600160601b0316156119095761190982858760600151614414565b600061192461065f6001600160601b03848116908b16614486565b83549091506001600160601b038083169116101561196a5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610432565b8254819084906000906119879084906001600160601b03166158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160008282829054906101000a90046001600160601b03166119d191906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160108282829054906101000a90046001600160601b0316611a1b91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152866020015182604051611a7992919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a1506000611a8e848a6137f4565b83549091506001600160601b0380831691161015611ad45760405162461bcd60e51b8152602060048201526003602482015262433c5760e81b6044820152606401610432565b8254611aea9082906001600160601b03166158e3565b83546001600160601b0319166001600160601b03919091161783558551606954607154611b2992879290918d916001600160a01b0390811691166140f2565b611b4183876040015160ff1688606001518b8b61449b565b611b735760405162461bcd60e51b815260206004820152600360248201526221494d60e81b6044820152606401610432565b85600001516001600160a01b03168a7f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f88602001518c85604051611bd89392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a3611be8613a75565b50505050505050505050565b6033546001600160a01b03163314611c1e5760405162461bcd60e51b8152600401610432906159c6565b606c5460ff8a1610611c425760405162461bcd60e51b8152600401610432906157fb565b87611c755760405162461bcd60e51b815260206004820152600360248201526253594d60e81b6044820152606401610432565b600060666006018a60ff1681548110611c9057611c9061586f565b90600052602060002090600b02019050888160000181905550878160020160006101000a81548163ffffffff021916908363ffffffff160217905550868160020160046101000a81548163ffffffff021916908363ffffffff160217905550858160020160106101000a81548163ffffffff021916908363ffffffff1602179055508481600501600c6101000a81548163ffffffff021916908363ffffffff160217905550838160020160086101000a81548163ffffffff021916908363ffffffff1602179055508281600201600c6101000a81548163ffffffff021916908363ffffffff160217905550818160090160146101000a81548163ffffffff021916908363ffffffff1602179055508960ff167f8a545db87c099fd21f0a6729013aaa68a315a726271eb8239bcc30170a4ce40b8a8a8a8a8a8a8a8a604051611e1d98979695949392919097885263ffffffff9687166020890152948616604088015292851660608701529084166080860152831660a0850152821660c08401521660e08201526101000190565b60405180910390a2611be8613a75565b6066546001600160a01b0316336001600160a01b031614611e605760405162461bcd60e51b8152600401610432906157c1565b604051636e4d522360e11b8152606660048201526001600160a01b038916602482015260ff8089166044830152871660648201526001600160601b03808716608483015280861660a483015260c4820185905280841660e483015282166101048201526000907303636b99aaf33e30acca596cc6de68ef3fabd6499063dc9aa4469061012401602060405180830381865af4158015611f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f279190615aed565b6040805160ff8b811682528a1660208201526001600160601b038681168284015285811660608301528981166080830152831660a082015290519192506001600160a01b038b16917feb9bf2cb51f41dfacd571a3fa0742757c031317644527fd26db5cb7516df197b9181900360c00190a2611fa1613a75565b505050505050505050565b3360009081526074602052604090205460ff16611fdb5760405162461bcd60e51b815260040161043290615971565b8151806120105760405162461bcd60e51b81526020600482015260036024820152624d545960e81b6044820152606401610432565b81518351146120315760405162461bcd60e51b81526004016104329061598e565b60005b81811015610a5757600060666006018583815181106120555761205561586f565b602002602001015160ff16815481106120705761207061586f565b90600052602060002090600b020190506120b6338584815181106120965761209661586f565b602090810291909101015160018401546001600160a01b03169190613b08565b60006120e48584815181106120cd576120cd61586f565b6020026020010151836137f490919063ffffffff16565b60058301549091506001600160601b03808316600160801b9092041610156121345760405162461bcd60e51b815260206004820152600360248201526213931560ea1b6044820152606401610432565b808260050160108282829054906101000a90046001600160601b031661215a91906158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550336001600160a01b0316306001600160a01b03167fb487f4e81f33139d200ec61f4269ce6361842282f43bd990d8c7f2dba2c942c88886815181106121c5576121c561586f565b60200260200101518887815181106121df576121df61586f565b602002602001015160405161220392919060ff929092168252602082015260400190565b60405180910390a350508080612218906159ab565b915050612034565b6033546001600160a01b0316331461224a5760405162461bcd60e51b8152600401610432906159c6565b620186a08463ffffffff16106122885760405162461bcd60e51b8152602060048201526003602482015262463e3160e81b6044820152606401610432565b620186a08363ffffffff16106122c65760405162461bcd60e51b8152602060048201526003602482015262463e3160e81b6044820152606401610432565b620186a08263ffffffff16106123045760405162461bcd60e51b8152602060048201526003602482015262443e3160e81b6044820152606401610432565b606a5463ffffffff868116600160c01b909204161461238757606a5460408051600160c01b90920463ffffffff9081168352871660208301527f41c014a2dd74f3d1ada70fc516f134f02e2ffdd2f208320663300a9e093c78e5910160405180910390a1606a805463ffffffff60c01b1916600160c01b63ffffffff8816021790555b60705463ffffffff858116600160c01b909204161415806123ba575060705463ffffffff848116600160e01b9092041614155b1561243357607080546001600160c01b0316600160c01b63ffffffff8781169182026001600160e01b031692909217600160e01b928716928302179092556040805192835260208301919091527fb97a93995fbff5fafc7defd9c41d52a41e402d34ef57abcf3eae22042910df9e910160405180910390a15b60715463ffffffff838116600160c01b90920416146124a4576071805463ffffffff60c01b1916600160c01b63ffffffff8516908102919091179091556040519081527f922a86287dffe250b116e287f13cec73d7aebbca29e35b545f13fafb203c1fa59060200160405180910390a15b6072546001600160601b03828116600160a01b909204161461251a57607280546001600160a01b0316600160a01b6001600160601b038416908102919091179091556040519081527f90d2ff51f70c1805e062f1e89009c2b4a86533de78b2145adf26f95f55fa1d999060200160405180910390a15b610eb5613a75565b6033546001600160a01b0316331461254c5760405162461bcd60e51b8152600401610432906159c6565b6001600160a01b038216600081815260746020908152604091829020805460ff191685151590811790915591519182527f8a24dcf97d36a7d6c53760ca365425426e042611881df1138051227059fa027d910160405180910390a25050565b6066546000906001600160a01b0316336001600160a01b0316146125e15760405162461bcd60e51b8152600401610432906157c1565b6001600160a01b03821661261d5760405162461bcd60e51b81526020600482015260036024820152622921ab60e91b6044820152606401610432565b606c5460005b818110156127e7576000606660060182815481106126435761264361586f565b60009182526020909120606954600b9092020160018101549092506001600160a01b03918216911614156127d4576071546072546000916126a59161065f91600160e01b900463ffffffff1690600160a01b90046001600160601b031661590b565b60058301549091506001600160601b03808316600160801b9092041610156126df5760405162461bcd60e51b815260040161043290615a12565b808260050160108282829054906101000a90046001600160601b031661270591906158e3565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061273c81836140b790919063ffffffff16565b60715460408051600160e01b90920463ffffffff168252602082018390529196506001600160a01b038816917f4fd749fd2a8d968a01b106b5d56dfff72958e32f65e3b1666d92f4607f7c04e5910160405180910390a2607180546001600160e01b0381169091556069546127c3918491899189916001600160a01b0390811691166140f2565b6127cb613a75565b50505050919050565b50806127df816159ab565b915050612623565b50505b919050565b3360009081526074602052604090205460ff1661281e5760405162461bcd60e51b815260040161043290615971565b600060666006018560ff16815481106128395761283961586f565b600091825260208220600b90910201915061285482866137f4565b9050600061286283866137f4565b9050600061287084866137f4565b9050600061287e83856158b8565b60058601805491925084916000906128a09084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b03166128ea91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550818461291a91906158b8565b6004860180546000906129379084906001600160601b03166158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152898460405161299192919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a160408051898152602081018990529081018790526001600160a01b038b1690339060ff8c16907f8b0e10e6a23b017a84b2cd5ba0a1cc73e4d5903cc3fc5cfedfd24dcb639761a99060600160405180910390a450505050505050505050565b6072546001600160a01b0316612a365760405162461bcd60e51b815260206004820152600360248201526215931560ea1b6044820152606401610432565b60005b8151811015612b3c576000828281518110612a5657612a5661586f565b6020908102919091010151604051620acde760ea1b81526066600482015260ff821660248201529091506000907303636b99aaf33e30acca596cc6de68ef3fabd64990632b379c0090604401602060405180830381865af4158015612abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae39190615aed565b6040516001600160601b038216815290915060ff8316907ff81b718ab776e0011332f4662259a37be2abe9511d53ca54206f4d71124515eb9060200160405180910390a250508080612b34906159ab565b915050612a39565b50612b45613a75565b50565b6066546001600160a01b0316336001600160a01b031614612b7b5760405162461bcd60e51b8152600401610432906157c1565b6000612b8683614034565b80519091506001600160a01b0316612bb05760405162461bcd60e51b8152600401610432906157de565b6020810151606c5460ff90911610612bda5760405162461bcd60e51b8152600401610432906157fb565b6040810151606c5460ff90911610612c045760405162461bcd60e51b8152600401610432906157fb565b81612c215760405162461bcd60e51b815260040161043290615818565b6000838152606d602052604080822090830151606c8054929392909160ff16908110612c4f57612c4f61586f565b90600052602060002090600b0201905060006066600601846020015160ff1681548110612c7e57612c7e61586f565b90600052602060002090600b02019050612c97826137b9565b612cb35760405162461bcd60e51b815260040161043290615885565b612cbc816137b9565b612cd85760405162461bcd60e51b815260040161043290615885565b6000612ce482876137f4565b845490915081908590600090612d049084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600001516001600160a01b0316877f94ab403b9852b1c10901b0928c9b5692d2d229c8e666bae8f77cac8814ead64f876020015189856040516112809392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b6033546001600160a01b03163314612db75760405162461bcd60e51b8152600401610432906159c6565b60128460ff161115612df15760405162461bcd60e51b815260206004820152600360248201526244434d60e81b6044820152606401610432565b606c5460ff871614612e155760405162461bcd60e51b815260040161043290615b0a565b60ff8660ff1610612e4e5760405162461bcd60e51b815260206004820152600360248201526211931360ea1b6044820152606401610432565b84612e815760405162461bcd60e51b815260206004820152600360248201526253594d60e81b6044820152606401610432565b606c80546001018082556000828152919060ff8916908110612ea557612ea561586f565b60009182526020909120600b9091020186815560018101805460ff888116600160a81b0260ff60a81b19918c16600160a01b029190911661ffff60a01b1990921691909117179055905083612efb576000612efe565b60015b6001820180546001600160a01b038681166001600160a01b031966ffffffffffffff9590951666fffffffffffffe600160b01b8086049190911691909117028516600168ffffffffffffff000160a01b03199093169290921782179092556009840180549286169290931682179092556040805189815260ff8981166020830152881515828401526060820194909452608081019290925251918916917ffdb7e1b6147dbcaa8b4b4f67f5f4324921f27271ae3463824452556451774d4d9181900360a00190a2611290613a75565b6033546001600160a01b03163314612ff75760405162461bcd60e51b8152600401610432906159c6565b6001600160a01b0381166130335760405162461bcd60e51b815260206004820152600360248201526204f3d360ec1b6044820152606401610432565b6033546001600160a01b03828116911614156130775760405162461bcd60e51b81526020600482015260036024820152624f3d4f60e81b6044820152606401610432565b606580546001600160a01b0319166001600160a01b0383169081179091556040517fb501f5dc3610d10a179d924f686fc89ddcdd3abb05afd7a1baeead166b2f122c90600090a250565b6073546001600160a01b0316336001600160a01b031614806130ed57506033546001600160a01b031633145b6131095760405162461bcd60e51b815260040161043290615ad0565b606c5460ff8c161061312d5760405162461bcd60e51b8152600401610432906157fb565b600060666006018c60ff16815481106131485761314861586f565b90600052602060002090600b0201905061316f81600190810154600160b01b900416151590565b6131a75785156131a75760405162461bcd60e51b815260206004820152600360248201526229aa2160e91b6044820152606401610432565b6001810154600160b01b900466ffffffffffffff168b6131c85760006131cc565b6101005b6101001991909116178a6131e15760006131e6565b620100005b62010000199190911617896131fc576000613202565b63010000005b630100000019919091161788613219576000613220565b6401000000005b64010000000019919091161787613238576000613240565b650100000000005b6501000000000019919091161786613259576000613262565b66010000000000005b66010000000000001991909116178561327c57600061327f565b60025b60018301805466ffffffffffffff60b01b1916600160b01b66fffffffffffffd851666ffffffffffffff8516179081029190911790915560038401805463ffffffff60c81b1916600160c81b63ffffffff8a16908102919091179091556006850180546001600160601b0319166001600160601b038981169182179092556007870180546bffffffffffffffffffffffff60601b1916600160601b938a16938402179055604080519485526020850193909352918301919091526060820152600219909216179060ff8e16907fc2584bc16fc882ad266db46fe5ccb39d246e07c58825b8cbbefbfb802d3092c19060800160405180910390a2613380613a75565b50505050505050505050505050565b6066546000906001600160a01b0316336001600160a01b0316146133c55760405162461bcd60e51b8152600401610432906157c1565b6001600160a01b0388166133eb5760405162461bcd60e51b8152600401610432906157de565b606c5460ff87161061340f5760405162461bcd60e51b8152600401610432906157fb565b6001600160601b0384166134355760405162461bcd60e51b815260040161043290615835565b6070546001600160601b03600160601b9091048116908516111561346b5760405162461bcd60e51b815260040161043290615852565b6070546001600160601b03908116908516101561349a5760405162461bcd60e51b815260040161043290615852565b6001600160601b0387166134c05760405162461bcd60e51b815260040161043290615818565b600060666006018760ff16815481106134db576134db61586f565b90600052602060002090600b020190506134f4816137b9565b6135105760405162461bcd60e51b815260040161043290615885565b6135278160010154600160b01b9004600216151590565b6135595760405162461bcd60e51b815260206004820152600360248201526215155360ea1b6044820152606401610432565b6135676066828860006137d1565b95506000613596876001600160601b0316876001600160601b03168b6001600160601b0316610761919061590b565b90506000806135ca8787600061066461065f8e6001600160601b0316896001600160601b031661383390919063ffffffff16565b90506135eb61065f6001600160601b03851663ffffffff80851690613a6516565b6005850180549193508392509060009061360f9084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152898260405161366992919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a161367b81836158e3565b60058401549092506001600160601b03600160801b909104811690831611156136b65760405162461bcd60e51b815260040161043290615a12565b818360050160108282829054906101000a90046001600160601b03166136dc91906158e3565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061371382846140b790919063ffffffff16565b6069546071549195506137389185918e9188916001600160a01b0390811691166140f2565b604080516001600160601b038a8116825289811660208301528c81168284015283166060820152905160ff8b16916001600160a01b038e16917fd20afc5b5a4edaae7399b3eb4737f26d8688e6a86e1b525ffbe063574583b1aa9181900360800190a36137a3613a75565b6137ab613ade565b505050979650505050505050565b60010154600160b01b90046501000000000016151590565b60006137de8585856141fe565b92506137eb848484614521565b95945050505050565b600182015460009061382a9061381590600160a81b900460ff166012615b27565b61382090600a615c2e565b61065f908461590b565b90505b92915050565b6000670de0b6b3a7640000613848838561590b565b61382a9190615940565b60006001600160601b038211156138915760405162461bcd60e51b8152602060048201526003602482015262279c9b60e91b6044820152606401610432565b5090565b60008085156138af576138a885896158b8565b90506138ee565b846001600160601b0316886001600160601b031610156138e15760405162461bcd60e51b815260040161043290615a12565b6138eb85896158e3565b90505b6000876001600160601b0316896001600160601b0316116139185761391389896158e3565b613922565b613922888a6158e3565b90506000886001600160601b0316836001600160601b03161161394e57613949838a6158e3565b613958565b61395889846158e3565b90506001600160601b03891661397357859350505050613a5b565b816001600160601b0316816001600160601b031610156139f75760006139c68a6001600160601b0316846001600160601b03168863ffffffff166139b7919061590b565b6139c19190615940565b6145db565b90508063ffffffff168763ffffffff16116139e25760006139ec565b6139ec8188615ab3565b945050505050613a5b565b60006002613a0583856158b8565b613a0f9190615c3d565b9050613a1b818b614617565b90506000613a478b6001600160601b0316836001600160601b03168963ffffffff166139b7919061590b565b9050613a538189615a94565b955050505050505b9695505050505050565b6000620186a0613848838561590b565b6071805463ffffffff600160a01b80830482166001018216810263ffffffff60a01b1990931692909217928390556040519190920490911681527f974288fc79dc8b8b1fe9f1f0b8f5738873e53e07d3122c5e3c02b834c018d6cb9060200160405180910390a1565b6071805463ffffffff600160e01b8083048216600101909116026001600160e01b03909116179055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610a60908490614640565b620186a08163ffffffff161115613b995760405162461bcd60e51b8152602060048201526003602482015262443e3160e81b6044820152606401610432565b6001836001811115613bad57613bad615a2f565b1415610a60576000829050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c1a9190615c57565b60ff16600814613c525760405162461bcd60e51b815260206004820152600360248201526204288760eb1b6044820152606401610432565b6000816001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cb69190615c74565b13613cd35760405162461bcd60e51b815260040161043290615835565b50505050565b858414613cf85760405162461bcd60e51b81526004016104329061598e565b858214613d175760405162461bcd60e51b81526004016104329061598e565b606a546000908190613d419063ffffffff600160801b8204811691600160a01b9004168c86614712565b606c5491935091506000805b8260ff168160ff16101561338057600060666006018260ff1681548110613d7657613d7661586f565b90600052602060002090600b02019050613d9d81600190810154600160b01b900416151590565b15613da85750613fd0565b60ff83168c11613dca5760405162461bcd60e51b81526004016104329061598e565b8160ff168d8d8560ff16818110613de357613de361586f565b9050602002016020810190613df89190615c8d565b60ff1614613e185760405162461bcd60e51b815260040161043290615b0a565b60098101546000908190613e6d9063ffffffff600160c01b8204811691600160e01b9004168f8f60ff8a16818110613e5257613e5261586f565b9050602002016020810190613e679190615caa565b8c614712565b600a850180549294509092508291600090613e929084906001600160801b0316615cc5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506000613eed6066858e8e8a60ff16818110613ed357613ed361586f565b9050602002016020810190613ee89190615ce7565b6141fe565b9050613f13613f0e6001600160801b038a166001600160601b038416613833565b6147b9565b600a85018054601090613f37908490600160801b90046001600160801b0316615cc5565b82546101009290920a6001600160801b03818102199093169183160217909155600a8601546040805163ffffffff808916825283851660208301528e1691810191909152600160801b909104909116606082015260ff871692507f59b0ca320d4e4d85aa96c127c03fb78b50d499a8ccf1987b628b97b9acb66cac915060800160405180910390a2613fca600186615d04565b94505050505b80613fda81615d29565b915050613d4d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60408051608081018252600080825260208201819052918101829052606081019190915268ffffffffffffffffff8216156140815760405162461bcd60e51b815260040161043290615b0a565b606082811c825260ff605884901c81166020840152605084901c8116604084015260489390931c90921615159181019190915290565b60018201546000906140d490600160a81b900460ff166012615b27565b6140df90600a615c2e565b61382a906001600160601b038416615940565b60018501546001600160a01b03838116911614156141e55760405163a9059cbb60e01b81526001600160a01b0382811660048301526024820185905283169063a9059cbb906044016020604051808303816000875af1158015614159573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061417d9190615954565b506040516339f4769360e01b81526001600160a01b038581166004830152602482018590528216906339f4769390604401600060405180830381600087803b1580156141c857600080fd5b505af11580156141dc573d6000803e3d6000fd5b50505050610eb5565b6001850154610eb5906001600160a01b03168585613b08565b60006001600160601b0382166142265760405162461bcd60e51b815260040161043290615835565b60016003840154600160c01b900460ff16600181111561424857614248615a2f565b600181111561425957614259615a2f565b141561428a576003830154600090614279906001600160a01b03166147f8565b9050614286848483614899565b9250505b6142a78360010154600160b01b9004660100000000000016151590565b1561433a576000670de0b6b3a7640000836001600160601b0316116142dd576142d883670de0b6b3a76400006158e3565b6142ef565b6142ef670de0b6b3a7640000846158e3565b600b8601546001600160601b0391909116915060009061432290600160c01b900463ffffffff166509184e72a00061590b565b905080821161433757670de0b6b3a764000093505b50505b50805b9392505050565b8354600090600160601b90046001600160601b031661436557506000610d67565b600083156143b9576001860154600a860154614394916001600160801b03600160601b90910481169116615d49565b6001600160801b031690506143b2816001600160601b038516613833565b90506143f3565b6001860154600a8601546143e7916001600160801b03600160601b909104811691600160801b900416615d49565b6001600160801b031690505b8554613a5b9061065f908390600160601b90046001600160601b0316613833565b801561444d5750600a01546001919091018054600160601b600160e01b0319166001600160801b03909216600160601b02919091179055565b50600a01546001919091018054600160601b600160e01b031916600160801b9092046001600160801b0316600160601b02919091179055565b60008161384884670de0b6b3a764000061590b565b60008060666006018663ffffffff16815481106144ba576144ba61586f565b90600052602060002090600b020190506000806144f1838a898c600001600c9054906101000a90046001600160601b031689614a40565b60028501549193509150614514908a908890889063ffffffff1686866000614bda565b9998505050505050505050565b6003830154600090600160c81b900463ffffffff1661454157508161433d565b60038401546000906145729061065f906001600160601b0387169063ffffffff600160c81b909104811690613a6516565b9050600183600181111561458857614588615a2f565b14156145d157806001600160601b0316846001600160601b0316116145bf5760405162461bcd60e51b815260040161043290615835565b6145c981856158e3565b91505061433d565b6145c981856158b8565b600063ffffffff8211156138915760405162461bcd60e51b815260206004820152600360248201526227999960e91b6044820152606401610432565b6000816001600160601b0316836001600160601b03161115614639578161382a565b5090919050565b6000614695826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614cb29092919063ffffffff16565b805190915015610a6057808060200190518101906146b39190615954565b610a605760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610432565b600080620186a08463ffffffff1611156147545760405162461bcd60e51b8152602060048201526003602482015262553e3160e81b6044820152606401610432565b61476d6139c163ffffffff8681169080891690613a6516565b91506147798287614cc1565b91506147ae61708061479463ffffffff86811690861661590b565b6147a4906509184e72a00061590b565b613f0e9190615940565b905094509492505050565b60006001600160801b038211156138915760405162461bcd60e51b815260206004820152600360248201526227989960e91b6044820152606401610432565b600080826001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061485d9190615c74565b90506000811361487f5760405162461bcd60e51b815260040161043290615835565b61488e6402540be40082615d69565b905061433d81613852565b6003830154600090600160a01b900463ffffffff166148b957508061433d565b60038401546000906148e6906001600160601b0385169063ffffffff600160a01b909104811690613a6516565b9050600061490061065f836001600160601b0387166159fb565b9050806001600160601b0316856001600160601b03161015614990576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b6149a661065f836001600160601b038716615dee565b9050806001600160601b0316856001600160601b03161115614a36576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b5092949350505050565b6000806001600160601b038416614a5c57506000905080614bd0565b6000836001600160601b031611614a855760405162461bcd60e51b815260040161043290615835565b84614aa35760018601546001600160601b0390811690841610614ab8565b60018601546001600160601b03908116908416115b60018701549092506000906001600160601b039081169085161015614af5576001870154614af09085906001600160601b03166158e3565b614b0d565b6001870154614b0d906001600160601b0316856158e3565b9050828015614b4e575060028801548754614b3f9163ffffffff600160601b909104811691600160c01b900416615a94565b63ffffffff164263ffffffff16105b8015614ba1575060028801546001880154614b8c9161065f916001600160601b03169063ffffffff68010000000000000000909104811690613a6516565b6001600160601b0316816001600160601b0316105b15614bb3575060009150819050614bd0565b614bcc61065f6001600160601b03838116908816613833565b9150505b9550959350505050565b86546000908190620186a090670de0b6b3a76400009063ffffffff891690614c15906001600160601b03808d1691600160601b90041661590b565b614c1f919061590b565b614c299190615940565b614c339190615940565b9050614c486001600160601b03841682615dee565b8954909150600090614c67906001600160601b03908116908b16613833565b90508515614c8e5781614c836001600160601b03871683615dee565b101592505050614ca7565b614ca16001600160601b03861683615dee565b11159150505b979650505050505050565b6060610d678484600085614cdd565b60008163ffffffff168363ffffffff161015614639578161382a565b606082471015614d3e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610432565b6001600160a01b0385163b614d955760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610432565b600080866001600160a01b03168587604051614db19190615e32565b60006040518083038185875af1925050503d8060008114614dee576040519150601f19603f3d011682016040523d82523d6000602084013e614df3565b606091505b5091509150614ca782828660608315614e0d57508161433d565b825115614e1d5782518084602001fd5b8160405162461bcd60e51b81526004016104329190615e4e565b80356001600160a01b03811681146127ea57600080fd5b60ff81168114612b4557600080fd5b6001600160601b0381168114612b4557600080fd5b80356127ea81614e5d565b600080600080600080600060e0888a031215614e9857600080fd5b614ea188614e37565b96506020880135614eb181614e4e565b9550604088013594506060880135614ec881614e5d565b93506080880135614ed881614e5d565b925060a0880135614ee881614e5d565b915060c0880135614ef881614e5d565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614f4757614f47614f08565b604052919050565b600067ffffffffffffffff821115614f6957614f69614f08565b5060051b60200190565b600082601f830112614f8457600080fd5b81356020614f99614f9483614f4f565b614f1e565b82815260059290921b84018101918181019086841115614fb857600080fd5b8286015b84811015614fdc578035614fcf81614e4e565b8352918301918301614fbc565b509695505050505050565b60008060408385031215614ffa57600080fd5b823567ffffffffffffffff8082111561501257600080fd5b61501e86838701614f73565b935060209150818501358181111561503557600080fd5b85019050601f8101861361504857600080fd5b8035615056614f9482614f4f565b81815260059190911b8201830190838101908883111561507557600080fd5b928401925b828410156150935783358252928401929084019061507a565b80955050505050509250929050565b6000602082840312156150b457600080fd5b61382a82614e37565b600080600080608085870312156150d357600080fd5b6150dc85614e37565b935060208501356150ec81614e4e565b93969395505050506040820135916060013590565b803563ffffffff811681146127ea57600080fd5b6000806000806080858703121561512b57600080fd5b843561513681614e4e565b935060208501356002811061514a57600080fd5b925061515860408601614e37565b915061516660608601615101565b905092959194509250565b60008083601f84011261518357600080fd5b50813567ffffffffffffffff81111561519b57600080fd5b6020830191508360208260051b85010111156151b657600080fd5b9250929050565b60008060008060008060006080888a0312156151d857600080fd5b6151e188615101565b9650602088013567ffffffffffffffff808211156151fe57600080fd5b61520a8b838c01615171565b909850965060408a013591508082111561522357600080fd5b61522f8b838c01615171565b909650945060608a013591508082111561524857600080fd5b506152558a828b01615171565b989b979a50959850939692959293505050565b60006020828403121561527a57600080fd5b5035919050565b6000806040838503121561529457600080fd5b823561529f81614e5d565b915060208301356152af81614e5d565b809150509250929050565b6000806000606084860312156152cf57600080fd5b6152d884614e37565b925060208401356152e881614e4e565b915060408401356152f881614e5d565b809150509250925092565b60008060006060848603121561531857600080fd5b833561532381614e4e565b925061533160208501615101565b915061533f60408501615101565b90509250925092565b6000806000806080858703121561535e57600080fd5b8435935060208501359250604085013561537781614e5d565b9150606085013561538781614e5d565b939692955090935050565b60008060008060008060008060006101208a8c0312156153b157600080fd5b89356153bc81614e4e565b985060208a013597506153d160408b01615101565b96506153df60608b01615101565b95506153ed60808b01615101565b94506153fb60a08b01615101565b935061540960c08b01615101565b925061541760e08b01615101565b91506154266101008b01615101565b90509295985092959850929598565b600080600080600080600080610100898b03121561545257600080fd5b61545b89614e37565b9750602089013561546b81614e4e565b9650604089013561547b81614e4e565b9550606089013561548b81614e5d565b9450608089013561549b81614e5d565b935060a0890135925060c08901356154b281614e5d565b915060e08901356154c281614e5d565b809150509295985092959890939650565b600080600080600060a086880312156154eb57600080fd5b6154f486615101565b945061550260208701615101565b935061551060408701615101565b925061551e60608701615101565b9150608086013561552e81614e5d565b809150509295509295909350565b8015158114612b4557600080fd5b6000806040838503121561555d57600080fd5b61556683614e37565b915060208301356152af8161553c565b600080600080600060a0868803121561558e57600080fd5b61559786614e37565b945060208601356155a781614e4e565b94979496505050506040830135926060810135926080909101359150565b6000602082840312156155d757600080fd5b813567ffffffffffffffff8111156155ee57600080fd5b610d6784828501614f73565b6000806040838503121561560d57600080fd5b50508035926020909101359150565b60008060008060008060c0878903121561563557600080fd5b863561564081614e4e565b955060208701359450604087013561565781614e4e565b935060608701356156678161553c565b925061567560808801614e37565b915061568360a08801614e37565b90509295509295509295565b60008060008060008060008060008060006101608c8e0312156156b157600080fd5b8b356156bc81614e4e565b9a5060208c01356156cc8161553c565b995060408c01356156dc8161553c565b985060608c01356156ec8161553c565b975060808c01356156fc8161553c565b965060a08c013561570c8161553c565b955060c08c013561571c8161553c565b945060e08c013561572c8161553c565b935061573b6101008d01615101565b92506101208c013561574c81614e5d565b915061575b6101408d01614e72565b90509295989b509295989b9093969950565b600080600080600080600060e0888a03121561578857600080fd5b61579188614e37565b965060208801356157a181614e5d565b955060408801356157b181614e4e565b94506060880135614ec881614e5d565b602080825260039082015262424f4b60e81b604082015260600190565b6020808252600390820152620543d360ec1b604082015260600190565b6020808252600390820152621314d560ea1b604082015260600190565b6020808252600390820152620413d360ec1b604082015260600190565b6020808252600390820152620503d360ec1b604082015260600190565b6020808252600390820152624d504f60e81b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b602080825260039082015262454e4160e81b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60006001600160601b038083168185168083038211156158da576158da6158a2565b01949350505050565b60006001600160601b0383811690831681811015615903576159036158a2565b039392505050565b6000816000190483118215151615615925576159256158a2565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261594f5761594f61592a565b500490565b60006020828403121561596657600080fd5b815161433d8161553c565b6020808252600390820152624c514d60e81b604082015260600190565b6020808252600390820152622622a760e91b604082015260600190565b60006000198214156159bf576159bf6158a2565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600082821015615a0d57615a0d6158a2565b500390565b6020808252600390820152624c495160e81b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b600063ffffffff80841680615a5c57615a5c61592a565b92169190910492915050565b600063ffffffff80831681851681830481118215151615615a8b57615a8b6158a2565b02949350505050565b600063ffffffff8083168185168083038211156158da576158da6158a2565b600063ffffffff83811690831681811015615903576159036158a2565b60208082526003908201526253214d60e81b604082015260600190565b600060208284031215615aff57600080fd5b815161433d81614e5d565b60208082526003908201526210525160ea1b604082015260600190565b600060ff821660ff841680821015615b4157615b416158a2565b90039392505050565b600181815b80851115615b85578160001904821115615b6b57615b6b6158a2565b80851615615b7857918102915b93841c9390800290615b4f565b509250929050565b600082615b9c5750600161382d565b81615ba95750600061382d565b8160018114615bbf5760028114615bc957615be5565b600191505061382d565b60ff841115615bda57615bda6158a2565b50506001821b61382d565b5060208310610133831016604e8410600b8410161715615c08575081810a61382d565b615c128383615b4a565b8060001904821115615c2657615c266158a2565b029392505050565b600061382a60ff841683615b8d565b60006001600160601b0380841680615a5c57615a5c61592a565b600060208284031215615c6957600080fd5b815161433d81614e4e565b600060208284031215615c8657600080fd5b5051919050565b600060208284031215615c9f57600080fd5b813561433d81614e4e565b600060208284031215615cbc57600080fd5b61382a82615101565b60006001600160801b038083168185168083038211156158da576158da6158a2565b600060208284031215615cf957600080fd5b813561433d81614e5d565b600060ff821660ff84168060ff03821115615d2157615d216158a2565b019392505050565b600060ff821660ff811415615d4057615d406158a2565b60010192915050565b60006001600160801b0383811690831681811015615903576159036158a2565b60006001600160ff1b0381841382841380821686840486111615615d8f57615d8f6158a2565b600160ff1b6000871282811687830589121615615dae57615dae6158a2565b60008712925087820587128484161615615dca57615dca6158a2565b87850587128184161615615de057615de06158a2565b505050929093029392505050565b60008219821115615e0157615e016158a2565b500190565b60005b83811015615e21578181015183820152602001615e09565b83811115613cd35750506000910152565b60008251615e44818460208701615e06565b9190910192915050565b6020815260008251806020840152615e6d816040850160208701615e06565b601f01601f1916919091016040019291505056fefe5353ab8f25b751655ce462adb7567c9e4dff8da72a5f26094ef0e9300d737ca264697066735822122030a9611243b401474347eae338b8e5bd5eb24b403fd2336620912b9e7803b51b64736f6c634300080a0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80638e010652116100f9578063dce674ed11610097578063ecca2a5511610071578063ecca2a55146103b0578063f2fde38b146103c3578063f54de365146103d6578063fdd25cda146103e957600080fd5b8063dce674ed14610377578063de19a8bc1461038a578063dea9b4641461039d57600080fd5b80639fe587a6116100d35780639fe587a61461032b578063bddb01d11461033e578063bdf2e8c614610351578063d78e5f5e1461036457600080fd5b80638e010652146102f25780639c93336c146103055780639eee96df1461031857600080fd5b80636b811a3c1161016657806375eace641161014057806375eace641461029e578063765cb83a146102b15780638461d8f7146102c45780638da5cb5b146102d757600080fd5b80636b811a3c146102705780636d67fd4214610283578063715018a61461029657600080fd5b8063166e1527116101a2578063166e15271461022157806331b58b23146102425780633d0ebb8914610255578063605361721461026857600080fd5b80630248b34d146101c95780630422dfca146101f957806313ea5d291461020e575b600080fd5b6101dc6101d7366004614e7d565b6103fc565b6040516001600160601b0390911681526020015b60405180910390f35b61020c610207366004614fe7565b61086f565b005b61020c61021c3660046150a2565b610a65565b61023461022f3660046150bd565b610b1d565b6040519081526020016101f0565b61020c610250366004615115565b610d6f565b61020c6102633660046151bd565b610ebc565b61020c610fe6565b61020c61027e366004615268565b611056565b61020c610291366004615281565b611299565b61020c61138b565b61020c6102ac3660046152ba565b6113d1565b61020c6102bf3660046150a2565b6114ea565b61020c6102d2366004615303565b61159b565b6033546040516001600160a01b0390911681526020016101f0565b61020c610300366004615348565b611723565b61020c610313366004615392565b611bf4565b61020c610326366004615435565b611e2d565b61020c610339366004614fe7565b611fac565b61020c61034c3660046154d3565b612220565b61020c61035f36600461554a565b612522565b6102346103723660046150a2565b6125ab565b61020c610385366004615576565b6127ef565b61020c6103983660046155c5565b6129f8565b61020c6103ab3660046155fa565b612b48565b61020c6103be36600461561c565b612d8d565b61020c6103d13660046150a2565b612fcd565b61020c6103e436600461568f565b6130c1565b6102346103f736600461576d565b61338f565b6066546000906001600160a01b0316336001600160a01b03161461043b5760405162461bcd60e51b8152600401610432906157c1565b60405180910390fd5b6001600160a01b0388166104615760405162461bcd60e51b8152600401610432906157de565b606c5460ff8816106104855760405162461bcd60e51b8152600401610432906157fb565b856104a25760405162461bcd60e51b815260040161043290615818565b6001600160601b0384166104c85760405162461bcd60e51b815260040161043290615835565b6070546001600160601b03600160601b909104811690851611156104fe5760405162461bcd60e51b815260040161043290615852565b6070546001600160601b03908116908516101561052d5760405162461bcd60e51b815260040161043290615852565b600060666006018860ff16815481106105485761054861586f565b90600052602060002090600b02019050610561816137b9565b61057d5760405162461bcd60e51b815260040161043290615885565b6105948160010154600160b01b9004600216151590565b6105c65760405162461bcd60e51b815260206004820152600360248201526215155360ea1b6044820152606401610432565b6105d46066828860016137d1565b955060006105e282896137f4565b9050808260050160108282829054906101000a90046001600160601b031661060a91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555060006106838686600161066461065f8d6001600160601b0316886001600160601b031661383390919063ffffffff16565b613852565b60705463ffffffff600160c01b8204811691600160e01b900416613895565b905060006106a661065f6001600160601b03851663ffffffff80861690613a6516565b60058501805491925082916000906106c89084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e828339815191528b8260405161072292919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a161073481846158e3565b925061076b886001600160601b03168a6001600160601b0316856001600160601b0316610761919061590b565b61065f9190615940565b60675460405163a9059cbb60e01b81526001600160a01b038f811660048301526001600160601b038416602483015292975091169063a9059cbb906044016020604051808303816000875af11580156107c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ec9190615954565b50604080516001600160601b038b811682528a811660208301528781168284015283166060820152905160ff8d16916001600160a01b038f16917f4b834bda1e32273014eaae217dcc631b391d25d7d056066e2f008aecd86083e89181900360800190a3610858613a75565b610860613ade565b50505050979650505050505050565b3360009081526074602052604090205460ff1661089e5760405162461bcd60e51b815260040161043290615971565b8151806108d35760405162461bcd60e51b81526020600482015260036024820152624d545960e81b6044820152606401610432565b81518351146108f45760405162461bcd60e51b81526004016104329061598e565b60005b81811015610a5757600060666006018583815181106109185761091861586f565b602002602001015160ff16815481106109335761093361586f565b90600052602060002090600b0201905061096f8483815181106109585761095861586f565b6020026020010151826137f490919063ffffffff16565b600582018054601090610993908490600160801b90046001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550306001600160a01b0316336001600160a01b03167fb487f4e81f33139d200ec61f4269ce6361842282f43bd990d8c7f2dba2c942c88785815181106109fe576109fe61586f565b6020026020010151878681518110610a1857610a1861586f565b6020026020010151604051610a3c92919060ff929092168252602082015260400190565b60405180910390a35080610a4f816159ab565b9150506108f7565b50610a60613a75565b505050565b6033546001600160a01b03163314610a8f5760405162461bcd60e51b8152600401610432906159c6565b6073546001600160a01b0382811691161415610ad35760405162461bcd60e51b815260206004820152600360248201526243484760e81b6044820152606401610432565b607380546001600160a01b0319166001600160a01b0383169081179091556040517f20495ad1c3942659031bc9602fe224bb567644c05306fe5656f04f0c5cd09ef590600090a250565b3360009081526074602052604081205460ff16610b4c5760405162461bcd60e51b815260040161043290615971565b600060666006018560ff1681548110610b6757610b6761586f565b600091825260208220600b909102019150610b8282866137f4565b90506000610b9083866137f4565b90506000610b9e86886159fb565b90506000610bac83856158e3565b60058601549091506001600160601b03808316600160801b909204161015610be65760405162461bcd60e51b815260040161043290615a12565b600585018054849190600090610c069084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b0316610c5091906158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550838560040160008282829054906101000a90046001600160601b0316610c9a91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e828339815191528984604051610cf492919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a16001850154610d15906001600160a01b03168b84613b08565b60408051898152602081018990526001600160a01b038c1691339160ff8d16917f79f5e17a0fd20a621a90272196d71907e6c55c9d9d8dadb013e04c7687d9907b910160405180910390a45093505050505b949350505050565b6033546001600160a01b03163314610d995760405162461bcd60e51b8152600401610432906159c6565b610da4838383613b5a565b606c5460ff851610610dc85760405162461bcd60e51b8152600401610432906157fb565b600060666006018560ff1681548110610de357610de361586f565b90600052602060002090600b02019050836001811115610e0557610e05615a2f565b6003820180546001600160a01b0386166001600160c81b0319909116600160c01b60ff948516026001600160c01b0319161717600160a01b63ffffffff86160217905585167f6f7ad294d40e361770177ff88ae3b62bdab34547e1b530060fe80fcad0fd5953856001811115610e7d57610e7d615a2f565b6040805160ff90921682526001600160a01b038716602083015263ffffffff86169082015260600160405180910390a2610eb5613a75565b5050505050565b6066546001600160a01b0316336001600160a01b031614610eef5760405162461bcd60e51b8152600401610432906157c1565b606a54600090600160c01b900463ffffffff16610f0c8142615a45565b610f169190615a68565b606a54909150600160e01b900463ffffffff16610f4f57606a80546001600160e01b0316600160e01b63ffffffff841602179055610fd4565b606a5463ffffffff42811691610f7691600160c01b8204811691600160e01b900416615a94565b63ffffffff1610610f8657610fd4565b606a54600090610fa390600160e01b900463ffffffff1683615ab3565b9050610fb58989898989898988613cd9565b50606a80546001600160e01b0316600160e01b63ffffffff8416021790555b610fdc613a75565b5050505050505050565b6065546001600160a01b0316336001600160a01b03161461102f5760405162461bcd60e51b815260206004820152600360248201526214d39160ea1b6044820152606401610432565b606554611044906001600160a01b0316613fe2565b606580546001600160a01b0319169055565b6066546001600160a01b0316336001600160a01b0316146110895760405162461bcd60e51b8152600401610432906157c1565b600061109482614034565b6000838152606d60205260409020805491925090600160601b90046001600160601b0316156110eb5760405162461bcd60e51b81526020600482015260036024820152620533e360ec1b6044820152606401610432565b80546001600160601b03166111285760405162461bcd60e51b81526020600482015260036024820152620433d360ec1b6044820152606401610432565b60006066600601836040015160ff16815481106111475761114761586f565b90600052602060002090600b0201905060006066600601846020015160ff16815481106111765761117661586f565b90600052602060002090600b0201905061118f826137b9565b6111ab5760405162461bcd60e51b815260040161043290615885565b6111b4816137b9565b6111d05760405162461bcd60e51b815260040161043290615885565b82546001600160601b031660006111e783836140b7565b85546001600160601b0319168655865160695460715492935061121b9286929185916001600160a01b0391821691166140f2565b85600001516001600160a01b0316877f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f886020015184866040516112809392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a3611290613a75565b50505050505050565b6073546001600160a01b0316336001600160a01b031614806112c557506033546001600160a01b031633145b6112e15760405162461bcd60e51b815260040161043290615ad0565b6070546001600160601b03838116911614158061131357506070546001600160601b03828116600160601b9092041614155b1561137f57607080546001600160601b038481166001600160c01b03199092168217600160601b918516918202179092556040805191825260208201929092527f715d9f598cbf93067f06e0db8773770d07a5f09b2c8f15ebbe8a4ed971ac1362910160405180910390a15b611387613a75565b5050565b6033546001600160a01b031633146113b55760405162461bcd60e51b8152600401610432906159c6565b606580546001600160a01b03191690556113cf6000613fe2565b565b6066546001600160a01b0316336001600160a01b0316146114045760405162461bcd60e51b8152600401610432906157c1565b60405163015e626960e51b8152606660048201526001600160a01b038416602482015260ff831660448201526001600160601b03821660648201527303636b99aaf33e30acca596cc6de68ef3fabd64990632bcc4d209060840160006040518083038186803b15801561147657600080fd5b505af415801561148a573d6000803e3d6000fd5b5050604080516001600160a01b038716815260ff861660208201526001600160601b0385168183015290517fa76ab15d99fce78af68594e15ef49d09c577827bb14729563a5746fe0e6ba4039350908190036060019150a1610a60613a75565b6073546001600160a01b0316336001600160a01b0316148061151657506033546001600160a01b031633145b6115325760405162461bcd60e51b815260040161043290615ad0565b6040516317066a5760e21b81526001600160a01b038216600482015273912ce59144191c1204e64559fe8253a0e49e654890635c19a95c90602401600060405180830381600087803b15801561158757600080fd5b505af1158015610eb5573d6000803e3d6000fd5b6033546001600160a01b031633146115c55760405162461bcd60e51b8152600401610432906159c6565b606c5460ff8416106115e95760405162461bcd60e51b8152600401610432906157fb565b61162660666006018460ff16815481106116055761160561586f565b90600052602060002090600b0201600190810154600160b01b900416151590565b1561166f57606a805463ffffffff838116600160a01b0263ffffffff60a01b19918616600160801b029190911667ffffffffffffffff60801b19909216919091171790556116d6565b600060666006018460ff168154811061168a5761168a61586f565b600091825260209091206009600b9092020101805463ffffffff848116600160e01b026001600160e01b03918716600160c01b02919091166001600160c01b0390921691909117179055505b6040805163ffffffff80851682528316602082015260ff8516917f318cd38ce4d3f61a5e60de4f51d143ee426378b241efbe076180870b3394405b910160405180910390a2610a60613a75565b6066546001600160a01b0316336001600160a01b0316146117565760405162461bcd60e51b8152600401610432906157c1565b826117735760405162461bcd60e51b815260040161043290615818565b600061177e85614034565b80519091506001600160a01b03166117a85760405162461bcd60e51b8152600401610432906157de565b6020810151606c5460ff909116106117d25760405162461bcd60e51b8152600401610432906157fb565b6040810151606c5460ff909116106117fc5760405162461bcd60e51b8152600401610432906157fb565b60006066600601826040015160ff168154811061181b5761181b61586f565b90600052602060002090600b0201905060006066600601836020015160ff168154811061184a5761184a61586f565b90600052602060002090600b02019050611863826137b9565b61187f5760405162461bcd60e51b815260040161043290615885565b611888816137b9565b6118a45760405162461bcd60e51b815260040161043290615885565b6000878152606d602052604090206118be606684876141fe565b94506118cc606683886141fe565b955060006118e08285876060015189614344565b8254909150600160601b90046001600160601b0316156119095761190982858760600151614414565b600061192461065f6001600160601b03848116908b16614486565b83549091506001600160601b038083169116101561196a5760405162461bcd60e51b815260206004820152600360248201526246454560e81b6044820152606401610432565b8254819084906000906119879084906001600160601b03166158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160008282829054906101000a90046001600160601b03166119d191906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808460050160108282829054906101000a90046001600160601b0316611a1b91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152866020015182604051611a7992919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a1506000611a8e848a6137f4565b83549091506001600160601b0380831691161015611ad45760405162461bcd60e51b8152602060048201526003602482015262433c5760e81b6044820152606401610432565b8254611aea9082906001600160601b03166158e3565b83546001600160601b0319166001600160601b03919091161783558551606954607154611b2992879290918d916001600160a01b0390811691166140f2565b611b4183876040015160ff1688606001518b8b61449b565b611b735760405162461bcd60e51b815260206004820152600360248201526221494d60e81b6044820152606401610432565b85600001516001600160a01b03168a7f39db80d104f204d9d7117515e240ca09e84cf05a4061c9a9a0877aecd8b2607f88602001518c85604051611bd89392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b60405180910390a3611be8613a75565b50505050505050505050565b6033546001600160a01b03163314611c1e5760405162461bcd60e51b8152600401610432906159c6565b606c5460ff8a1610611c425760405162461bcd60e51b8152600401610432906157fb565b87611c755760405162461bcd60e51b815260206004820152600360248201526253594d60e81b6044820152606401610432565b600060666006018a60ff1681548110611c9057611c9061586f565b90600052602060002090600b02019050888160000181905550878160020160006101000a81548163ffffffff021916908363ffffffff160217905550868160020160046101000a81548163ffffffff021916908363ffffffff160217905550858160020160106101000a81548163ffffffff021916908363ffffffff1602179055508481600501600c6101000a81548163ffffffff021916908363ffffffff160217905550838160020160086101000a81548163ffffffff021916908363ffffffff1602179055508281600201600c6101000a81548163ffffffff021916908363ffffffff160217905550818160090160146101000a81548163ffffffff021916908363ffffffff1602179055508960ff167f8a545db87c099fd21f0a6729013aaa68a315a726271eb8239bcc30170a4ce40b8a8a8a8a8a8a8a8a604051611e1d98979695949392919097885263ffffffff9687166020890152948616604088015292851660608701529084166080860152831660a0850152821660c08401521660e08201526101000190565b60405180910390a2611be8613a75565b6066546001600160a01b0316336001600160a01b031614611e605760405162461bcd60e51b8152600401610432906157c1565b604051636e4d522360e11b8152606660048201526001600160a01b038916602482015260ff8089166044830152871660648201526001600160601b03808716608483015280861660a483015260c4820185905280841660e483015282166101048201526000907303636b99aaf33e30acca596cc6de68ef3fabd6499063dc9aa4469061012401602060405180830381865af4158015611f03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f279190615aed565b6040805160ff8b811682528a1660208201526001600160601b038681168284015285811660608301528981166080830152831660a082015290519192506001600160a01b038b16917feb9bf2cb51f41dfacd571a3fa0742757c031317644527fd26db5cb7516df197b9181900360c00190a2611fa1613a75565b505050505050505050565b3360009081526074602052604090205460ff16611fdb5760405162461bcd60e51b815260040161043290615971565b8151806120105760405162461bcd60e51b81526020600482015260036024820152624d545960e81b6044820152606401610432565b81518351146120315760405162461bcd60e51b81526004016104329061598e565b60005b81811015610a5757600060666006018583815181106120555761205561586f565b602002602001015160ff16815481106120705761207061586f565b90600052602060002090600b020190506120b6338584815181106120965761209661586f565b602090810291909101015160018401546001600160a01b03169190613b08565b60006120e48584815181106120cd576120cd61586f565b6020026020010151836137f490919063ffffffff16565b60058301549091506001600160601b03808316600160801b9092041610156121345760405162461bcd60e51b815260206004820152600360248201526213931560ea1b6044820152606401610432565b808260050160108282829054906101000a90046001600160601b031661215a91906158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550336001600160a01b0316306001600160a01b03167fb487f4e81f33139d200ec61f4269ce6361842282f43bd990d8c7f2dba2c942c88886815181106121c5576121c561586f565b60200260200101518887815181106121df576121df61586f565b602002602001015160405161220392919060ff929092168252602082015260400190565b60405180910390a350508080612218906159ab565b915050612034565b6033546001600160a01b0316331461224a5760405162461bcd60e51b8152600401610432906159c6565b620186a08463ffffffff16106122885760405162461bcd60e51b8152602060048201526003602482015262463e3160e81b6044820152606401610432565b620186a08363ffffffff16106122c65760405162461bcd60e51b8152602060048201526003602482015262463e3160e81b6044820152606401610432565b620186a08263ffffffff16106123045760405162461bcd60e51b8152602060048201526003602482015262443e3160e81b6044820152606401610432565b606a5463ffffffff868116600160c01b909204161461238757606a5460408051600160c01b90920463ffffffff9081168352871660208301527f41c014a2dd74f3d1ada70fc516f134f02e2ffdd2f208320663300a9e093c78e5910160405180910390a1606a805463ffffffff60c01b1916600160c01b63ffffffff8816021790555b60705463ffffffff858116600160c01b909204161415806123ba575060705463ffffffff848116600160e01b9092041614155b1561243357607080546001600160c01b0316600160c01b63ffffffff8781169182026001600160e01b031692909217600160e01b928716928302179092556040805192835260208301919091527fb97a93995fbff5fafc7defd9c41d52a41e402d34ef57abcf3eae22042910df9e910160405180910390a15b60715463ffffffff838116600160c01b90920416146124a4576071805463ffffffff60c01b1916600160c01b63ffffffff8516908102919091179091556040519081527f922a86287dffe250b116e287f13cec73d7aebbca29e35b545f13fafb203c1fa59060200160405180910390a15b6072546001600160601b03828116600160a01b909204161461251a57607280546001600160a01b0316600160a01b6001600160601b038416908102919091179091556040519081527f90d2ff51f70c1805e062f1e89009c2b4a86533de78b2145adf26f95f55fa1d999060200160405180910390a15b610eb5613a75565b6033546001600160a01b0316331461254c5760405162461bcd60e51b8152600401610432906159c6565b6001600160a01b038216600081815260746020908152604091829020805460ff191685151590811790915591519182527f8a24dcf97d36a7d6c53760ca365425426e042611881df1138051227059fa027d910160405180910390a25050565b6066546000906001600160a01b0316336001600160a01b0316146125e15760405162461bcd60e51b8152600401610432906157c1565b6001600160a01b03821661261d5760405162461bcd60e51b81526020600482015260036024820152622921ab60e91b6044820152606401610432565b606c5460005b818110156127e7576000606660060182815481106126435761264361586f565b60009182526020909120606954600b9092020160018101549092506001600160a01b03918216911614156127d4576071546072546000916126a59161065f91600160e01b900463ffffffff1690600160a01b90046001600160601b031661590b565b60058301549091506001600160601b03808316600160801b9092041610156126df5760405162461bcd60e51b815260040161043290615a12565b808260050160108282829054906101000a90046001600160601b031661270591906158e3565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061273c81836140b790919063ffffffff16565b60715460408051600160e01b90920463ffffffff168252602082018390529196506001600160a01b038816917f4fd749fd2a8d968a01b106b5d56dfff72958e32f65e3b1666d92f4607f7c04e5910160405180910390a2607180546001600160e01b0381169091556069546127c3918491899189916001600160a01b0390811691166140f2565b6127cb613a75565b50505050919050565b50806127df816159ab565b915050612623565b50505b919050565b3360009081526074602052604090205460ff1661281e5760405162461bcd60e51b815260040161043290615971565b600060666006018560ff16815481106128395761283961586f565b600091825260208220600b90910201915061285482866137f4565b9050600061286283866137f4565b9050600061287084866137f4565b9050600061287e83856158b8565b60058601805491925084916000906128a09084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550808560050160108282829054906101000a90046001600160601b03166128ea91906158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550818461291a91906158b8565b6004860180546000906129379084906001600160601b03166158e3565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152898460405161299192919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a160408051898152602081018990529081018790526001600160a01b038b1690339060ff8c16907f8b0e10e6a23b017a84b2cd5ba0a1cc73e4d5903cc3fc5cfedfd24dcb639761a99060600160405180910390a450505050505050505050565b6072546001600160a01b0316612a365760405162461bcd60e51b815260206004820152600360248201526215931560ea1b6044820152606401610432565b60005b8151811015612b3c576000828281518110612a5657612a5661586f565b6020908102919091010151604051620acde760ea1b81526066600482015260ff821660248201529091506000907303636b99aaf33e30acca596cc6de68ef3fabd64990632b379c0090604401602060405180830381865af4158015612abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae39190615aed565b6040516001600160601b038216815290915060ff8316907ff81b718ab776e0011332f4662259a37be2abe9511d53ca54206f4d71124515eb9060200160405180910390a250508080612b34906159ab565b915050612a39565b50612b45613a75565b50565b6066546001600160a01b0316336001600160a01b031614612b7b5760405162461bcd60e51b8152600401610432906157c1565b6000612b8683614034565b80519091506001600160a01b0316612bb05760405162461bcd60e51b8152600401610432906157de565b6020810151606c5460ff90911610612bda5760405162461bcd60e51b8152600401610432906157fb565b6040810151606c5460ff90911610612c045760405162461bcd60e51b8152600401610432906157fb565b81612c215760405162461bcd60e51b815260040161043290615818565b6000838152606d602052604080822090830151606c8054929392909160ff16908110612c4f57612c4f61586f565b90600052602060002090600b0201905060006066600601846020015160ff1681548110612c7e57612c7e61586f565b90600052602060002090600b02019050612c97826137b9565b612cb35760405162461bcd60e51b815260040161043290615885565b612cbc816137b9565b612cd85760405162461bcd60e51b815260040161043290615885565b6000612ce482876137f4565b845490915081908590600090612d049084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600001516001600160a01b0316877f94ab403b9852b1c10901b0928c9b5692d2d229c8e666bae8f77cac8814ead64f876020015189856040516112809392919060ff93909316835260208301919091526001600160601b0316604082015260600190565b6033546001600160a01b03163314612db75760405162461bcd60e51b8152600401610432906159c6565b60128460ff161115612df15760405162461bcd60e51b815260206004820152600360248201526244434d60e81b6044820152606401610432565b606c5460ff871614612e155760405162461bcd60e51b815260040161043290615b0a565b60ff8660ff1610612e4e5760405162461bcd60e51b815260206004820152600360248201526211931360ea1b6044820152606401610432565b84612e815760405162461bcd60e51b815260206004820152600360248201526253594d60e81b6044820152606401610432565b606c80546001018082556000828152919060ff8916908110612ea557612ea561586f565b60009182526020909120600b9091020186815560018101805460ff888116600160a81b0260ff60a81b19918c16600160a01b029190911661ffff60a01b1990921691909117179055905083612efb576000612efe565b60015b6001820180546001600160a01b038681166001600160a01b031966ffffffffffffff9590951666fffffffffffffe600160b01b8086049190911691909117028516600168ffffffffffffff000160a01b03199093169290921782179092556009840180549286169290931682179092556040805189815260ff8981166020830152881515828401526060820194909452608081019290925251918916917ffdb7e1b6147dbcaa8b4b4f67f5f4324921f27271ae3463824452556451774d4d9181900360a00190a2611290613a75565b6033546001600160a01b03163314612ff75760405162461bcd60e51b8152600401610432906159c6565b6001600160a01b0381166130335760405162461bcd60e51b815260206004820152600360248201526204f3d360ec1b6044820152606401610432565b6033546001600160a01b03828116911614156130775760405162461bcd60e51b81526020600482015260036024820152624f3d4f60e81b6044820152606401610432565b606580546001600160a01b0319166001600160a01b0383169081179091556040517fb501f5dc3610d10a179d924f686fc89ddcdd3abb05afd7a1baeead166b2f122c90600090a250565b6073546001600160a01b0316336001600160a01b031614806130ed57506033546001600160a01b031633145b6131095760405162461bcd60e51b815260040161043290615ad0565b606c5460ff8c161061312d5760405162461bcd60e51b8152600401610432906157fb565b600060666006018c60ff16815481106131485761314861586f565b90600052602060002090600b0201905061316f81600190810154600160b01b900416151590565b6131a75785156131a75760405162461bcd60e51b815260206004820152600360248201526229aa2160e91b6044820152606401610432565b6001810154600160b01b900466ffffffffffffff168b6131c85760006131cc565b6101005b6101001991909116178a6131e15760006131e6565b620100005b62010000199190911617896131fc576000613202565b63010000005b630100000019919091161788613219576000613220565b6401000000005b64010000000019919091161787613238576000613240565b650100000000005b6501000000000019919091161786613259576000613262565b66010000000000005b66010000000000001991909116178561327c57600061327f565b60025b60018301805466ffffffffffffff60b01b1916600160b01b66fffffffffffffd851666ffffffffffffff8516179081029190911790915560038401805463ffffffff60c81b1916600160c81b63ffffffff8a16908102919091179091556006850180546001600160601b0319166001600160601b038981169182179092556007870180546bffffffffffffffffffffffff60601b1916600160601b938a16938402179055604080519485526020850193909352918301919091526060820152600219909216179060ff8e16907fc2584bc16fc882ad266db46fe5ccb39d246e07c58825b8cbbefbfb802d3092c19060800160405180910390a2613380613a75565b50505050505050505050505050565b6066546000906001600160a01b0316336001600160a01b0316146133c55760405162461bcd60e51b8152600401610432906157c1565b6001600160a01b0388166133eb5760405162461bcd60e51b8152600401610432906157de565b606c5460ff87161061340f5760405162461bcd60e51b8152600401610432906157fb565b6001600160601b0384166134355760405162461bcd60e51b815260040161043290615835565b6070546001600160601b03600160601b9091048116908516111561346b5760405162461bcd60e51b815260040161043290615852565b6070546001600160601b03908116908516101561349a5760405162461bcd60e51b815260040161043290615852565b6001600160601b0387166134c05760405162461bcd60e51b815260040161043290615818565b600060666006018760ff16815481106134db576134db61586f565b90600052602060002090600b020190506134f4816137b9565b6135105760405162461bcd60e51b815260040161043290615885565b6135278160010154600160b01b9004600216151590565b6135595760405162461bcd60e51b815260206004820152600360248201526215155360ea1b6044820152606401610432565b6135676066828860006137d1565b95506000613596876001600160601b0316876001600160601b03168b6001600160601b0316610761919061590b565b90506000806135ca8787600061066461065f8e6001600160601b0316896001600160601b031661383390919063ffffffff16565b90506135eb61065f6001600160601b03851663ffffffff80851690613a6516565b6005850180549193508392509060009061360f9084906001600160601b03166158b8565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550600080516020615e82833981519152898260405161366992919060ff9290921682526001600160601b0316602082015260400190565b60405180910390a161367b81836158e3565b60058401549092506001600160601b03600160801b909104811690831611156136b65760405162461bcd60e51b815260040161043290615a12565b818360050160108282829054906101000a90046001600160601b03166136dc91906158e3565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555061371382846140b790919063ffffffff16565b6069546071549195506137389185918e9188916001600160a01b0390811691166140f2565b604080516001600160601b038a8116825289811660208301528c81168284015283166060820152905160ff8b16916001600160a01b038e16917fd20afc5b5a4edaae7399b3eb4737f26d8688e6a86e1b525ffbe063574583b1aa9181900360800190a36137a3613a75565b6137ab613ade565b505050979650505050505050565b60010154600160b01b90046501000000000016151590565b60006137de8585856141fe565b92506137eb848484614521565b95945050505050565b600182015460009061382a9061381590600160a81b900460ff166012615b27565b61382090600a615c2e565b61065f908461590b565b90505b92915050565b6000670de0b6b3a7640000613848838561590b565b61382a9190615940565b60006001600160601b038211156138915760405162461bcd60e51b8152602060048201526003602482015262279c9b60e91b6044820152606401610432565b5090565b60008085156138af576138a885896158b8565b90506138ee565b846001600160601b0316886001600160601b031610156138e15760405162461bcd60e51b815260040161043290615a12565b6138eb85896158e3565b90505b6000876001600160601b0316896001600160601b0316116139185761391389896158e3565b613922565b613922888a6158e3565b90506000886001600160601b0316836001600160601b03161161394e57613949838a6158e3565b613958565b61395889846158e3565b90506001600160601b03891661397357859350505050613a5b565b816001600160601b0316816001600160601b031610156139f75760006139c68a6001600160601b0316846001600160601b03168863ffffffff166139b7919061590b565b6139c19190615940565b6145db565b90508063ffffffff168763ffffffff16116139e25760006139ec565b6139ec8188615ab3565b945050505050613a5b565b60006002613a0583856158b8565b613a0f9190615c3d565b9050613a1b818b614617565b90506000613a478b6001600160601b0316836001600160601b03168963ffffffff166139b7919061590b565b9050613a538189615a94565b955050505050505b9695505050505050565b6000620186a0613848838561590b565b6071805463ffffffff600160a01b80830482166001018216810263ffffffff60a01b1990931692909217928390556040519190920490911681527f974288fc79dc8b8b1fe9f1f0b8f5738873e53e07d3122c5e3c02b834c018d6cb9060200160405180910390a1565b6071805463ffffffff600160e01b8083048216600101909116026001600160e01b03909116179055565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610a60908490614640565b620186a08163ffffffff161115613b995760405162461bcd60e51b8152602060048201526003602482015262443e3160e81b6044820152606401610432565b6001836001811115613bad57613bad615a2f565b1415610a60576000829050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c1a9190615c57565b60ff16600814613c525760405162461bcd60e51b815260206004820152600360248201526204288760eb1b6044820152606401610432565b6000816001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cb69190615c74565b13613cd35760405162461bcd60e51b815260040161043290615835565b50505050565b858414613cf85760405162461bcd60e51b81526004016104329061598e565b858214613d175760405162461bcd60e51b81526004016104329061598e565b606a546000908190613d419063ffffffff600160801b8204811691600160a01b9004168c86614712565b606c5491935091506000805b8260ff168160ff16101561338057600060666006018260ff1681548110613d7657613d7661586f565b90600052602060002090600b02019050613d9d81600190810154600160b01b900416151590565b15613da85750613fd0565b60ff83168c11613dca5760405162461bcd60e51b81526004016104329061598e565b8160ff168d8d8560ff16818110613de357613de361586f565b9050602002016020810190613df89190615c8d565b60ff1614613e185760405162461bcd60e51b815260040161043290615b0a565b60098101546000908190613e6d9063ffffffff600160c01b8204811691600160e01b9004168f8f60ff8a16818110613e5257613e5261586f565b9050602002016020810190613e679190615caa565b8c614712565b600a850180549294509092508291600090613e929084906001600160801b0316615cc5565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506000613eed6066858e8e8a60ff16818110613ed357613ed361586f565b9050602002016020810190613ee89190615ce7565b6141fe565b9050613f13613f0e6001600160801b038a166001600160601b038416613833565b6147b9565b600a85018054601090613f37908490600160801b90046001600160801b0316615cc5565b82546101009290920a6001600160801b03818102199093169183160217909155600a8601546040805163ffffffff808916825283851660208301528e1691810191909152600160801b909104909116606082015260ff871692507f59b0ca320d4e4d85aa96c127c03fb78b50d499a8ccf1987b628b97b9acb66cac915060800160405180910390a2613fca600186615d04565b94505050505b80613fda81615d29565b915050613d4d565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60408051608081018252600080825260208201819052918101829052606081019190915268ffffffffffffffffff8216156140815760405162461bcd60e51b815260040161043290615b0a565b606082811c825260ff605884901c81166020840152605084901c8116604084015260489390931c90921615159181019190915290565b60018201546000906140d490600160a81b900460ff166012615b27565b6140df90600a615c2e565b61382a906001600160601b038416615940565b60018501546001600160a01b03838116911614156141e55760405163a9059cbb60e01b81526001600160a01b0382811660048301526024820185905283169063a9059cbb906044016020604051808303816000875af1158015614159573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061417d9190615954565b506040516339f4769360e01b81526001600160a01b038581166004830152602482018590528216906339f4769390604401600060405180830381600087803b1580156141c857600080fd5b505af11580156141dc573d6000803e3d6000fd5b50505050610eb5565b6001850154610eb5906001600160a01b03168585613b08565b60006001600160601b0382166142265760405162461bcd60e51b815260040161043290615835565b60016003840154600160c01b900460ff16600181111561424857614248615a2f565b600181111561425957614259615a2f565b141561428a576003830154600090614279906001600160a01b03166147f8565b9050614286848483614899565b9250505b6142a78360010154600160b01b9004660100000000000016151590565b1561433a576000670de0b6b3a7640000836001600160601b0316116142dd576142d883670de0b6b3a76400006158e3565b6142ef565b6142ef670de0b6b3a7640000846158e3565b600b8601546001600160601b0391909116915060009061432290600160c01b900463ffffffff166509184e72a00061590b565b905080821161433757670de0b6b3a764000093505b50505b50805b9392505050565b8354600090600160601b90046001600160601b031661436557506000610d67565b600083156143b9576001860154600a860154614394916001600160801b03600160601b90910481169116615d49565b6001600160801b031690506143b2816001600160601b038516613833565b90506143f3565b6001860154600a8601546143e7916001600160801b03600160601b909104811691600160801b900416615d49565b6001600160801b031690505b8554613a5b9061065f908390600160601b90046001600160601b0316613833565b801561444d5750600a01546001919091018054600160601b600160e01b0319166001600160801b03909216600160601b02919091179055565b50600a01546001919091018054600160601b600160e01b031916600160801b9092046001600160801b0316600160601b02919091179055565b60008161384884670de0b6b3a764000061590b565b60008060666006018663ffffffff16815481106144ba576144ba61586f565b90600052602060002090600b020190506000806144f1838a898c600001600c9054906101000a90046001600160601b031689614a40565b60028501549193509150614514908a908890889063ffffffff1686866000614bda565b9998505050505050505050565b6003830154600090600160c81b900463ffffffff1661454157508161433d565b60038401546000906145729061065f906001600160601b0387169063ffffffff600160c81b909104811690613a6516565b9050600183600181111561458857614588615a2f565b14156145d157806001600160601b0316846001600160601b0316116145bf5760405162461bcd60e51b815260040161043290615835565b6145c981856158e3565b91505061433d565b6145c981856158b8565b600063ffffffff8211156138915760405162461bcd60e51b815260206004820152600360248201526227999960e91b6044820152606401610432565b6000816001600160601b0316836001600160601b03161115614639578161382a565b5090919050565b6000614695826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614cb29092919063ffffffff16565b805190915015610a6057808060200190518101906146b39190615954565b610a605760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610432565b600080620186a08463ffffffff1611156147545760405162461bcd60e51b8152602060048201526003602482015262553e3160e81b6044820152606401610432565b61476d6139c163ffffffff8681169080891690613a6516565b91506147798287614cc1565b91506147ae61708061479463ffffffff86811690861661590b565b6147a4906509184e72a00061590b565b613f0e9190615940565b905094509492505050565b60006001600160801b038211156138915760405162461bcd60e51b815260206004820152600360248201526227989960e91b6044820152606401610432565b600080826001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061485d9190615c74565b90506000811361487f5760405162461bcd60e51b815260040161043290615835565b61488e6402540be40082615d69565b905061433d81613852565b6003830154600090600160a01b900463ffffffff166148b957508061433d565b60038401546000906148e6906001600160601b0385169063ffffffff600160a01b909104811690613a6516565b9050600061490061065f836001600160601b0387166159fb565b9050806001600160601b0316856001600160601b03161015614990576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b6149a661065f836001600160601b038716615dee565b9050806001600160601b0316856001600160601b03161115614a36576001860154600387015460408051600160a01b9384900460ff1681526001600160601b03808a16602083015288169181019190915291900463ffffffff1660608201527ffe9ee1875afc0594e1dee9eaff7974f80f8690186178b0fe780077776deed4c89060800160405180910390a18094505b5092949350505050565b6000806001600160601b038416614a5c57506000905080614bd0565b6000836001600160601b031611614a855760405162461bcd60e51b815260040161043290615835565b84614aa35760018601546001600160601b0390811690841610614ab8565b60018601546001600160601b03908116908416115b60018701549092506000906001600160601b039081169085161015614af5576001870154614af09085906001600160601b03166158e3565b614b0d565b6001870154614b0d906001600160601b0316856158e3565b9050828015614b4e575060028801548754614b3f9163ffffffff600160601b909104811691600160c01b900416615a94565b63ffffffff164263ffffffff16105b8015614ba1575060028801546001880154614b8c9161065f916001600160601b03169063ffffffff68010000000000000000909104811690613a6516565b6001600160601b0316816001600160601b0316105b15614bb3575060009150819050614bd0565b614bcc61065f6001600160601b03838116908816613833565b9150505b9550959350505050565b86546000908190620186a090670de0b6b3a76400009063ffffffff891690614c15906001600160601b03808d1691600160601b90041661590b565b614c1f919061590b565b614c299190615940565b614c339190615940565b9050614c486001600160601b03841682615dee565b8954909150600090614c67906001600160601b03908116908b16613833565b90508515614c8e5781614c836001600160601b03871683615dee565b101592505050614ca7565b614ca16001600160601b03861683615dee565b11159150505b979650505050505050565b6060610d678484600085614cdd565b60008163ffffffff168363ffffffff161015614639578161382a565b606082471015614d3e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610432565b6001600160a01b0385163b614d955760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610432565b600080866001600160a01b03168587604051614db19190615e32565b60006040518083038185875af1925050503d8060008114614dee576040519150601f19603f3d011682016040523d82523d6000602084013e614df3565b606091505b5091509150614ca782828660608315614e0d57508161433d565b825115614e1d5782518084602001fd5b8160405162461bcd60e51b81526004016104329190615e4e565b80356001600160a01b03811681146127ea57600080fd5b60ff81168114612b4557600080fd5b6001600160601b0381168114612b4557600080fd5b80356127ea81614e5d565b600080600080600080600060e0888a031215614e9857600080fd5b614ea188614e37565b96506020880135614eb181614e4e565b9550604088013594506060880135614ec881614e5d565b93506080880135614ed881614e5d565b925060a0880135614ee881614e5d565b915060c0880135614ef881614e5d565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715614f4757614f47614f08565b604052919050565b600067ffffffffffffffff821115614f6957614f69614f08565b5060051b60200190565b600082601f830112614f8457600080fd5b81356020614f99614f9483614f4f565b614f1e565b82815260059290921b84018101918181019086841115614fb857600080fd5b8286015b84811015614fdc578035614fcf81614e4e565b8352918301918301614fbc565b509695505050505050565b60008060408385031215614ffa57600080fd5b823567ffffffffffffffff8082111561501257600080fd5b61501e86838701614f73565b935060209150818501358181111561503557600080fd5b85019050601f8101861361504857600080fd5b8035615056614f9482614f4f565b81815260059190911b8201830190838101908883111561507557600080fd5b928401925b828410156150935783358252928401929084019061507a565b80955050505050509250929050565b6000602082840312156150b457600080fd5b61382a82614e37565b600080600080608085870312156150d357600080fd5b6150dc85614e37565b935060208501356150ec81614e4e565b93969395505050506040820135916060013590565b803563ffffffff811681146127ea57600080fd5b6000806000806080858703121561512b57600080fd5b843561513681614e4e565b935060208501356002811061514a57600080fd5b925061515860408601614e37565b915061516660608601615101565b905092959194509250565b60008083601f84011261518357600080fd5b50813567ffffffffffffffff81111561519b57600080fd5b6020830191508360208260051b85010111156151b657600080fd5b9250929050565b60008060008060008060006080888a0312156151d857600080fd5b6151e188615101565b9650602088013567ffffffffffffffff808211156151fe57600080fd5b61520a8b838c01615171565b909850965060408a013591508082111561522357600080fd5b61522f8b838c01615171565b909650945060608a013591508082111561524857600080fd5b506152558a828b01615171565b989b979a50959850939692959293505050565b60006020828403121561527a57600080fd5b5035919050565b6000806040838503121561529457600080fd5b823561529f81614e5d565b915060208301356152af81614e5d565b809150509250929050565b6000806000606084860312156152cf57600080fd5b6152d884614e37565b925060208401356152e881614e4e565b915060408401356152f881614e5d565b809150509250925092565b60008060006060848603121561531857600080fd5b833561532381614e4e565b925061533160208501615101565b915061533f60408501615101565b90509250925092565b6000806000806080858703121561535e57600080fd5b8435935060208501359250604085013561537781614e5d565b9150606085013561538781614e5d565b939692955090935050565b60008060008060008060008060006101208a8c0312156153b157600080fd5b89356153bc81614e4e565b985060208a013597506153d160408b01615101565b96506153df60608b01615101565b95506153ed60808b01615101565b94506153fb60a08b01615101565b935061540960c08b01615101565b925061541760e08b01615101565b91506154266101008b01615101565b90509295985092959850929598565b600080600080600080600080610100898b03121561545257600080fd5b61545b89614e37565b9750602089013561546b81614e4e565b9650604089013561547b81614e4e565b9550606089013561548b81614e5d565b9450608089013561549b81614e5d565b935060a0890135925060c08901356154b281614e5d565b915060e08901356154c281614e5d565b809150509295985092959890939650565b600080600080600060a086880312156154eb57600080fd5b6154f486615101565b945061550260208701615101565b935061551060408701615101565b925061551e60608701615101565b9150608086013561552e81614e5d565b809150509295509295909350565b8015158114612b4557600080fd5b6000806040838503121561555d57600080fd5b61556683614e37565b915060208301356152af8161553c565b600080600080600060a0868803121561558e57600080fd5b61559786614e37565b945060208601356155a781614e4e565b94979496505050506040830135926060810135926080909101359150565b6000602082840312156155d757600080fd5b813567ffffffffffffffff8111156155ee57600080fd5b610d6784828501614f73565b6000806040838503121561560d57600080fd5b50508035926020909101359150565b60008060008060008060c0878903121561563557600080fd5b863561564081614e4e565b955060208701359450604087013561565781614e4e565b935060608701356156678161553c565b925061567560808801614e37565b915061568360a08801614e37565b90509295509295509295565b60008060008060008060008060008060006101608c8e0312156156b157600080fd5b8b356156bc81614e4e565b9a5060208c01356156cc8161553c565b995060408c01356156dc8161553c565b985060608c01356156ec8161553c565b975060808c01356156fc8161553c565b965060a08c013561570c8161553c565b955060c08c013561571c8161553c565b945060e08c013561572c8161553c565b935061573b6101008d01615101565b92506101208c013561574c81614e5d565b915061575b6101408d01614e72565b90509295989b509295989b9093969950565b600080600080600080600060e0888a03121561578857600080fd5b61579188614e37565b965060208801356157a181614e5d565b955060408801356157b181614e4e565b94506060880135614ec881614e5d565b602080825260039082015262424f4b60e81b604082015260600190565b6020808252600390820152620543d360ec1b604082015260600190565b6020808252600390820152621314d560ea1b604082015260600190565b6020808252600390820152620413d360ec1b604082015260600190565b6020808252600390820152620503d360ec1b604082015260600190565b6020808252600390820152624d504f60e81b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b602080825260039082015262454e4160e81b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b60006001600160601b038083168185168083038211156158da576158da6158a2565b01949350505050565b60006001600160601b0383811690831681811015615903576159036158a2565b039392505050565b6000816000190483118215151615615925576159256158a2565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261594f5761594f61592a565b500490565b60006020828403121561596657600080fd5b815161433d8161553c565b6020808252600390820152624c514d60e81b604082015260600190565b6020808252600390820152622622a760e91b604082015260600190565b60006000198214156159bf576159bf6158a2565b5060010190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600082821015615a0d57615a0d6158a2565b500390565b6020808252600390820152624c495160e81b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b600063ffffffff80841680615a5c57615a5c61592a565b92169190910492915050565b600063ffffffff80831681851681830481118215151615615a8b57615a8b6158a2565b02949350505050565b600063ffffffff8083168185168083038211156158da576158da6158a2565b600063ffffffff83811690831681811015615903576159036158a2565b60208082526003908201526253214d60e81b604082015260600190565b600060208284031215615aff57600080fd5b815161433d81614e5d565b60208082526003908201526210525160ea1b604082015260600190565b600060ff821660ff841680821015615b4157615b416158a2565b90039392505050565b600181815b80851115615b85578160001904821115615b6b57615b6b6158a2565b80851615615b7857918102915b93841c9390800290615b4f565b509250929050565b600082615b9c5750600161382d565b81615ba95750600061382d565b8160018114615bbf5760028114615bc957615be5565b600191505061382d565b60ff841115615bda57615bda6158a2565b50506001821b61382d565b5060208310610133831016604e8410600b8410161715615c08575081810a61382d565b615c128383615b4a565b8060001904821115615c2657615c266158a2565b029392505050565b600061382a60ff841683615b8d565b60006001600160601b0380841680615a5c57615a5c61592a565b600060208284031215615c6957600080fd5b815161433d81614e4e565b600060208284031215615c8657600080fd5b5051919050565b600060208284031215615c9f57600080fd5b813561433d81614e4e565b600060208284031215615cbc57600080fd5b61382a82615101565b60006001600160801b038083168185168083038211156158da576158da6158a2565b600060208284031215615cf957600080fd5b813561433d81614e5d565b600060ff821660ff84168060ff03821115615d2157615d216158a2565b019392505050565b600060ff821660ff811415615d4057615d406158a2565b60010192915050565b60006001600160801b0383811690831681811015615903576159036158a2565b60006001600160ff1b0381841382841380821686840486111615615d8f57615d8f6158a2565b600160ff1b6000871282811687830589121615615dae57615dae6158a2565b60008712925087820587128484161615615dca57615dca6158a2565b87850587128184161615615de057615de06158a2565b505050929093029392505050565b60008219821115615e0157615e016158a2565b500190565b60005b83811015615e21578181015183820152602001615e09565b83811115613cd35750506000910152565b60008251615e44818460208701615e06565b9190910192915050565b6020815260008251806020840152615e6d816040850160208701615e06565b601f01601f1916919091016040019291505056fefe5353ab8f25b751655ce462adb7567c9e4dff8da72a5f26094ef0e9300d737ca264697066735822122030a9611243b401474347eae338b8e5bd5eb24b403fd2336620912b9e7803b51b64736f6c634300080a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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