ETH Price: $2,427.19 (-9.89%)

Contract

0x0f4DfD1CEAbb84C2e79645B71dA321D09203ea39

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OrderBook

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

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

import "../interfaces/IOrderBook.sol";
import "../interfaces/IWETH9.sol";
import "../interfaces/IReferralManager.sol";
import "../libraries/LibConfigMap.sol";
import "../libraries/LibOrderBook.sol";
import "../libraries/LibOrderBook2.sol";
import "../libraries/LibCodec.sol";
import "./OrderBookStore.sol";
import "./OrderBookGetter.sol";
import "./PriceProvider.sol";

contract OrderBook is OrderBookStore, ReentrancyGuardUpgradeable, OrderBookGetter, PriceProvider, IOrderBook {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    /**
     * @notice Restricts function access when specific order type is paused
     * @param orderType The type of order to check
     */
    modifier whenNotPaused(OrderType orderType) {
        require(!_isOrderPaused(orderType), "Paused");
        _;
    }

    /**
     * @notice Updates the sequence number after function execution
     */
    modifier updateSequence() {
        _;
        unchecked {
            _storage.sequence += 1;
        }
        emit UpdateSequence(_storage.sequence);
    }

    /**
     * @notice Initializes the OrderBook contract
     * @param mux3Facet The address of the Mux3 Facet contract
     * @param weth The address of the WETH contract
     * @dev Can only be called once due to initializer modifier
     */
    function initialize(address mux3Facet, address weth) external initializer {
        __OrderBookStore_init();
        __ReentrancyGuard_init();
        _storage.mux3Facet = mux3Facet;
        _storage.weth = weth;
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MAINTAINER_ROLE, msg.sender);
    }

    /**
     * @notice Allows contract to receive ETH from WETH contract only
     */
    receive() external payable {
        require(msg.sender == _storage.weth, "WETH");
    }

    /**
     * @notice Executes multiple function calls in a single transaction
     * @param proxyCalls Array of function calls to execute
     * @return results Array of return values from each call
     *
     *      example for collateral = USDC:
     *        multicall([
     *          wrapNative(gas),
     *          depositGas(gas),
     *          transferToken(collateral),
     *          placePositionOrder(positionOrderParams),
     *        ])
     *      example for collateral = ETH:
     *        multicall([
     *          wrapNative(gas),
     *          depositGas(gas),
     *          wrapNative(collateral),
     *          placePositionOrder(positionOrderParams),
     *        ])
     */
    function multicall(bytes[] calldata proxyCalls) external payable returns (bytes[] memory results) {
        results = new bytes[](proxyCalls.length);
        for (uint256 i = 0; i < proxyCalls.length; i++) {
            (bool success, bytes memory returnData) = address(this).delegatecall(proxyCalls[i]);
            AddressUpgradeable.verifyCallResult(success, returnData, "multicallFailed");
            results[i] = returnData;
        }
    }

    /**
     * @notice Trader/LP can wrap native ETH to WETH and WETH will stay in OrderBook for subsequent commands
     *
     *         note: wrapNative is intended to be used as part of a multicall. If it is called directly
     *               the caller would end up losing the funds.
     *         note: wrapNative is intended to be consumed in depositGas or placePositionOrder/placeLiquidityOrder.
     *               Any excess ETH sent beyond the amount parameter will be lost in the contract.
     * @param amount Amount of ETH to wrap
     * @dev Amount must be greater than 0 and less than or equal to msg.value
     */
    function wrapNative(uint256 amount) external payable nonReentrant {
        require(amount > 0 && amount <= msg.value, "Invalid wrap amount");
        IWETH9(_storage.weth).deposit{ value: amount }();
    }

    /**
     * @notice Trader/LP transfer ERC20 tokens (usually collaterals) to the OrderBook
     *
     *         note: transferToken is intended to be used as part of a multicall. If it is called directly
     *               the caller would end up losing the funds.
     *         note: transferToken is intended to be consumed in placePositionOrder/placeLiquidityOrder.
     *               Any excess tokens sent beyond the amount parameter will be lost in the contract.
     * @param token Address of the token to transfer
     * @param amount Amount of tokens to transfer
     */
    function transferToken(address token, uint256 amount) external payable nonReentrant {
        IERC20Upgradeable(token).safeTransferFrom(msg.sender, address(this), amount);
    }

    /**
     * @notice Allows Delegator to transfer tokens from Trader/LP to OrderBook
     * @param from Address to transfer tokens from
     * @param token Address of the token to transfer
     * @param amount Amount of tokens to transfer
     */
    function transferTokenFrom(address from, address token, uint256 amount) external payable nonReentrant {
        require(_isDelegator(msg.sender), "Delegator only");
        IERC20Upgradeable(token).safeTransferFrom(from, address(this), amount);
    }

    /**
     * @notice Trader/LP should pay for gas for their orders
     *         you should pay at least configValue(MCO_ORDER_GAS_FEE_GWEI) * 1e9 / 1e18 ETH for each order
     * @param amount The amount of gas to deposit
     */
    function depositGas(address account, uint256 amount) external payable nonReentrant {
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(account == msg.sender, "Not authorized");
        }
        LibOrderBook.depositGas(_storage, amount, account);
    }

    /**
     * @notice Trader/LP can withdraw gas
     *      usually your deposited gas should be consumed by your orders immediately,
     *      but if you want to withdraw it, you can call this function
     * @param amount The amount of gas to withdraw
     */
    function withdrawGas(address account, uint256 amount) external payable nonReentrant {
        if (_isDelegator(msg.sender)) {
            // although Delegator does not support withdrawGas yet, it is still safe here
        } else {
            require(account == msg.sender, "Not authorized");
        }
        LibOrderBook.withdrawGas(_storage, amount, account);
    }

    /**
     * @notice A trader should set initial leverage at least once before open-position
     * @param positionId The ID of the position
     * @param marketId The ID of the market
     * @param initialLeverage The initial leverage to set
     * @dev do not need depositGas
     */
    function setInitialLeverage(
        bytes32 positionId,
        bytes32 marketId,
        uint256 initialLeverage
    ) external payable updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        LibOrderBook.setInitialLeverage(_storage, positionId, marketId, initialLeverage);
    }

    /**
     * @notice A Trader can open/close position
     *         Market order will expire after marketOrderTimeout seconds.
     *         Limit/Trigger order will expire after deadline.
     * @param orderParams The parameters for the position order
     * @param referralCode The referral code for the position order
     * @dev depositGas required (consume gas when filled)
     */
    function placePositionOrder(
        PositionOrderParams memory orderParams,
        bytes32 referralCode
    ) external payable whenNotPaused(OrderType.PositionOrder) updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        // referral code
        address referralManager = _referralManager();
        if (referralCode != bytes32(0) && referralManager != address(0)) {
            IReferralManager(referralManager).setReferrerCodeFor(positionAccount, referralCode);
        }
        // place
        LibOrderBook.placePositionOrder(_storage, orderParams, _blockTimestamp());
    }

    /**
     * @notice A LP can add/remove liquidity to a CollateralPool
     *         Can be filled after liquidityLockPeriod seconds.
     * @param orderParams The parameters for the liquidity order
     * @dev depositGas required (consume gas when filled)
     */
    function placeLiquidityOrder(
        LiquidityOrderParams memory orderParams
    ) external payable whenNotPaused(OrderType.LiquidityOrder) updateSequence {
        LibOrderBook2.placeLiquidityOrder(_storage, orderParams, msg.sender, _blockTimestamp());
    }

    /**
     * @notice A Trader can withdraw collateral
     *         This order will expire after marketOrderTimeout seconds.
     * @param orderParams The parameters for the withdrawal order
     * @dev depositGas required (consume gas when filled)
     */
    function placeWithdrawalOrder(
        WithdrawalOrderParams memory orderParams
    ) external payable whenNotPaused(OrderType.WithdrawalOrder) updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        LibOrderBook2.placeWithdrawalOrder(_storage, orderParams, _blockTimestamp());
    }

    /**
     * @notice A Trader can deposit collateral into a PositionAccount
     * @param positionId The ID of the position
     * @param collateralToken The address of the collateral token
     * @param collateralAmount The amount of collateral token
     * @dev do not need depositGas
     */
    function depositCollateral(
        bytes32 positionId,
        address collateralToken,
        uint256 collateralAmount // token decimals
    ) external payable nonReentrant updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        LibOrderBook2.depositCollateral(_storage, positionId, collateralToken, collateralAmount);
    }

    /**
     * @notice A Trader can withdraw all collateral only when position = 0
     * @param orderParams The parameters for the withdrawal order
     * @dev do not need depositGas
     */
    function withdrawAllCollateral(
        WithdrawAllOrderParams memory orderParams
    ) external payable nonReentrant whenNotPaused(OrderType.WithdrawalOrder) updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        LibOrderBook2.withdrawAllCollateral(_storage, orderParams);
    }

    /**
     * @notice A Trader can modify a position order
     * @param orderParams The parameters for the modify position order
     * @dev do not need depositGas
     */
    function modifyPositionOrder(
        ModifyPositionOrderParams memory orderParams
    ) external payable whenNotPaused(OrderType.PositionOrder) updateSequence {
        (address positionAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        if (_isDelegator(msg.sender)) {
            // pass
        } else {
            require(positionAccount == msg.sender, "Not authorized");
        }
        // place
        LibOrderBook2.modifyPositionOrder(_storage, orderParams, _blockTimestamp());
    }

    /**
     * @notice A Trader/LP can cancel an Order by orderId after a cool down period.
     *         A Broker can also cancel an Order after expiration.
     * @param orderId The ID of the order to cancel
     */
    function cancelOrder(uint64 orderId) external payable nonReentrant updateSequence {
        LibOrderBook.cancelOrder(_storage, orderId, _blockTimestamp(), msg.sender);
    }

    /**
     * @notice A Rebalancer can rebalance pool liquidity by swap token0 for pool.collateralToken
     *         msg.sender must implement IMux3RebalancerCallback.
     * @param orderParams The parameters for the rebalance order
     * @dev do not need depositGas
     */
    function placeRebalanceOrder(
        RebalanceOrderParams memory orderParams
    ) external onlyRole(REBALANCER_ROLE) whenNotPaused(OrderType.RebalanceOrder) updateSequence {
        address rebalancer = msg.sender;
        LibOrderBook2.placeRebalanceOrder(_storage, rebalancer, orderParams, _blockTimestamp());
    }

    /**
     * @notice Open/close a position. called by Broker
     * @param orderId The ID of the order to fill
     */
    function fillPositionOrder(
        uint64 orderId
    )
        external
        onlyRole(BROKER_ROLE)
        nonReentrant
        whenNotPaused(OrderType.PositionOrder)
        updateSequence
        returns (uint256 tradingPrice)
    {
        return LibOrderBook.fillPositionOrder(_storage, orderId, _blockTimestamp());
    }

    /**
     * @notice Add/remove liquidity. called by Broker
     * @param orderId The ID of the order to fill
     * @param reallocateArgs Arguments to reallocate positions between pools to keep a more balanced utilization (optional)
     * @return outAmount The amount of output tokens
     */
    function fillLiquidityOrder(
        uint64 orderId,
        IFacetOpen.ReallocatePositionArgs[] memory reallocateArgs
    )
        external
        onlyRole(BROKER_ROLE)
        whenNotPaused(OrderType.LiquidityOrder)
        nonReentrant
        updateSequence
        returns (uint256 outAmount)
    {
        return LibOrderBook2.fillLiquidityOrder(_storage, orderId, reallocateArgs, _blockTimestamp());
    }

    /**
     * @notice Withdraw collateral. called by Broker
     * @param orderId The ID of the order to fill
     */
    function fillWithdrawalOrder(
        uint64 orderId
    ) external onlyRole(BROKER_ROLE) nonReentrant whenNotPaused(OrderType.WithdrawalOrder) updateSequence {
        LibOrderBook2.fillWithdrawalOrder(_storage, orderId, _blockTimestamp());
    }

    /**
     * @notice Swap token0 for pool.collateralToken of a pool. called by Broker
     * @param orderId The ID of the order to fill
     */
    function fillRebalanceOrder(
        uint64 orderId
    ) external onlyRole(BROKER_ROLE) nonReentrant whenNotPaused(OrderType.RebalanceOrder) updateSequence {
        LibOrderBook2.fillRebalanceOrder(_storage, orderId);
    }

    /**
     * @notice Liquidate all positions in a PositionAccount. called by Broker
     * @param positionId The ID of the position
     * @param lastConsumedToken The address of the last consumed token
     * @param isWithdrawAllIfEmpty Set false so that collaterals will remain in the position account.
     * @param isUnwrapWeth Whether to unwrap WETH
     */
    function liquidate(
        bytes32 positionId,
        address lastConsumedToken,
        bool isWithdrawAllIfEmpty,
        bool isUnwrapWeth
    ) external onlyRole(BROKER_ROLE) nonReentrant whenNotPaused(OrderType.LiquidateOrder) updateSequence {
        LibOrderBook.liquidate(_storage, positionId, lastConsumedToken, isWithdrawAllIfEmpty, isUnwrapWeth);
    }

    /**
     * @notice Deleverage a position. called by Broker
     * @param positionId The ID of the position
     * @param marketId The ID of the market
     * @param lastConsumedToken The address of the last consumed token
     * @param isWithdrawAllIfEmpty Whether to withdraw all collateral
     * @param isUnwrapWeth Whether to unwrap WETH
     * @return tradingPrice The trading price
     */
    function fillAdlOrder(
        bytes32 positionId,
        bytes32 marketId,
        address lastConsumedToken,
        bool isWithdrawAllIfEmpty,
        bool isUnwrapWeth
    )
        external
        onlyRole(BROKER_ROLE)
        nonReentrant
        whenNotPaused(OrderType.AdlOrder)
        updateSequence
        returns (uint256 tradingPrice)
    {
        return
            LibOrderBook.fillAdlOrder(
                _storage,
                positionId,
                marketId,
                lastConsumedToken,
                isWithdrawAllIfEmpty,
                isUnwrapWeth
            );
    }

    /**
     * @dev Reallocate a position from pool0 to pool1. called by Broker
     */
    function reallocate(
        bytes32 positionId,
        bytes32 marketId,
        address fromPool,
        address toPool,
        uint256 size,
        address lastConsumedToken,
        bool isUnwrapWeth
    ) external onlyRole(BROKER_ROLE) nonReentrant whenNotPaused(OrderType.PositionOrder) updateSequence {
        LibOrderBook.reallocate(
            _storage,
            positionId,
            marketId,
            fromPool,
            toPool,
            size,
            lastConsumedToken,
            isUnwrapWeth
        );
    }

    /**
     * @notice Updates the borrowing fee for a position and market,
     *         allowing LPs to collect fees even if the position remains open.
     * @param positionId The ID of the position
     * @param marketId The ID of the market
     * @param lastConsumedToken The address of the last consumed token
     * @param isUnwrapWeth Whether to unwrap WETH
     */
    function updateBorrowingFee(
        bytes32 positionId,
        bytes32 marketId,
        address lastConsumedToken,
        bool isUnwrapWeth
    ) external onlyRole(BROKER_ROLE) nonReentrant updateSequence {
        LibOrderBook2.updateBorrowingFee(_storage, positionId, marketId, lastConsumedToken, isUnwrapWeth);
    }

    /**
     * @notice Similar to fillLiquidityOrder, but no share minted.
     * @param poolAddress The address of the pool
     * @param collateralAddress The address of the collateral token
     * @param rawAmount The amount of collateral token in token decimals
     * @dev Usually used to send trading fees to CollateralPool
     */
    function donateLiquidity(
        address poolAddress,
        address collateralAddress,
        uint256 rawAmount // token.decimals
    ) external updateSequence {
        require(_isFeeDonator(msg.sender), "Not authorized");
        LibOrderBook.donateLiquidity(_storage, poolAddress, collateralAddress, rawAmount);
    }

    function setConfig(bytes32 key, bytes32 value) external updateSequence {
        _checkRole(MAINTAINER_ROLE, msg.sender);
        LibConfigMap.setBytes32(_storage.configTable, key, value);
    }

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

    function _isFeeDonator(address addr) internal view returns (bool) {
        if (addr == LibOrderBook._feeDistributor(_storage)) {
            // Mux3FeeDistributor is valid
            return true;
        }
        if (hasRole(FEE_DONATOR_ROLE, addr)) {
            // for future use
            return true;
        }
        return false;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlEnumerableUpgradeable.sol";
import "./AccessControlUpgradeable.sol";
import "../utils/structs/EnumerableSetUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;

    mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;

    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        super._grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        super._revokeRole(role, account);
        _roleMembers[role].remove(account);
    }

    /**
     * @dev 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        StringsUpgradeable.toHexString(account),
                        " is missing role ",
                        StringsUpgradeable.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }

    /**
     * @dev 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)

pragma solidity ^0.8.0;

import "./IAccessControlUpgradeable.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

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.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * 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 prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

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

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

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

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";

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

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

    uint256 private _status;

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

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

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);

    /**
     * @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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.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;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    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));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    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");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }

    /**
     * @dev 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

    /**
     * @dev 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;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMathUpgradeable {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

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

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

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

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

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

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 19 of 47 : IBorrowingRate.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

library IBorrowingRate {
    struct Global {
        int256 baseApy;
    }

    /**
     * @dev Borrowing config
     *
     *      k != 0
     *      reserveRate > 0
     *      0e18 < k + b < 10e18
     */
    struct AllocatePool {
        uint256 poolId; // the allocator does not care what is a poolId, you can use any index or address here
        int256 k;
        int256 b;
        int256 poolSizeUsd;
        int256 reservedUsd;
        int256 reserveRate;
        bool isDraining; // whether this pool is draining (only supports deallocate, not allocate)
    }

    struct AllocateResult {
        uint256 poolId; // the allocator does not care what is a poolId, you can use any index or address here
        int256 xi; // result of allocation. unit is usd
    }

    struct DeallocatePool {
        uint256 poolId; // the deallocator does not care what is a poolId, you can use any index or address here
        int256 mySizeForPool; // not necessarily usd. we even do not care about the unit of "mySizeForPool"
    }

    struct DeallocateResult {
        uint256 poolId; // the allocator does not care what is a poolId, you can use any index or address here
        int256 xi; // not necessarily usd. we even do not care about the unit of "mySizeForPool"
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface ICallback {
    function afterLiquidityOrderFilled(
        uint64 orderId,
        uint256 assetAmount,
        uint256 lpAmount,
        uint256 assetPrice,
        uint256 mlpPrice
    ) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface ICallbackRegister {
    function isCallbackRegistered(address callback) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "../interfaces/IBorrowingRate.sol";

struct MarketState {
    bool isLong;
    uint256 totalSize;
    uint256 averageEntryPrice;
    uint256 cumulatedBorrowingPerUsd; // $borrowingFee / $positionValue, always increasing
    uint256 lastBorrowingUpdateTime;
}

interface ICollateralPool {
    function setConfig(bytes32 key, bytes32 value) external;

    function configValue(bytes32 key) external view returns (bytes32);

    function collateralToken() external view returns (address);

    function borrowingFeeRateApy(bytes32 marketId) external view returns (uint256 feeRateApy);

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

    function marketState(bytes32 marketId) external view returns (MarketState memory);

    function marketStates() external view returns (bytes32[] memory marketIds, MarketState[] memory states);

    function setMarket(bytes32 marketId, bool isLong) external;

    function liquidityBalances() external view returns (address[] memory tokens, uint256[] memory balances);

    function getCollateralTokenUsd() external view returns (uint256);

    function getAumUsd() external view returns (uint256);

    function getReservedUsd() external view returns (uint256);

    function openPosition(bytes32 marketId, uint256 size, uint256 entryPrice) external;

    function closePosition(bytes32 marketId, uint256 size, uint256 entryPrice) external;

    function realizeProfit(
        uint256 pnlUsd
    )
        external
        returns (
            address token,
            uint256 wad // 1e18
        );

    function realizeLoss(
        address token,
        uint256 rawAmount // token decimals
    ) external;

    struct AddLiquidityArgs {
        address account; // lp address
        uint256 rawCollateralAmount; // token in. token decimals
        bool isUnwrapWeth; // useful for discount
    }

    struct AddLiquidityResult {
        uint256 shares;
        uint256 collateralPrice;
        uint256 lpPrice;
    }

    function addLiquidity(AddLiquidityArgs memory args) external returns (AddLiquidityResult memory result);

    struct RemoveLiquidityArgs {
        address account; // lp address
        uint256 shares; // token in. 1e18
        address token; // token out
        bool isUnwrapWeth; // useful for discount
        uint256 extraFeeCollateral; // 1e18. send to OrderBook
    }

    struct RemoveLiquidityResult {
        uint256 rawCollateralAmount; // token out. token decimals
        uint256 collateralPrice;
        uint256 lpPrice;
    }

    function removeLiquidity(RemoveLiquidityArgs memory args) external returns (RemoveLiquidityResult memory result);

    function rebalance(
        address rebalancer,
        address token0,
        uint256 rawAmount0, // token0 decimals
        uint256 maxRawAmount1, // collateralToken decimals
        bytes memory userData
    ) external returns (uint256 rawAmount1);

    function receiveFee(
        address token,
        uint256 rawAmount // token.decimals
    ) external;

    function updateMarketBorrowing(bytes32 marketId) external returns (uint256 newCumulatedBorrowingPerUsd);

    function makeBorrowingContext(bytes32 marketId) external view returns (IBorrowingRate.AllocatePool memory);

    function positionPnl(
        bytes32 marketId,
        uint256 size,
        uint256 entryPrice,
        uint256 marketPrice
    ) external view returns (int256 pnlUsd, int256 cappedPnlUsd);
}

File 23 of 47 : IConstants.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "./IRoles.sol";
import "./IErrors.sol";
import "./IKeys.sol";
import "./ILimits.sol";

File 24 of 47 : IErrors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface IErrors {
    // config
    error EssentialConfigNotSet(string key);
    error CapacityExceeded(uint256 capacity, uint256 old, uint256 appending);
    error UnexpectedState(uint256 expected, uint256 actual);

    // params
    error InvalidId(string key);
    error InvalidAmount(string key);
    error InvalidAddress(address addr);
    error InvalidArrayLength(uint256 a, uint256 b);
    error InvalidLotSize(uint256 positionSize, uint256 lotSize);
    error InvalidDecimals(uint256 decimals);
    error UnmatchedDecimals(uint256 decimals, uint256 expectDecimals);
    error InvalidCloseSize(uint256 closingSize, uint256 positionSize);

    // price
    error InvalidPriceTimestamp(uint256 timestamp);
    error MissingPrice(bytes32 oracleId);
    error LimitPriceNotMet(uint256 expected, uint256 actual);

    // access control
    error NotOwner(bytes32 positionId, address caller, address owner);
    error UnauthorizedRole(bytes32 requiredRole, address caller);
    error UnauthorizedAgent(address account, bytes32 positionId);
    error UnauthorizedCaller(address caller);

    // collateral
    error CollateralAlreadyExist(address tokenAddress);
    error CollateralNotExist(address tokenAddress);

    // market
    error InvalidMarketId(bytes32 marketId);
    error MarketNotExists(bytes32 marketId);
    error MarketAlreadyExist(bytes32 marketId);
    error MarketTradeDisabled(bytes32 marketId);
    error MarketFull();

    // pool
    error InsufficientLiquidity(uint256 requiredLiquidity, uint256 liquidityBalance); // 1e18, 1e18
    error DuplicatedAddress(address pool);
    error PoolAlreadyExist(address pool);
    error PoolNotExists(address pool);
    error CreateProxyFailed();
    error PoolBankrupt();

    // account
    error PositionAccountAlreadyExist(bytes32 positionId);
    error PositionAccountNotExist(bytes32 positionId);
    error UnsafePositionAccount(bytes32 positionId, uint256 safeType);
    error SafePositionAccount(bytes32 positionId, uint256 safeType);
    error InsufficientCollateralBalance(address collateralToken, uint256 balance, uint256 requiredAmount);
    error InsufficientCollateralUsd(uint256 requiredUsd, uint256 remainUsd);
    error InsufficientCollateral(uint256 required, uint256 remain);
    error InitialLeverageOutOfRange(uint256 leverage, uint256 leverageLimit);
    error PositionNotClosed(bytes32 positionId);
    error OnlySingleMarketPositionAllowed(bytes32 positionId);

    // potential bugs
    error ArrayAppendFailed();
    error AllocationLengthMismatch(uint256 len1, uint256 len2);
    error AllocationPositionMismatch(uint256 positionSize1, uint256 positionSize2);
    error OutOfBound(uint256 index, uint256 length);
    error BadAllocation(int256 maxX, int256 xi);

    // oracle
    error InvalidPrice(uint256 price);
    error InvalidPriceExpiration(uint256 expiration);
    error PriceExpired(uint256 timestamp, uint256 blockTimestamp);
    error IdMismatch(bytes32 id, bytes32 expectedId);
    error MissingSignature();
    error InvalidSequence(uint256 sequence, uint256 expectedSequence);
    error InvalidSinger(address signer);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface IFacetManagement {
    event AddCollateralToken(address token, uint8 decimals, bool isStable);
    event SetStrictStableId(bytes32 oracleId, bool strictStable);
    event CreateCollateralPool(string name, string symbol, address collateral, uint8 collateralDecimals, address pool);
    event AppendBackedPoolsToMarket(bytes32 marketId, address[] backedPools);
    event SetCollateralPoolImplementation(address newImplementation);
    event CreateMarket(bytes32 marketId, string symbol, bool isLong, address[] backedPools);
    event SetConfig(bytes32 key, bytes32 value);
    event SetMarketConfig(bytes32 marketId, bytes32 key, bytes32 value);
    event SetCollateralPoolConfig(address pool, bytes32 key, bytes32 value);
    event SetCollateralTokenEnabled(address token, bool enabled);
    event SetOracleProvider(address oracleProvider, bool isValid);
    event SetPrice(bytes32 oracleId, address provider, uint256 price, uint256 timestamp);

    function setPrice(bytes32 oracleId, address provider, bytes memory oracleCalldata) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "../interfaces/IPositionAccount.sol";
import "../interfaces/IMarket.sol";

struct AccountReader {
    bytes32 positionId;
    CollateralReader[] collaterals;
    PositionReader[] positions;
}

struct CollateralReader {
    address collateralAddress;
    uint256 collateralAmount;
}

struct PositionReader {
    bytes32 marketId;
    uint256 initialLeverage;
    uint256 lastIncreasedTime;
    uint256 realizedBorrowingUsd;
    PositionPoolReader[] pools;
}

struct PositionPoolReader {
    address poolAddress;
    uint256 size;
    uint256 entryPrice;
    uint256 entryBorrowing;
}

interface IFacetReader {
    /**
     * @dev Get price of a token
     */
    function priceOf(address token) external view returns (uint256 price);

    /**
     * @dev Get price of an OracleId
     */
    function priceOf(bytes32 oracleId) external view returns (uint256 price);

    /**
     * @dev Get core global config
     */
    function configValue(bytes32 key) external view returns (bytes32 value);

    /**
     * @dev Get Market config
     */
    function marketConfigValue(bytes32 marketId, bytes32 key) external view returns (bytes32 value);

    /**
     * @dev Get Market state
     */
    function marketState(bytes32 marketId) external view returns (string memory symbol, bool isLong);

    /**
     * @dev Get Collateral config
     */
    function getCollateralToken(address token) external view returns (bool isExist, uint8 decimals, bool isStable);

    /**
     * @dev List collateral tokens
     */
    function listCollateralTokens() external view returns (address[] memory tokens);

    /**
     * @dev Get CollateralPool config
     */
    function getCollateralPool(address pool) external view returns (bool isExist);

    /**
     * @dev List CollateralPool addresses
     */
    function listCollateralPool() external view returns (address[] memory pools);

    /**
     * @dev List Markets
     */
    function listMarkets() external view returns (bytes32[] memory marketIds);

    /**
     * @dev List backed CollateralPool in a Market
     */
    function listMarketPools(bytes32 marketId) external view returns (BackedPoolState[] memory pools);

    /**
     * @dev List PositionIds of a Trader
     */
    function listPositionIdsOf(address trader) external view returns (bytes32[] memory positionIds);

    /**
     * @dev List active PositionIds
     *
     *      "active" means positionId that likely has positions. positionId with only collateral may not be in this list
     */
    function listActivePositionIds(
        uint256 begin,
        uint256 end
    ) external view returns (bytes32[] memory positionIds, uint256 totalLength);

    /**
     * @dev Get Position of (positionId, marketId)
     */
    function getPositionAccount(
        bytes32 positionId,
        bytes32 marketId
    ) external view returns (PositionReader memory position);

    /**
     * @dev List Collaterals of a PositionAccount
     */
    function listAccountCollaterals(bytes32 positionId) external view returns (CollateralReader[] memory collaterals);

    /**
     * @dev List Positions of a PositionAccount
     */
    function listAccountPositions(bytes32 positionId) external view returns (PositionReader[] memory positions);

    /**
     * @dev List Collaterals and Positions of all PositionAccounts of a Trader
     */
    function listCollateralsAndPositionsOf(address trader) external view returns (AccountReader[] memory positions);

    /**
     * @dev List active Collaterals and Positions
     *
     *      "active" means positionId that likely has positions. positionId with only collateral may not be in this list
     */
    function listActiveCollateralsAndPositions(
        uint256 begin,
        uint256 end
    ) external view returns (AccountReader[] memory positions, uint256 totalLength);

    /**
     * @dev Check if deleverage is allowed
     */
    function isDeleverageAllowed(bytes32 positionId, bytes32 marketId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "./IConstants.sol";

interface IFacetOpen {
    event OpenPosition(
        address indexed owner,
        bytes32 indexed positionId,
        bytes32 indexed marketId,
        bool isLong,
        uint256 size,
        uint256 tradingPrice,
        address[] backedPools,
        uint256[] allocations, // 1e18
        uint256[] newSizes, // 1e18
        uint256[] newEntryPrices, // 1e18
        uint256 positionFeeUsd, // 1e18
        uint256 borrowingFeeUsd, // 1e18
        address[] newCollateralTokens,
        uint256[] newCollateralAmounts // 1e18
    );

    event ReallocatePosition(
        address indexed owner,
        bytes32 indexed positionId,
        bytes32 indexed marketId,
        bool isLong,
        address fromPool,
        address toPool,
        uint256 size,
        uint256 tradingPrice, // the price for settling between pools
        uint256 fromPoolOldEntryPrice, // previous entry price from the fromPool
        address[] backedPools,
        uint256[] newSizes,
        uint256[] newEntryPrices,
        // reallocation doesn't settle upnl for the PositionAccount.
        // this represents pnL settlement between pools where only the fromPool generates pnl
        int256[] poolPnlUsds,
        uint256 borrowingFeeUsd, // 1e18
        address[] newCollateralTokens,
        uint256[] newCollateralAmounts
    );

    struct OpenPositionArgs {
        bytes32 positionId;
        bytes32 marketId;
        uint256 size;
        address lastConsumedToken;
        bool isUnwrapWeth;
    }

    struct OpenPositionResult {
        uint256 tradingPrice;
        uint256 borrowingFeeUsd;
        uint256 positionFeeUsd;
    }

    struct ReallocatePositionArgs {
        bytes32 positionId;
        bytes32 marketId;
        address fromPool;
        address toPool;
        uint256 size;
        address lastConsumedToken;
        bool isUnwrapWeth;
    }

    struct ReallocatePositionResult {
        uint256 tradingPrice;
        uint256 borrowingFeeUsd;
        // note: reallocate does not settle upnl for this PositionAccount
    }

    function openPosition(OpenPositionArgs memory args) external returns (OpenPositionResult memory result);

    function reallocatePosition(
        ReallocatePositionArgs memory args
    ) external returns (ReallocatePositionResult memory result);
}

interface IFacetClose {
    event ClosePosition(
        address indexed owner,
        bytes32 indexed positionId,
        bytes32 indexed marketId,
        bool isLong,
        uint256 size, // closing
        uint256 tradingPrice,
        address[] backedPools,
        uint256[] allocations, // 1e18
        uint256[] newSizes, // 1e18
        uint256[] newEntryPrices, // 1e18
        int256[] poolPnlUsds, // 1e18
        uint256 positionFeeUsd, // 1e18
        uint256 borrowingFeeUsd, // 1e18
        address[] newCollateralTokens,
        uint256[] newCollateralAmounts // 1e18
    );

    event LiquidatePosition(
        address indexed owner,
        bytes32 indexed positionId,
        bytes32 indexed marketId,
        bool isLong,
        uint256 size, // size before liquidate = liquidate size
        uint256 tradingPrice, // 1e18
        address[] backedPools,
        uint256[] allocations, // 1e18
        int256[] poolPnlUsds, // 1e18
        uint256 positionFeeUsd, // 1e18
        uint256 borrowingFeeUsd, // 1e18
        address[] newCollateralTokens,
        uint256[] newCollateralAmounts // 1e18
    );

    struct ClosePositionArgs {
        bytes32 positionId;
        bytes32 marketId;
        uint256 size;
        address lastConsumedToken;
        bool isUnwrapWeth;
    }

    struct ClosePositionResult {
        uint256 tradingPrice;
        int256[] poolPnlUsds;
        uint256 borrowingFeeUsd;
        uint256 positionFeeUsd;
    }

    function closePosition(ClosePositionArgs memory args) external returns (ClosePositionResult memory result);

    struct LiquidateArgs {
        bytes32 positionId;
        address lastConsumedToken;
        bool isUnwrapWeth;
    }

    struct LiquidatePositionResult {
        bytes32 marketId;
        uint256 tradingPrice;
        int256[] poolPnlUsds;
        uint256 borrowingFeeUsd;
        uint256 positionFeeUsd;
    }

    struct LiquidateResult {
        LiquidatePositionResult[] positions;
    }

    function liquidate(LiquidateArgs memory args) external returns (LiquidateResult memory result);
}

interface IFacetPositionAccount {
    event Deposit(
        address indexed owner,
        bytes32 indexed positionId,
        address collateralToken,
        uint256 collateralAmount // token.decimals
    );

    event Withdraw(
        address indexed owner,
        bytes32 indexed positionId,
        address collateralToken,
        uint256 collateralWad, // 1e18
        address withdrawToken, // if swap, this is the tokeOut. if not swap, this is the collateralToken
        uint256 withdrawAmount // token.decimals
    );

    event DepositWithdrawFinish(
        address indexed owner,
        bytes32 indexed positionId,
        uint256 borrowingFeeUsd, // 1e18
        address[] newCollateralTokens,
        uint256[] newCollateralAmounts
    );

    event CreatePositionAccount(address indexed owner, uint256 index, bytes32 indexed positionId);

    event SetInitialLeverage(address indexed owner, bytes32 indexed positionId, bytes32 marketId, uint256 leverage);

    event UpdatePositionBorrowingFee(
        address indexed owner,
        bytes32 indexed positionId,
        bytes32 indexed marketId,
        uint256 borrowingFeeUsd
    );

    function setInitialLeverage(bytes32 positionId, bytes32 marketId, uint256 leverage) external;

    function deposit(bytes32 positionId, address collateralToken, uint256 amount) external;

    struct WithdrawArgs {
        bytes32 positionId;
        address collateralToken;
        uint256 amount;
        address lastConsumedToken;
        bool isUnwrapWeth;
        address withdrawSwapToken;
        uint256 withdrawSwapSlippage;
    }

    function withdraw(WithdrawArgs memory args) external;

    struct WithdrawAllArgs {
        bytes32 positionId;
        bool isUnwrapWeth;
        address withdrawSwapToken;
        uint256 withdrawSwapSlippage;
    }

    function withdrawAll(WithdrawAllArgs memory args) external;

    struct WithdrawUsdArgs {
        bytes32 positionId;
        uint256 collateralUsd; // 1e18
        address lastConsumedToken;
        bool isUnwrapWeth;
        address withdrawSwapToken;
        uint256 withdrawSwapSlippage;
    }

    function withdrawUsd(WithdrawUsdArgs memory args) external;

    function updateBorrowingFee(
        bytes32 positionId,
        bytes32 marketId,
        address lastConsumedToken,
        bool isUnwrapWeth
    ) external;
}

File 28 of 47 : IKeys.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

// ==================== core ====================

// borrowingFeeRate = MC_BORROWING_BASE_APY + E^(MCP_BORROWING_K * util + MCP_BORROWING_B),
// where util = reservedUsd / poolSizeUsd. decimals = 18
bytes32 constant MC_BORROWING_BASE_APY = keccak256("MC_BORROWING_BASE_APY");

// an interval in seconds. CollateralPool collects borrowing fee every interval
bytes32 constant MC_BORROWING_INTERVAL = keccak256("MC_BORROWING_INTERVAL");

// an IMux3FeeDistributor address that receives positionFee, borrowingFee and liquidityFee
bytes32 constant MC_FEE_DISTRIBUTOR = keccak256("MC_FEE_DISTRIBUTOR");

// an ISwapper address that swaps collateral/profit to another collateral token when requested by trader
bytes32 constant MC_SWAPPER = keccak256("MC_SWAPPER");

// for collateral tokens marked as strict stable via IFacetManagement.setStrictStableId,
// oracle prices within the range [1 - MC_STRICT_STABLE_DEVIATION, 1 + MC_STRICT_STABLE_DEVIATION] are normalized to 1.000
// decimals = 18
bytes32 constant MC_STRICT_STABLE_DEVIATION = keccak256("MC_STRICT_STABLE_DEVIATION");

// rebalance slippage between two tokens. decimals = 18
bytes32 constant MC_REBALANCE_SLIPPAGE = keccak256("MC_REBALANCE_SLIPPAGE");

// ==================== market ====================

// positionFee = price * size * MM_POSITION_FEE_RATE when openPosition/closePosition. decimals = 18
bytes32 constant MM_POSITION_FEE_RATE = keccak256("MM_POSITION_FEE_RATE");

// positionFee = price * size * MM_LIQUIDATION_FEE_RATE when liquidatePosition. decimals = 18
bytes32 constant MM_LIQUIDATION_FEE_RATE = keccak256("MM_LIQUIDATION_FEE_RATE");

// when openPosition, require marginBalance >= Σ(price * size * MM_INITIAL_MARGIN_RATE). decimals = 18
bytes32 constant MM_INITIAL_MARGIN_RATE = keccak256("MM_INITIAL_MARGIN_RATE");

// when marginBalance < Σ(price * size * MM_MAINTENANCE_MARGIN_RATE), liquidate is allowed. decimals = 18
bytes32 constant MM_MAINTENANCE_MARGIN_RATE = keccak256("MM_MAINTENANCE_MARGIN_RATE");

// openPosition/closePosition/liquidatePosition size must be a multiple of MM_LOT_SIZE. decimals = 18
bytes32 constant MM_LOT_SIZE = keccak256("MM_LOT_SIZE");

// market price is identified and fetched from oracle using this ID
bytes32 constant MM_ORACLE_ID = keccak256("MM_ORACLE_ID");

// pause trade of a market
bytes32 constant MM_DISABLE_TRADE = keccak256("MM_DISABLE_TRADE");

// pause openPosition of a market. if MM_DISABLE_OPEN && !MM_DISABLE_TRADE, only closePosition is allowed
bytes32 constant MM_DISABLE_OPEN = keccak256("MM_DISABLE_OPEN");

// the maximum open interest limit for a single market. the open interest is constrained by both
// this cap and MCP_ADL_RESERVE_RATE of each pool. decimals = 18
bytes32 constant MM_OPEN_INTEREST_CAP_USD = keccak256("MM_OPEN_INTEREST_CAP_USD");

// ==================== pool ====================

// if not empty, override CollateralPool ERC20 name
bytes32 constant MCP_TOKEN_NAME = keccak256("MCP_TOKEN_NAME");

// if not empty, override CollateralPool ERC20 symbol
bytes32 constant MCP_TOKEN_SYMBOL = keccak256("MCP_TOKEN_SYMBOL");

// liquidityFee = price * liquidity * MCP_LIQUIDITY_FEE_RATE. decimals = 18
bytes32 constant MCP_LIQUIDITY_FEE_RATE = keccak256("MCP_LIQUIDITY_FEE_RATE");

// reject addLiquidity if aumUsdWithoutPnl > MCP_LIQUIDITY_CAP_USD. decimals = 18
bytes32 constant MCP_LIQUIDITY_CAP_USD = keccak256("MCP_LIQUIDITY_CAP_USD");

// borrowingFeeRate = MC_BORROWING_BASE_APY + E^(MCP_BORROWING_K * util + MCP_BORROWING_B), where util = reservedUsd / poolSizeUsd
bytes32 constant MCP_BORROWING_K = keccak256("MCP_BORROWING_K");

// borrowingFeeRate = MC_BORROWING_BASE_APY + E^(MCP_BORROWING_K * util + MCP_BORROWING_B), where util = reservedUsd / poolSizeUsd
bytes32 constant MCP_BORROWING_B = keccak256("MCP_BORROWING_B");

// if true, allocate algorithm will skip this CollateralPool when openPosition
bytes32 constant MCP_IS_DRAINING = keccak256("MCP_IS_DRAINING");

// ==================== pool + market ====================

// reserve = (entryPrice or marketPrice) * positions * MCP_ADL_RESERVE_RATE. affects borrowing fee rate and open interest.
// the open interest is constrained by both this rate and MM_OPEN_INTEREST_CAP_USD of the market. decimals = 18
bytes32 constant MCP_ADL_RESERVE_RATE = keccak256("MCP_ADL_RESERVE_RATE");

// position pnl is capped at (entryPrice or marketPrice) * positions * MCP_ADL_MAX_PNL_RATE. decimals = 18
bytes32 constant MCP_ADL_MAX_PNL_RATE = keccak256("MCP_ADL_MAX_PNL_RATE");

// if upnl > (entryPrice or marketPrice) * positions * MCP_ADL_TRIGGER_RATE, ADL is allowed. decimals = 18
bytes32 constant MCP_ADL_TRIGGER_RATE = keccak256("MCP_ADL_TRIGGER_RATE");

// ==================== order book ====================

// only allow fillLiquidityOrder after this seconds
bytes32 constant MCO_LIQUIDITY_LOCK_PERIOD = keccak256("MCO_LIQUIDITY_LOCK_PERIOD");

// pause position order
bytes32 constant MCO_POSITION_ORDER_PAUSED = keccak256("MCO_POSITION_ORDER_PAUSED");

// pause liquidity order
bytes32 constant MCO_LIQUIDITY_ORDER_PAUSED = keccak256("MCO_LIQUIDITY_ORDER_PAUSED");

// pause withdrawal order
bytes32 constant MCO_WITHDRAWAL_ORDER_PAUSED = keccak256("MCO_WITHDRAWAL_ORDER_PAUSED");

// pause rebalance order
bytes32 constant MCO_REBALANCE_ORDER_PAUSED = keccak256("MCO_REBALANCE_ORDER_PAUSED");

// pause adl order
bytes32 constant MCO_ADL_ORDER_PAUSED = keccak256("MCO_ADL_ORDER_PAUSED");

// pause liquidate order
bytes32 constant MCO_LIQUIDATE_ORDER_PAUSED = keccak256("MCO_LIQUIDATE_ORDER_PAUSED");

// timeout for market order. after this seconds, Broker can cancel the order
bytes32 constant MCO_MARKET_ORDER_TIMEOUT = keccak256("MCO_MARKET_ORDER_TIMEOUT");

// timeout for limit order. after this seconds, Broker can cancel the order
bytes32 constant MCO_LIMIT_ORDER_TIMEOUT = keccak256("MCO_LIMIT_ORDER_TIMEOUT");

// an IReferralManager address
bytes32 constant MCO_REFERRAL_MANAGER = keccak256("MCO_REFERRAL_MANAGER");

// Trader can not cancelOrder before this number of seconds has elapsed
bytes32 constant MCO_CANCEL_COOL_DOWN = keccak256("MCO_CANCEL_COOL_DOWN");

// when calling fillPositionOrder, fillLiquidityOrder, fillWithdrawalOrder, send (MCO_ORDER_GAS_FEE_GWEI * 1e9) ETH
// to Broker as a gas compensation
bytes32 constant MCO_ORDER_GAS_FEE_GWEI = keccak256("MCO_ORDER_GAS_FEE_GWEI");

// minimum order value in USD for adding/removing liquidity
bytes32 constant MCO_MIN_LIQUIDITY_ORDER_USD = keccak256("MCO_MIN_LIQUIDITY_ORDER_USD");

// callback gas limit for liquidity order
bytes32 constant MCO_CALLBACK_GAS_LIMIT = keccak256("MCO_CALLBACK_GAS_LIMIT");

// to verify that callback is whitelisted
bytes32 constant MCO_CALLBACK_REGISTER = keccak256("MCO_CALLBACK_REGISTER");

File 29 of 47 : ILimits.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

uint256 constant MAX_COLLATERAL_TOKENS = 128;
uint256 constant MAX_MARKETS = 128;
uint256 constant MAX_COLLATERAL_POOLS = 256;
uint256 constant MAX_MARKET_BACKED_POOLS = 16;

uint256 constant MAX_COLLATERALS_PER_POSITION_ACCOUNT = 16;
uint256 constant MAX_MARKETS_PER_POSITION_ACCOUNT = 16;
uint256 constant MAX_POSITION_ACCOUNT_PER_TRADER = 64;

uint256 constant MAX_TP_SL_ORDERS = 32;

File 30 of 47 : IMarket.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

struct BackedPoolState {
    address backedPool;
}

struct MarketInfo {
    string symbol;
    bool isLong;
    mapping(bytes32 => bytes32) configs;
    BackedPoolState[] pools;
}

struct AllocationData {
    bytes32 marketId;
    uint256 size;
}

interface IMarket {
    event CollectFee(address feeToken, uint256 wad);
}

File 31 of 47 : IMux3Core.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "../interfaces/IFacetTrade.sol";
import "../interfaces/IFacetManagement.sol";
import "../interfaces/IFacetReader.sol";

struct CollateralTokenInfo {
    bool isExist;
    uint8 decimals;
    bool isStable;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface IMux3FeeDistributor {
    event FeeDistributedToLP(address indexed tokenAddress, address indexed poolAddress, uint256 rawAmount);
    event FeeDistributedAsDiscount(address indexed tokenAddress, address indexed trader, uint256 rawAmount);
    event FeeDistributedAsRebate(address indexed tokenAddress, address indexed trader, uint256 rawAmount);
    event FeeDistributedToVe(address indexed tokenAddress, uint256 rawAmount);
    event ClaimVeReward(address indexed tokenAddress, uint256 rawAmount);

    function updateLiquidityFees(
        address lp,
        address poolAddress,
        address tokenAddress,
        uint256 rawAmount, // token decimals
        bool isUnwrapWeth
    ) external;

    // note: allocation only represents a proportional relationship.
    //       the sum of allocations does not necessarily have to be consistent with the total value.
    function updatePositionFees(
        address trader,
        address[] memory tokenAddresses,
        uint256[] memory rawAmounts, // [amount foreach tokenAddresses], token decimals
        address[] memory backedPools,
        uint256[] memory allocations, // [amount foreach backed pools], decimals = 18
        bool isUnwrapWeth
    ) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

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

enum OrderType {
    None, // 0
    PositionOrder, // 1
    LiquidityOrder, // 2
    WithdrawalOrder, // 3
    RebalanceOrder, // 4
    AdlOrder, // 5
    LiquidateOrder // 6
}

// position order flags
uint256 constant POSITION_OPEN = 0x80; // this flag means open-position; otherwise close-position
uint256 constant POSITION_MARKET_ORDER = 0x40; // this flag only affects order expire time and shows a better effect on UI
uint256 constant POSITION_WITHDRAW_ALL_IF_EMPTY = 0x20; // this flag means auto withdraw all collateral if position.size == 0
// this flag means this is a trigger order (ex: stop-loss order). otherwise this is a limit order (ex: take-profit order)
// |                         | Limit Order             | Trigger Order           |
// +-------------------------+-------------------------+-------------------------+
// | Open long / Close short | fillPrice <= limitPrice | fillPrice >= limitPrice |
// | Close long / Open short | fillPrice >= limitPrice | fillPrice <= limitPrice |
uint256 constant POSITION_TRIGGER_ORDER = 0x10;
// 0x08 was POSITION_TPSL_STRATEGY. not suitable for mux3
// 0x04 was POSITION_SHOULD_REACH_MIN_PROFIT. not suitable for mux3
uint256 constant POSITION_AUTO_DELEVERAGE = 0x02; // denotes that this order is an auto-deleverage order
uint256 constant POSITION_UNWRAP_ETH = 0x100; // unwrap WETH into ETH. only valid when fill close-position, or cancel open-position, or fill liquidity, or cancel liquidity
uint256 constant POSITION_WITHDRAW_PROFIT = 0x200; // withdraw profit - fee. only valid when fill close-position

struct OrderData {
    uint64 id;
    address account;
    OrderType orderType;
    uint8 version;
    uint64 placeOrderTime;
    uint64 gasFeeGwei;
    bytes payload;
}

struct OrderBookStorage {
    address mux3Facet;
    uint64 nextOrderId;
    mapping(uint64 => OrderData) orderData;
    EnumerableSetUpgradeable.UintSet orders;
    mapping(address => EnumerableSetUpgradeable.UintSet) userOrders;
    mapping(bytes32 => mapping(bytes32 => EnumerableSetUpgradeable.UintSet)) tpslOrders; // positionId => marketId => [orderId]
    uint32 sequence; // will be 0 after 0xffffffff
    mapping(address => bool) priceProviders;
    address weth;
    mapping(address => uint256) _reserved1; // was previousTokenBalance
    mapping(address => uint256) gasBalances;
    mapping(bytes32 => bytes32) configTable;
    mapping(address => uint256) previousTokenBalance;
    mapping(address => bool) _reserved2; // was callbackWhitelist
    bytes32[46] __gap;
}

struct PositionOrderParams {
    bytes32 positionId;
    bytes32 marketId;
    uint256 size;
    uint256 flags; // see "constant POSITION_*"
    uint256 limitPrice; // decimals = 18
    uint64 expiration; // timestamp. decimals = 0
    address lastConsumedToken; // when paying fees or losses (for both open and close positions), this token will be consumed last. can be 0 if no preference
    // when openPosition
    // * collateralToken == 0 means do not deposit collateral
    // * collateralToken != 0 means to deposit collateralToken as collateral
    // * deduct fees
    // * open positions
    address collateralToken; // only valid when flags.POSITION_OPEN
    uint256 collateralAmount; // only valid when flags.POSITION_OPEN. erc20.decimals
    // when closePosition, pnl and fees
    // * realize pnl
    // * deduct fees
    // * flags.POSITION_WITHDRAW_PROFIT means also withdraw (profit - fee)
    // * withdrawUsd means to withdraw collateral. this is independent of flags.POSITION_WITHDRAW_PROFIT
    // * flags.POSITION_UNWRAP_ETH means to unwrap WETH into ETH
    uint256 withdrawUsd; // only valid when close a position
    address withdrawSwapToken; // only valid when close a position and withdraw. try to swap to this token
    uint256 withdrawSwapSlippage; // only valid when close a position and withdraw. slippage tolerance for withdrawSwapToken. if swap cannot achieve this slippage, swap will be skipped
    // tpsl strategy, only valid when openPosition
    uint256 tpPriceDiff; // take-profit price will be marketPrice * diff. decimals = 18. leave 0 if no tp
    uint256 slPriceDiff; // stop-loss price will be marketPrice * diff. decimals = 18. leave 0 if no sl
    uint64 tpslExpiration; // timestamp. decimals = 0. only valid when tpPriceDiff > 0 or slPriceDiff > 0
    uint256 tpslFlags; // POSITION_WITHDRAW_ALL_IF_EMPTY, POSITION_WITHDRAW_PROFIT, POSITION_UNWRAP_ETH. only valid when tpPriceDiff > 0 or slPriceDiff > 0
    address tpslWithdrawSwapToken; // only valid when tpPriceDiff > 0 or slPriceDiff > 0
    uint256 tpslWithdrawSwapSlippage; // only valid when tpPriceDiff > 0 or slPriceDiff > 0
}

struct LiquidityOrderParams {
    address poolAddress;
    address token; // which address to add/remove. this must be pool.collateralToken when addLiquidity
    uint256 rawAmount; // erc20.decimals
    bool isAdding;
    bool isUnwrapWeth;
}

struct WithdrawalOrderParams {
    bytes32 positionId;
    address tokenAddress;
    uint256 rawAmount; // erc20.decimals
    bool isUnwrapWeth;
    address lastConsumedToken; // this token will be consumed last. can be 0 if no preference
    address withdrawSwapToken; // try to swap to this token
    uint256 withdrawSwapSlippage; // slippage tolerance for withdrawSwapToken. if swap cannot achieve this slippage, swap will be skipped
}

struct WithdrawAllOrderParams {
    bytes32 positionId;
    bool isUnwrapWeth;
}

struct ModifyPositionOrderParams {
    uint64 orderId;
    bytes32 positionId; // this is to double check if orderId has the same positionId
    uint256 limitPrice; // decimals = 18. leave 0 if not changed
    // tpsl strategy, only valid when openPosition
    uint256 tpPriceDiff; // take-profit price will be marketPrice * diff. decimals = 18. leave 0 if not changed
    uint256 slPriceDiff; // stop-loss price will be marketPrice * diff. decimals = 18. leave 0 if not changed
}

struct RebalanceOrderParams {
    address poolAddress;
    address token0;
    uint256 rawAmount0; // erc20.decimals
    uint256 maxRawAmount1; // erc20.decimals
    bytes userData;
}

struct AdlOrderParams {
    bytes32 positionId;
    bytes32 marketId;
    uint256 size; // 1e18
    uint256 price; // 1e18
    bool isUnwrapWeth;
}

interface IOrderBook {
    event UpdateSequence(uint32 sequence);
    event CancelOrder(address indexed account, uint64 indexed orderId, OrderData orderData);
    event NewLiquidityOrder(address indexed account, uint64 indexed orderId, LiquidityOrderParams params);
    event NewPositionOrder(address indexed account, uint64 indexed orderId, PositionOrderParams params);
    event NewWithdrawalOrder(address indexed account, uint64 indexed orderId, WithdrawalOrderParams params);
    event NewRebalanceOrder(address indexed rebalancer, uint64 indexed orderId, RebalanceOrderParams params);
    event FillOrder(address indexed account, uint64 indexed orderId, OrderData orderData);
    event FillAdlOrder(address indexed account, AdlOrderParams params);
    event CallbackFailed(address indexed account, uint64 indexed orderId, bytes reason);
    event ModifyPositionOrder(address indexed account, uint64 indexed orderId, ModifyPositionOrderParams params);

    /**
     * @dev Trader/LP can wrap ETH to OrderBook, transfer ERC20 to OrderBook, placeOrders
     *
     *      example for collateral = USDC:
     *        multicall([
     *          wrapNative(gas),
     *          depositGas(gas),
     *          transferToken(collateral),
     *          placePositionOrder(positionOrderParams),
     *        ])
     *      example for collateral = ETH:
     *        multicall([
     *          wrapNative(gas),
     *          depositGas(gas),
     *          wrapNative(collateral),
     *          placePositionOrder(positionOrderParams),
     *        ])
     */
    function multicall(bytes[] calldata proxyCalls) external payable returns (bytes[] memory results);

    /**
     * @dev Trader/LP can wrap ETH to OrderBook
     *
     *      note: wrapNative is intended to be used as part of a multicall. If it is called directly
     *            the caller would end up losing the funds.
     *      note: wrapNative is intended to be consumed in depositGas or placePositionOrder/placeLiquidityOrder.
     *            Any excess ETH sent beyond the amount parameter will be lost in the contract.
     */
    function wrapNative(uint256 amount) external payable;

    /**
     * @dev Trader/LP can transfer ERC20 to OrderBook
     *
     *      note: transferToken is intended to be used as part of a multicall. If it is called directly
     *            the caller would end up losing the funds.
     *      note: transferToken is intended to be consumed in placePositionOrder/placeLiquidityOrder.
     *            Any excess tokens sent beyond the amount parameter will be lost in the contract.
     */
    function transferToken(address token, uint256 amount) external payable;

    /**
     * @dev Delegator can transfer ERC20 from Trader/LP to OrderBook
     */
    function transferTokenFrom(address from, address token, uint256 amount) external payable;

    /**
     * @dev Trader/LP should pay for gas for their orders
     *
     *      you should pay configValue(MCO_ORDER_GAS_FEE_GWEI) * 1e9 / 1e18 ETH for each order
     */
    function depositGas(address account, uint256 amount) external payable;

    /**
     * @dev Trader/LP can withdraw gas
     *
     *      usually your deposited gas should be consumed by your orders immediately,
     *      but if you want to withdraw it, you can call this function
     */
    function withdrawGas(address account, uint256 amount) external payable;

    /**
     * @notice A trader should set initial leverage at least once before open-position
     */
    function setInitialLeverage(bytes32 positionId, bytes32 marketId, uint256 initialLeverage) external payable;

    /**
     * @notice A Trader can open/close position
     *
     *         Market order will expire after marketOrderTimeout seconds.
     *         Limit/Trigger order will expire after deadline.
     */
    function placePositionOrder(PositionOrderParams memory orderParams, bytes32 referralCode) external payable;

    /**
     * @notice A LP can add/remove liquidity to a CollateralPool
     *
     *         Can be filled after liquidityLockPeriod seconds.
     */
    function placeLiquidityOrder(LiquidityOrderParams memory orderParams) external payable;

    /**
     * @notice A Trader can withdraw collateral
     *
     *         This order will expire after marketOrderTimeout seconds.
     */
    function placeWithdrawalOrder(WithdrawalOrderParams memory orderParams) external payable;

    /**
     * @notice A Trader can deposit collateral into a PositionAccount
     */
    function depositCollateral(
        bytes32 positionId,
        address collateralToken,
        uint256 collateralAmount // token decimals
    ) external payable;

    /**
     * @notice A Trader can withdraw all collateral only when position = 0
     */
    function withdrawAllCollateral(WithdrawAllOrderParams memory orderParams) external payable;

    /**
     * @notice A Trader can modify a position order
     */
    function modifyPositionOrder(ModifyPositionOrderParams memory orderParams) external payable;

    /**
     * @notice A Trader/LP can cancel an Order by orderId after a cool down period.
     *         A Broker can also cancel an Order after expiration.
     */
    function cancelOrder(uint64 orderId) external payable;

    /**
     * @notice A Rebalancer can rebalance pool liquidity by swap token 0 for token 1
     *
     *         msg.sender must implement IMux3RebalancerCallback.
     */
    function placeRebalanceOrder(RebalanceOrderParams memory orderParams) external;

    /**
     * @notice Add liquidity to a CollateralPool without mint shares
     */
    function donateLiquidity(
        address poolAddress,
        address collateralAddress,
        uint256 rawAmount // token.decimals
    ) external;
}

interface IOrderBookGetter {
    function nextOrderId() external view returns (uint64);

    function sequence() external view returns (uint32);

    function configValue(bytes32 key) external view returns (bytes32);

    function getOrder(uint64 orderId) external view returns (OrderData memory, bool);

    function getOrders(
        uint256 begin,
        uint256 end
    ) external view returns (OrderData[] memory orderDataArray, uint256 totalCount);

    function getOrdersOf(
        address user,
        uint256 begin,
        uint256 end
    ) external view returns (OrderData[] memory orderDataArray, uint256 totalCount);
}

File 34 of 47 : IPositionAccount.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

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

uint256 constant SAFE_INITIAL_MARGIN = 0x1;
uint256 constant SAFE_MAINTENANCE_MARGIN = 0x2;
uint256 constant SAFE_LEVERAGE = 0x3;

struct PositionAccountInfo {
    address owner;
    EnumerableSetUpgradeable.AddressSet activeCollaterals;
    EnumerableSetUpgradeable.Bytes32Set activeMarkets;
    mapping(address => uint256) collaterals; // decimals = 18
    mapping(bytes32 => PositionData) positions; // marketId (implied isLong) => PositionData
}

struct PositionData {
    uint256 initialLeverage;
    uint256 lastIncreasedTime;
    uint256 realizedBorrowingUsd;
    mapping(address => PositionPoolData) pools; // poolId => PositionPoolData
}

struct PositionPoolData {
    uint256 size;
    uint256 entryPrice;
    uint256 entryBorrowing;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

// first introduced in mux-staking
interface IReferralManager {
    struct TierSetting {
        uint8 tier;
        uint64 stakeThreshold;
        uint64 discountRate; // 1e5. ex: 2500 = 2.5%
        uint64 rebateRate; // 1e5. ex: 2500 = 2.5%
    }

    event RegisterReferralCode(address referralCodeOwner, bytes32 referralCode);
    event SetReferralCode(address trader, bytes32 referralCode);
    event SetHandler(address handler, bool enable);
    event SetTiers(TierSetting[] newTierSettings);
    event SetMaintainer(address previousMaintainer, address newMaintainer);
    event SetRebateRecipient(bytes32 referralCode, address referralCodeOwner, address rebateRecipient);
    event TransferReferralCode(bytes32 referralCode, address previousOwner, address newOwner);

    function isHandler(address handler) external view returns (bool);

    function rebateRecipients(bytes32 referralCode) external view returns (address);

    // management methods
    function setHandler(address handler, bool enable) external;

    function setTiers(TierSetting[] memory newTierSettings) external;

    // methods only available on primary network
    function isValidReferralCode(bytes32 referralCode) external view returns (bool);

    function registerReferralCode(bytes32 referralCode, address rebateRecipient) external;

    function setRebateRecipient(bytes32 referralCode, address rebateRecipient) external;

    function transferReferralCode(bytes32 referralCode, address newOwner) external;

    // methods available on secondary network
    function getReferralCodeOf(address trader) external view returns (bytes32, uint256);

    function setReferrerCode(bytes32 referralCode) external;

    function setReferrerCodeFor(address trader, bytes32 referralCode) external;

    function tierSettings(
        uint256 tier
    ) external view returns (uint8 retTier, uint64 stakeThreshold, uint64 discountRate, uint64 rebateRate);
}

File 36 of 47 : IRoles.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

bytes32 constant PRICE_SETTER_ROLE = keccak256("PRICE_SETTER_ROLE");
bytes32 constant ORDER_BOOK_ROLE = keccak256("ORDER_BOOK_ROLE");
bytes32 constant BROKER_ROLE = keccak256("BROKER_ROLE");
bytes32 constant MAINTAINER_ROLE = keccak256("MAINTAINER_ROLE");
bytes32 constant DELEGATOR_ROLE = keccak256("DELEGATOR_ROLE");
bytes32 constant FEE_DISTRIBUTOR_USER_ROLE = keccak256("FEE_DISTRIBUTOR_USER_ROLE");
bytes32 constant REBALANCER_ROLE = keccak256("REBALANCER_ROLE");
bytes32 constant FEE_DONATOR_ROLE = keccak256("FEE_DONATOR_ROLE");
bytes32 constant ORACLE_SIGNER = keccak256("ORACLE_SIGNER");

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

interface IWETH9 {
    function deposit() external payable;

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

    function withdraw(uint256) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

library LibCodec {
    // |----- 160 -----|------ 96 ------|
    // | user address  | position index |
    //
    // note:
    // * positionIndex == 0: the PositionAccount can have multiple collaterals and multiple market positions
    // * positionIndex != 0: the PositionAccount can have multiple collaterals but only single market position
    function decodePositionId(bytes32 positionId) internal pure returns (address trader, uint96 positionIndex) {
        trader = address(bytes20(positionId));
        positionIndex = uint96(uint256(positionId));
    }

    function encodePositionId(address trader, uint96 positionIndex) internal pure returns (bytes32) {
        return bytes32(bytes20(trader)) | bytes32(uint256(positionIndex));
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "./LibTypeCast.sol";

library LibConfigMap {
    using LibTypeCast for bytes32;
    using LibTypeCast for address;
    using LibTypeCast for uint256;
    using LibTypeCast for bool;

    event SetValue(bytes32 key, bytes32 value);

    // ================================== single functions ======================================

    function setUint256(mapping(bytes32 => bytes32) storage store, bytes32 key, uint256 value) internal {
        setBytes32(store, key, bytes32(value));
    }

    function setAddress(mapping(bytes32 => bytes32) storage store, bytes32 key, address value) internal {
        setBytes32(store, key, bytes32(bytes20(value)));
    }

    function setBytes32(mapping(bytes32 => bytes32) storage store, bytes32 key, bytes32 value) internal {
        store[key] = value;
        emit SetValue(key, value);
    }

    function setBoolean(mapping(bytes32 => bytes32) storage store, bytes32 key, bool flag) internal {
        bytes32 value = bytes32(uint256(flag ? 1 : 0));
        setBytes32(store, key, value);
    }

    function getBytes32(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (bytes32) {
        return store[key];
    }

    function getUint256(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (uint256) {
        return store[key].toUint256();
    }

    function getInt256(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (int256) {
        return store[key].toInt256();
    }

    function getAddress(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (address) {
        return store[key].toAddress();
    }

    function getBoolean(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (bool) {
        return store[key].toBoolean();
    }

    function getString(mapping(bytes32 => bytes32) storage store, bytes32 key) internal view returns (string memory) {
        return toString(store[key]);
    }

    function toBytes32(address a) internal pure returns (bytes32) {
        return bytes32(bytes20(a));
    }

    function toString(bytes32 b) internal pure returns (string memory) {
        uint256 length = 0;
        while (length < 32 && b[length] != 0) {
            length++;
        }
        bytes memory bytesArray = new bytes(length);
        for (uint256 i = 0; i < length; i++) {
            bytesArray[i] = b[i];
        }
        return string(bytesArray);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "../interfaces/IWETH9.sol";
import "../interfaces/IErrors.sol";

library LibEthUnwrapper {
    uint256 constant DEFAULT_GAS_LIMIT = 50_000;

    /**
     * @dev Unwrap WETH into ETH and send to `to`
     *
     *      assume the current contract has enough WETH balance.
     */
    function unwrap(address weth, address payable to, uint256 rawAmount) internal returns (bool) {
        require(to != address(0), IErrors.InvalidAddress(to));
        if (rawAmount == 0) {
            return false;
        }

        // wrap
        IWETH9(weth).withdraw(rawAmount);

        // send
        bool success;
        // use an assembly call to avoid loading large data into memory
        // input mem[in…(in+insize)]
        // output area mem[out…(out+outsize))]
        assembly {
            success := call(
                DEFAULT_GAS_LIMIT, // gas limit
                to, // receiver
                rawAmount, // value
                0, // in
                0, // insize
                0, // out
                0 // outsize
            )
        }
        if (success) {
            return true;
        }

        // wrap and send WETH
        IWETH9(weth).deposit{ value: rawAmount }();
        IWETH9(weth).transfer(to, rawAmount);
        return false;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "../interfaces/IOrderBook.sol";

library LibOrder {
    function encodePositionOrder(
        PositionOrderParams memory orderParams,
        uint64 orderId,
        address account,
        uint64 blockTimestamp,
        uint64 gasFeeGwei
    ) internal pure returns (OrderData memory orderData) {
        orderData.orderType = OrderType.PositionOrder;
        orderData.id = orderId;
        orderData.version = 1;
        orderData.placeOrderTime = blockTimestamp;
        orderData.account = account;
        orderData.gasFeeGwei = gasFeeGwei;
        orderData.payload = abi.encode(orderParams);
    }

    function decodePositionOrder(
        OrderData memory orderData
    ) internal pure returns (PositionOrderParams memory orderParams) {
        require(orderData.orderType == OrderType.PositionOrder, "Unexpected order type");
        require(orderData.version == 1, "Unexpected order version");
        require(orderData.payload.length == 18 * 32, "Unexpected order payload length");
        orderParams = abi.decode(orderData.payload, (PositionOrderParams));
    }

    function encodeLiquidityOrder(
        LiquidityOrderParams memory orderParams,
        uint64 orderId,
        address account,
        uint64 blockTimestamp,
        uint64 gasFeeGwei
    ) internal pure returns (OrderData memory orderData) {
        orderData.orderType = OrderType.LiquidityOrder;
        orderData.id = orderId;
        orderData.version = 1;
        orderData.placeOrderTime = blockTimestamp;
        orderData.account = account;
        orderData.gasFeeGwei = gasFeeGwei;
        orderData.payload = abi.encode(orderParams);
    }

    function decodeLiquidityOrder(
        OrderData memory orderData
    ) internal pure returns (LiquidityOrderParams memory orderParams) {
        require(orderData.orderType == OrderType.LiquidityOrder, "Unexpected order type");
        require(orderData.version == 1, "Unexpected order version");
        require(orderData.payload.length == 5 * 32, "Unexpected order payload length");
        orderParams = abi.decode(orderData.payload, (LiquidityOrderParams));
    }

    function encodeWithdrawalOrder(
        WithdrawalOrderParams memory orderParams,
        uint64 orderId,
        uint64 blockTimestamp,
        address account,
        uint64 gasFeeGwei
    ) internal pure returns (OrderData memory orderData) {
        orderData.orderType = OrderType.WithdrawalOrder;
        orderData.id = orderId;
        orderData.version = 1;
        orderData.placeOrderTime = blockTimestamp;
        orderData.account = account;
        orderData.gasFeeGwei = gasFeeGwei;
        orderData.payload = abi.encode(orderParams);
    }

    function decodeWithdrawalOrder(
        OrderData memory orderData
    ) internal pure returns (WithdrawalOrderParams memory orderParams) {
        require(orderData.orderType == OrderType.WithdrawalOrder, "Unexpected order type");
        require(orderData.version == 1, "Unexpected order version");
        require(orderData.payload.length == 7 * 32, "Unexpected order payload length");
        orderParams = abi.decode(orderData.payload, (WithdrawalOrderParams));
    }

    function encodeRebalanceOrder(
        RebalanceOrderParams memory orderParams,
        uint64 orderId,
        uint64 blockTimestamp,
        address rebalancer
    ) internal pure returns (OrderData memory orderData) {
        orderData.orderType = OrderType.RebalanceOrder;
        orderData.id = orderId;
        orderData.version = 1;
        orderData.placeOrderTime = blockTimestamp;
        orderData.account = rebalancer;
        orderData.payload = abi.encode(orderParams);
    }

    function decodeRebalanceOrder(
        OrderData memory orderData
    ) internal pure returns (RebalanceOrderParams memory orderParams) {
        require(orderData.orderType == OrderType.RebalanceOrder, "Unexpected order type");
        require(orderData.version == 1, "Unexpected order version");
        require(orderData.payload.length >= 5 * 32, "Unexpected order payload length"); // poolAddress, token0, rawAmount0, maxRawAmount1, userData
        orderParams = abi.decode(orderData.payload, (RebalanceOrderParams));
    }

    function isOpenPosition(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_OPEN) != 0;
    }

    function isMarketOrder(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_MARKET_ORDER) != 0;
    }

    function isWithdrawAllIfEmpty(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_WITHDRAW_ALL_IF_EMPTY) != 0;
    }

    function isTriggerOrder(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_TRIGGER_ORDER) != 0;
    }

    function isAdl(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_AUTO_DELEVERAGE) != 0;
    }

    function isUnwrapWeth(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_UNWRAP_ETH) != 0;
    }

    function isWithdrawProfit(PositionOrderParams memory orderParams) internal pure returns (bool) {
        return (orderParams.flags & POSITION_WITHDRAW_PROFIT) != 0;
    }
}

File 42 of 47 : LibOrderBook.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";

import "../interfaces/ICollateralPool.sol";
import "../interfaces/IMux3Core.sol";
import "../interfaces/IMux3FeeDistributor.sol";
import "../interfaces/IMarket.sol";
import "../interfaces/IOrderBook.sol";
import "../interfaces/IWETH9.sol";
import "../libraries/LibCodec.sol";
import "../libraries/LibConfigMap.sol";
import "../libraries/LibEthUnwrapper.sol";
import "../libraries/LibOrder.sol";

library LibOrderBook {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using LibTypeCast for bytes32;
    using LibTypeCast for uint256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using LibConfigMap for mapping(bytes32 => bytes32);

    function _appendOrder(OrderBookStorage storage orderBook, OrderData memory orderData) internal {
        orderBook.orderData[orderData.id] = orderData;
        require(
            orderBook.orders.add(orderData.id) && orderBook.userOrders[orderData.account].add(orderData.id),
            "Failed to append order"
        );
    }

    function _removeOrder(OrderBookStorage storage orderBook, OrderData memory orderData) internal {
        require(
            orderBook.userOrders[orderData.account].remove(orderData.id) && orderBook.orders.remove(orderData.id),
            "Failed to remove order"
        );
        delete orderBook.orderData[orderData.id];
    }

    function donateLiquidity(
        OrderBookStorage storage orderBook,
        address poolAddress,
        address collateralAddress,
        uint256 rawAmount // token.decimals
    ) external {
        require(rawAmount != 0, "Zero amount");
        LibOrderBook._validateCollateral(orderBook, collateralAddress);
        LibOrderBook._validatePool(orderBook, poolAddress);
        LibOrderBook._transferIn(orderBook, collateralAddress, rawAmount);
        LibOrderBook._transferOut(orderBook, collateralAddress, poolAddress, rawAmount, false);
        ICollateralPool(poolAddress).receiveFee(collateralAddress, rawAmount);
    }

    function placePositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint64 blockTimestamp
    ) external {
        _validateMarketId(orderBook, orderParams.marketId);
        require(orderParams.size != 0, "Position order size = 0");
        {
            uint256 lotSize = _lotSize(orderBook, orderParams.marketId);
            require(orderParams.size % lotSize == 0, "size must be a multiple of lot size");
        }
        require(!LibOrder.isAdl(orderParams), "ADL is not allowed");
        require(orderParams.limitPrice > 0, "Position order must have limitPrice");
        require(orderParams.expiration > blockTimestamp, "Expiration is earlier than now");
        if (orderParams.lastConsumedToken != address(0)) {
            _validateCollateral(orderBook, orderParams.lastConsumedToken);
        }
        if (LibOrder.isOpenPosition(orderParams)) {
            _placeOpenPositionOrder(orderBook, orderParams, blockTimestamp);
        } else {
            _placeClosePositionOrder(orderBook, orderParams, blockTimestamp);
        }
    }

    function _placeOpenPositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint64 blockTimestamp
    ) private {
        require(orderParams.withdrawUsd == 0, "WithdrawUsd is not suitable for open-position");
        require(orderParams.withdrawSwapToken == address(0), "WithdrawSwapToken is not suitable for open-position");
        require(orderParams.withdrawSwapSlippage == 0, "WithdrawSwapSlippage is not suitable for open-position");
        // fetch collateral
        if (orderParams.collateralToken != address(0)) {
            _validateCollateral(orderBook, orderParams.collateralToken);
            if (orderParams.collateralAmount > 0) {
                // deposit collateral
                _transferIn(orderBook, orderParams.collateralToken, orderParams.collateralAmount);
            }
        }
        // tp/sl strategy
        if (orderParams.tpPriceDiff > 0 || orderParams.slPriceDiff > 0) {
            require(orderParams.tpslExpiration > blockTimestamp, "tpslExpiration is earlier than now");
            uint256 validFlags = POSITION_WITHDRAW_ALL_IF_EMPTY | POSITION_WITHDRAW_PROFIT | POSITION_UNWRAP_ETH;
            require((orderParams.tpslFlags & (~validFlags)) == 0, "Unsupported tpslFlags");
            if (orderParams.tpslWithdrawSwapToken != address(0)) {
                _validateCollateral(orderBook, orderParams.tpslWithdrawSwapToken);
            }
            bool isLong = _isMarketLong(orderBook, orderParams.marketId);
            if (isLong) {
                // close a long means sell, sl means limitPrice = tradingPrice * (1 - slPriceDiff)
                require(orderParams.slPriceDiff < 1e18, "slPriceDiff too large");
            } else {
                // close a short means buy, tp means limitPrice = tradingPrice * (1 - tpPriceDiff)
                require(orderParams.tpPriceDiff < 1e18, "tpPriceDiff too large");
            }
            require(orderParams.tpslWithdrawSwapSlippage <= 1e18, "tpslWithdrawSwapSlippage too large");
        }
        // add order
        uint64 gasFeeGwei = _orderGasFeeGwei(orderBook);
        _appendPositionOrder(orderBook, orderParams, blockTimestamp, gasFeeGwei);
    }

    function _placeClosePositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint64 blockTimestamp
    ) private {
        require(orderParams.collateralToken == address(0) && orderParams.collateralAmount == 0, "Use withdraw instead");
        if (orderParams.withdrawSwapToken != address(0)) {
            _validateCollateral(orderBook, orderParams.withdrawSwapToken);
        }
        require(orderParams.withdrawSwapSlippage <= 1e18, "withdrawSwapSlippage too large");
        // tp/sl strategy is not supported
        require(
            orderParams.tpPriceDiff == 0 &&
                orderParams.slPriceDiff == 0 &&
                orderParams.tpslExpiration == 0 &&
                orderParams.tpslFlags == 0 &&
                orderParams.tpslWithdrawSwapToken == address(0) &&
                orderParams.tpslWithdrawSwapSlippage == 0,
            "Place multiple close-position orders instead"
        );
        // add order
        uint64 gasFeeGwei = _orderGasFeeGwei(orderBook);
        _appendPositionOrder(orderBook, orderParams, blockTimestamp, gasFeeGwei);
    }

    function _cancelActivatedTpslOrders(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId
    ) internal {
        EnumerableSetUpgradeable.UintSet storage orderIds = orderBook.tpslOrders[positionId][marketId];
        uint256 length = orderIds.length();
        for (uint256 i = 0; i < length; i++) {
            uint64 orderId = uint64(orderIds.at(i));
            require(orderBook.orders.contains(orderId), "No such orderId");
            OrderData memory orderData = orderBook.orderData[orderId];
            OrderType orderType = OrderType(orderData.orderType);
            require(orderType == OrderType.PositionOrder, "Order type mismatch");
            PositionOrderParams memory orderParams = LibOrder.decodePositionOrder(orderData);
            require(
                !LibOrder.isOpenPosition(orderParams) && orderParams.collateralAmount == 0,
                "TP/SL order should be a CLOSE order and without collateralAmount"
            );
            _removeOrder(orderBook, orderData);
            emit IOrderBook.CancelOrder(orderData.account, orderId, orderData);
        }
        _clearTpslOrders(orderBook.tpslOrders[positionId][marketId]);
    }

    function _clearTpslOrders(EnumerableSetUpgradeable.UintSet storage orders) internal {
        for (uint256 len = orders.length(); len > 0; len--) {
            orders.remove(orders.at(len - 1));
        }
    }

    function fillPositionOrder(
        OrderBookStorage storage orderBook,
        uint64 orderId,
        uint64 blockTimestamp
    ) external returns (uint256 tradingPrice) {
        require(orderBook.orders.contains(orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[orderId];
        _removeOrder(orderBook, orderData);
        require(orderData.orderType == OrderType.PositionOrder, "Order type mismatch");
        PositionOrderParams memory orderParams = LibOrder.decodePositionOrder(orderData);
        uint256 deadline = MathUpgradeable
            .min(orderData.placeOrderTime + _positionOrderTimeout(orderBook, orderParams), orderParams.expiration)
            .toUint64();
        require(blockTimestamp <= deadline, "Order expired");
        // fill
        if (LibOrder.isOpenPosition(orderParams)) {
            tradingPrice = _fillOpenPositionOrder(orderBook, orderParams, blockTimestamp);
        } else {
            tradingPrice = _fillClosePositionOrder(orderBook, orderParams, orderId);
        }
        // price check
        // open,long      0,0   0,1   1,1   1,0
        // limitOrder     <=    >=    <=    >=
        // triggerOrder   >=    <=    >=    <=
        bool isLong = _isMarketLong(orderBook, orderParams.marketId);
        bool isLess = (isLong == LibOrder.isOpenPosition(orderParams));
        if (LibOrder.isTriggerOrder(orderParams)) {
            isLess = !isLess;
        }
        if (isLess) {
            require(tradingPrice <= orderParams.limitPrice, "limitPrice");
        } else {
            require(tradingPrice >= orderParams.limitPrice, "limitPrice");
        }
        emit IOrderBook.FillOrder(orderData.account, orderId, orderData);
        // gas
        _payGasFee(orderBook, orderData, msg.sender);
    }

    function _fillOpenPositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint64 blockTimestamp
    ) private returns (uint256 tradingPrice) {
        // auto deposit
        if (orderParams.collateralToken != address(0) && orderParams.collateralAmount > 0) {
            // deposit collateral
            _transferOut(
                orderBook,
                orderParams.collateralToken,
                address(orderBook.mux3Facet),
                orderParams.collateralAmount,
                false // unwrap eth
            );
            IFacetPositionAccount(orderBook.mux3Facet).deposit(
                orderParams.positionId,
                orderParams.collateralToken,
                orderParams.collateralAmount
            );
        }
        // open
        IFacetOpen.OpenPositionResult memory result = IFacetOpen(orderBook.mux3Facet).openPosition(
            IFacetOpen.OpenPositionArgs({
                positionId: orderParams.positionId,
                marketId: orderParams.marketId,
                size: orderParams.size,
                lastConsumedToken: orderParams.lastConsumedToken,
                isUnwrapWeth: LibOrder.isUnwrapWeth(orderParams)
            })
        );
        tradingPrice = result.tradingPrice;
        // tp/sl strategy
        if (orderParams.tpPriceDiff > 0 || orderParams.slPriceDiff > 0) {
            _placeTpslOrders(orderBook, orderParams, tradingPrice, blockTimestamp);
        }
    }

    function _fillClosePositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint64 orderId
    ) private returns (uint256 tradingPrice) {
        // close
        IFacetClose.ClosePositionResult memory result = IFacetClose(orderBook.mux3Facet).closePosition(
            IFacetClose.ClosePositionArgs({
                positionId: orderParams.positionId,
                marketId: orderParams.marketId,
                size: orderParams.size,
                lastConsumedToken: orderParams.lastConsumedToken,
                isUnwrapWeth: LibOrder.isUnwrapWeth(orderParams)
            })
        );
        tradingPrice = result.tradingPrice;
        // auto withdraw
        uint256 withdrawUsd = orderParams.withdrawUsd;
        if (LibOrder.isWithdrawProfit(orderParams)) {
            int256 pnlUsd = 0;
            for (uint256 i = 0; i < result.poolPnlUsds.length; i++) {
                pnlUsd += result.poolPnlUsds[i];
            }
            pnlUsd -= result.borrowingFeeUsd.toInt256();
            pnlUsd -= result.positionFeeUsd.toInt256();
            if (pnlUsd > 0) {
                withdrawUsd += uint256(pnlUsd);
            }
        }
        // auto withdraw
        if (withdrawUsd > 0) {
            IFacetPositionAccount(orderBook.mux3Facet).withdrawUsd(
                IFacetPositionAccount.WithdrawUsdArgs({
                    positionId: orderParams.positionId,
                    collateralUsd: withdrawUsd,
                    lastConsumedToken: orderParams.lastConsumedToken,
                    isUnwrapWeth: LibOrder.isUnwrapWeth(orderParams),
                    withdrawSwapToken: orderParams.withdrawSwapToken,
                    withdrawSwapSlippage: orderParams.withdrawSwapSlippage
                })
            );
        }
        // remove the current order from tp/sl list
        orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].remove(uint256(orderId));
        // is the position completely closed
        if (_isPositionAccountFullyClosed(orderBook, orderParams.positionId)) {
            // auto withdraw
            if (LibOrder.isWithdrawAllIfEmpty(orderParams)) {
                IFacetPositionAccount(orderBook.mux3Facet).withdrawAll(
                    IFacetPositionAccount.WithdrawAllArgs({
                        positionId: orderParams.positionId,
                        isUnwrapWeth: LibOrder.isUnwrapWeth(orderParams),
                        withdrawSwapToken: orderParams.withdrawSwapToken,
                        withdrawSwapSlippage: orderParams.withdrawSwapSlippage
                    })
                );
            }
        }
        if (_isPositionAccountMarketFullyClosed(orderBook, orderParams.positionId, orderParams.marketId)) {
            // cancel activated tp/sl orders
            _cancelActivatedTpslOrders(orderBook, orderParams.positionId, orderParams.marketId);
        }
    }

    function liquidate(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        address lastConsumedToken,
        bool isWithdrawAllIfEmpty,
        bool isUnwrapWeth
    ) external {
        // close
        IFacetClose.LiquidateResult memory result = IFacetClose(orderBook.mux3Facet).liquidate(
            IFacetClose.LiquidateArgs({
                positionId: positionId,
                lastConsumedToken: lastConsumedToken,
                isUnwrapWeth: isUnwrapWeth
            })
        );
        // is the position completely closed
        if (_isPositionAccountFullyClosed(orderBook, positionId)) {
            // auto withdraw, equivalent to POSITION_WITHDRAW_ALL_IF_EMPTY
            if (isWithdrawAllIfEmpty) {
                // default values of isUnwrapWeth, withdrawSwapToken, withdrawSwapSlippage
                // so that mux3 looks like mux1
                IFacetPositionAccount(orderBook.mux3Facet).withdrawAll(
                    IFacetPositionAccount.WithdrawAllArgs({
                        positionId: positionId,
                        isUnwrapWeth: true,
                        withdrawSwapToken: address(0),
                        withdrawSwapSlippage: 0
                    })
                );
            }
        }
        // cancel activated tp/sl orders
        for (uint256 i = 0; i < result.positions.length; i++) {
            bytes32 marketId = result.positions[i].marketId;
            if (_isPositionAccountMarketFullyClosed(orderBook, positionId, marketId)) {
                _cancelActivatedTpslOrders(orderBook, positionId, marketId);
            }
        }
    }

    function setInitialLeverage(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId,
        uint256 initialLeverage
    ) external {
        require(initialLeverage > 0, "initialLeverage must be greater than 0");
        IFacetPositionAccount(orderBook.mux3Facet).setInitialLeverage(positionId, marketId, initialLeverage);
    }

    /**
     * @dev Check if position account is closed
     */
    function _isPositionAccountFullyClosed(
        OrderBookStorage storage orderBook,
        bytes32 positionId
    ) internal view returns (bool) {
        PositionReader[] memory positions = IFacetReader(orderBook.mux3Facet).listAccountPositions(positionId);
        for (uint256 i = 0; i < positions.length; i++) {
            PositionPoolReader[] memory positionForPool = positions[i].pools;
            for (uint256 j = 0; j < positionForPool.length; j++) {
                if (positionForPool[j].size != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @dev Check if specific market position is closed in a position account
     */
    function _isPositionAccountMarketFullyClosed(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId
    ) internal view returns (bool) {
        PositionReader[] memory positions = IFacetReader(orderBook.mux3Facet).listAccountPositions(positionId);
        for (uint256 i = 0; i < positions.length; i++) {
            PositionPoolReader[] memory positionForPool = positions[i].pools;
            for (uint256 j = 0; j < positionForPool.length; j++) {
                if (positionForPool[j].size != 0) {
                    if (positions[i].marketId == marketId) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    function fillAdlOrder(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId,
        address lastConsumedToken,
        bool isWithdrawAllIfEmpty,
        bool isUnwrapWeth
    ) external returns (uint256 tradingPrice) {
        // pre-check
        require(IFacetReader(orderBook.mux3Facet).isDeleverageAllowed(positionId, marketId), "ADL safe");
        // close all
        uint256 size = 0;
        {
            PositionReader memory position = IFacetReader(orderBook.mux3Facet).getPositionAccount(positionId, marketId);
            for (uint256 i = 0; i < position.pools.length; i++) {
                size += position.pools[i].size;
            }
        }
        IFacetClose.ClosePositionResult memory result = IFacetClose(orderBook.mux3Facet).closePosition(
            IFacetClose.ClosePositionArgs({
                positionId: positionId,
                marketId: marketId,
                size: size,
                lastConsumedToken: lastConsumedToken,
                isUnwrapWeth: isUnwrapWeth
            })
        );
        tradingPrice = result.tradingPrice;
        {
            (address positionOwner, ) = LibCodec.decodePositionId(positionId);
            emit IOrderBook.FillAdlOrder(
                positionOwner,
                AdlOrderParams({
                    positionId: positionId,
                    marketId: marketId,
                    size: size,
                    price: tradingPrice,
                    isUnwrapWeth: isUnwrapWeth
                })
            );
        }
        // is the position completely closed
        if (_isPositionAccountFullyClosed(orderBook, positionId)) {
            // auto withdraw, equivalent to POSITION_WITHDRAW_ALL_IF_EMPTY
            if (isWithdrawAllIfEmpty) {
                // default values of isUnwrapWeth, withdrawSwapToken, withdrawSwapSlippage
                // so that mux3 looks like mux1
                IFacetPositionAccount(orderBook.mux3Facet).withdrawAll(
                    IFacetPositionAccount.WithdrawAllArgs({
                        positionId: positionId,
                        isUnwrapWeth: isUnwrapWeth,
                        withdrawSwapToken: address(0),
                        withdrawSwapSlippage: 0
                    })
                );
            }
        }

        // cancel activated tp/sl orders
        _cancelActivatedTpslOrders(orderBook, positionId, marketId);
        return tradingPrice;
    }

    function reallocate(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId,
        address fromPool,
        address toPool,
        uint256 size,
        address lastConsumedToken,
        bool isUnwrapWeth
    ) external returns (uint256 tradingPrice) {
        require(!_isPoolDraining(toPool), "toPool is draining");
        require(fromPool != toPool, "fromPool and toPool should be different");
        IFacetOpen.ReallocatePositionResult memory result = IFacetOpen(orderBook.mux3Facet).reallocatePosition(
            IFacetOpen.ReallocatePositionArgs({
                positionId: positionId,
                marketId: marketId,
                fromPool: fromPool,
                toPool: toPool,
                size: size,
                lastConsumedToken: lastConsumedToken,
                isUnwrapWeth: isUnwrapWeth
            })
        );
        tradingPrice = result.tradingPrice;
    }

    function _placeTpslOrders(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams,
        uint256 tradingPrice,
        uint64 blockTimestamp
    ) private {
        bool isLong = _isMarketLong(orderBook, orderParams.marketId);
        if (orderParams.tpPriceDiff > 0) {
            uint256 limitPrice = 0;
            if (isLong) {
                // close a long means sell, tp means limitPrice = tradingPrice * (1 + tpPriceDiff)
                limitPrice = (tradingPrice * (1e18 + orderParams.tpPriceDiff)) / 1e18;
            } else {
                // close a short means buy, tp means limitPrice = tradingPrice * (1 - tpPriceDiff)
                require(orderParams.tpPriceDiff < 1e18, "tpPriceDiff too large");
                limitPrice = (tradingPrice * (1e18 - orderParams.tpPriceDiff)) / 1e18;
            }
            uint64 orderId = _appendPositionOrder(
                orderBook,
                PositionOrderParams({
                    positionId: orderParams.positionId,
                    marketId: orderParams.marketId,
                    size: orderParams.size,
                    flags: orderParams.tpslFlags,
                    limitPrice: limitPrice,
                    expiration: orderParams.tpslExpiration,
                    lastConsumedToken: orderParams.lastConsumedToken,
                    collateralToken: address(0), // a close-order never contains collateral
                    collateralAmount: 0, // a close-order never contains collateral
                    withdrawUsd: 0,
                    withdrawSwapToken: orderParams.tpslWithdrawSwapToken,
                    withdrawSwapSlippage: orderParams.tpslWithdrawSwapSlippage,
                    tpPriceDiff: 0,
                    slPriceDiff: 0,
                    tpslExpiration: 0,
                    tpslFlags: 0,
                    tpslWithdrawSwapToken: address(0),
                    tpslWithdrawSwapSlippage: 0
                }),
                blockTimestamp,
                0 // gasFeeGwei
            );
            orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].add(uint256(orderId));
            require(
                orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].length() <= MAX_TP_SL_ORDERS,
                "Too Many TP/SL Orders"
            );
        }
        if (orderParams.slPriceDiff > 0) {
            uint256 limitPrice = 0;
            if (isLong) {
                // close a long means sell, sl means limitPrice = tradingPrice * (1 - slPriceDiff)
                require(orderParams.slPriceDiff < 1e18, "slPriceDiff too large");
                limitPrice = (tradingPrice * (1e18 - orderParams.slPriceDiff)) / 1e18;
            } else {
                // close a short means buy, sl means limitPrice = tradingPrice * (1 + slPriceDiff)
                limitPrice = (tradingPrice * (1e18 + orderParams.slPriceDiff)) / 1e18;
            }
            uint64 orderId = _appendPositionOrder(
                orderBook,
                PositionOrderParams({
                    positionId: orderParams.positionId,
                    marketId: orderParams.marketId,
                    size: orderParams.size,
                    flags: orderParams.tpslFlags | POSITION_TRIGGER_ORDER,
                    limitPrice: limitPrice,
                    expiration: orderParams.tpslExpiration,
                    lastConsumedToken: orderParams.lastConsumedToken,
                    collateralToken: address(0), // a close-order never contains collateral
                    collateralAmount: 0, // a close-order never contains collateral
                    withdrawUsd: 0,
                    withdrawSwapToken: orderParams.tpslWithdrawSwapToken,
                    withdrawSwapSlippage: orderParams.tpslWithdrawSwapSlippage,
                    tpPriceDiff: 0,
                    slPriceDiff: 0,
                    tpslExpiration: 0,
                    tpslFlags: 0,
                    tpslWithdrawSwapToken: address(0),
                    tpslWithdrawSwapSlippage: 0
                }),
                blockTimestamp,
                0 // gasFeeGwei
            );
            orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].add(uint256(orderId));
            require(
                orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].length() <= MAX_TP_SL_ORDERS,
                "Too Many TP/SL Orders"
            );
        }
    }

    function cancelOrder(
        OrderBookStorage storage orderBook,
        uint64 orderId,
        uint64 blockTimestamp,
        address msgSender
    ) external {
        require(orderBook.orders.contains(orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[orderId];
        _removeOrder(orderBook, orderData);
        // check cancel cool down
        uint256 coolDown = _cancelCoolDown(orderBook);
        require(blockTimestamp >= orderData.placeOrderTime + coolDown, "Cool down");
        if (orderData.orderType == OrderType.PositionOrder) {
            _cancelPositionOrder(orderBook, orderData, blockTimestamp, msgSender);
        } else if (orderData.orderType == OrderType.LiquidityOrder) {
            _cancelLiquidityOrder(orderBook, orderData, msgSender);
        } else if (orderData.orderType == OrderType.WithdrawalOrder) {
            _cancelWithdrawalOrder(orderBook, orderData, blockTimestamp, msgSender);
        } else if (orderData.orderType == OrderType.RebalanceOrder) {
            _cancelRebalanceOrder(orderData, msgSender);
        } else {
            revert("Unsupported order type");
        }
        _refundGasFee(orderBook, orderData);
        emit IOrderBook.CancelOrder(orderData.account, orderId, orderData);
    }

    function _cancelPositionOrder(
        OrderBookStorage storage orderBook,
        OrderData memory orderData,
        uint64 blockTimestamp,
        address msgSender
    ) private {
        PositionOrderParams memory orderParams = LibOrder.decodePositionOrder(orderData);
        if (_isBroker(msgSender)) {
            // broker can cancel expired order
            uint64 deadline = MathUpgradeable
                .min(orderData.placeOrderTime + _positionOrderTimeout(orderBook, orderParams), orderParams.expiration)
                .toUint64();
            require(blockTimestamp > deadline, "Not expired");
        } else if (_isDelegator(msgSender)) {
            // pass
        } else {
            // account owner can cancel order
            require(msgSender == orderData.account, "Not authorized");
        }
        // return deposited collateral for open-position
        if (
            LibOrder.isOpenPosition(orderParams) &&
            orderParams.collateralToken != address(0) &&
            orderParams.collateralAmount > 0
        ) {
            _transferOut(
                orderBook,
                orderParams.collateralToken,
                orderData.account,
                orderParams.collateralAmount,
                LibOrder.isUnwrapWeth(orderParams)
            );
        }
        // remove the current order from tp/sl list
        orderBook.tpslOrders[orderParams.positionId][orderParams.marketId].remove(uint256(orderData.id));
    }

    function _cancelLiquidityOrder(
        OrderBookStorage storage orderBook,
        OrderData memory orderData,
        address msgSender
    ) private {
        // account owner can cancel order
        require(msgSender == orderData.account, "Not authorized");
        // Delegator does not support liquidity order yet. if we want to support in the future,
        // _isDelegator(msgSender) should be checked here
        LiquidityOrderParams memory orderParams = LibOrder.decodeLiquidityOrder(orderData);
        if (orderParams.isAdding) {
            address collateralAddress = ICollateralPool(orderParams.poolAddress).collateralToken();
            _transferOut(
                orderBook,
                collateralAddress,
                orderData.account,
                orderParams.rawAmount,
                orderParams.isUnwrapWeth
            );
        } else {
            _transferOut(
                orderBook,
                orderParams.poolAddress,
                orderData.account,
                orderParams.rawAmount,
                false // unwrap eth
            );
        }
    }

    function _cancelWithdrawalOrder(
        OrderBookStorage storage orderBook,
        OrderData memory orderData,
        uint64 blockTimestamp,
        address msgSender
    ) private view {
        if (_isBroker(msgSender)) {
            uint64 deadline = orderData.placeOrderTime + _withdrawalOrderTimeout(orderBook);
            require(blockTimestamp > deadline, "Not expired");
        } else if (_isDelegator(msgSender)) {
            // pass
        } else {
            require(msgSender == orderData.account, "Not authorized");
        }
    }

    function _cancelRebalanceOrder(OrderData memory orderData, address msgSender) private pure {
        require(msgSender == orderData.account, "Not authorized");
    }

    function _appendPositionOrder(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams, // NOTE: id, placeOrderTime, expire10s will be ignored
        uint64 blockTimestamp,
        uint64 gasFeeGwei
    ) private returns (uint64 newOrderId) {
        (address positionAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        newOrderId = orderBook.nextOrderId++;
        _deductGasFee(orderBook, positionAccount, gasFeeGwei);
        OrderData memory orderData = LibOrder.encodePositionOrder(
            orderParams,
            newOrderId,
            positionAccount,
            blockTimestamp,
            gasFeeGwei
        );
        _appendOrder(orderBook, orderData);
        emit IOrderBook.NewPositionOrder(positionAccount, newOrderId, orderParams);
    }

    function depositGas(OrderBookStorage storage orderBook, uint256 amount, address account) external {
        _transferIn(orderBook, orderBook.weth, amount);
        orderBook.gasBalances[account] += amount;
    }

    function withdrawGas(OrderBookStorage storage orderBook, uint256 amount, address account) external {
        require(orderBook.gasBalances[account] >= amount, "Insufficient gas balance");
        orderBook.gasBalances[account] -= amount;
        _transferOut(orderBook, orderBook.weth, account, amount, true);
    }

    function _transferIn(OrderBookStorage storage orderBook, address tokenAddress, uint256 rawAmount) internal {
        uint256 oldBalance = orderBook.previousTokenBalance[tokenAddress];
        uint256 newBalance = IERC20Upgradeable(tokenAddress).balanceOf(address(this));
        require(newBalance >= oldBalance, "Token balance decreased");
        uint256 realRawAmount = newBalance - oldBalance;
        require(realRawAmount >= rawAmount, "Token balance not enough");
        orderBook.previousTokenBalance[tokenAddress] = newBalance;
    }

    function _transferOut(
        OrderBookStorage storage orderBook,
        address tokenAddress,
        address recipient,
        uint256 rawAmount,
        bool isUnwrapWeth
    ) internal {
        if (tokenAddress == address(orderBook.weth) && isUnwrapWeth) {
            LibEthUnwrapper.unwrap(orderBook.weth, payable(recipient), rawAmount);
        } else {
            IERC20Upgradeable(tokenAddress).safeTransfer(recipient, rawAmount);
        }
        orderBook.previousTokenBalance[tokenAddress] = IERC20Upgradeable(tokenAddress).balanceOf(address(this));
    }

    function _validateMarketId(OrderBookStorage storage orderBook, bytes32 marketId) internal view {
        BackedPoolState[] memory pools = IFacetReader(orderBook.mux3Facet).listMarketPools(marketId);
        require(pools.length > 0, "Invalid marketId");
    }

    function _validateCollateral(OrderBookStorage storage orderBook, address tokenAddress) internal view {
        (bool isExist, , ) = IFacetReader(orderBook.mux3Facet).getCollateralToken(tokenAddress);
        require(isExist, "Invalid collateralToken");
    }

    function _validatePool(OrderBookStorage storage orderBook, address poolAddress) internal view {
        bool isExist = IFacetReader(orderBook.mux3Facet).getCollateralPool(poolAddress);
        require(isExist, "Invalid pool");
    }

    function _isBroker(address msgSender) internal view returns (bool) {
        return IAccessControlUpgradeable(address(this)).hasRole(BROKER_ROLE, msgSender);
    }

    function _isDelegator(address msgSender) internal view returns (bool) {
        return IAccessControlUpgradeable(address(this)).hasRole(DELEGATOR_ROLE, msgSender);
    }

    function _positionOrderTimeout(
        OrderBookStorage storage orderBook,
        PositionOrderParams memory orderParams
    ) internal view returns (uint64 timeout) {
        timeout = LibOrder.isMarketOrder(orderParams)
            ? orderBook.configTable.getUint256(MCO_MARKET_ORDER_TIMEOUT).toUint64()
            : orderBook.configTable.getUint256(MCO_LIMIT_ORDER_TIMEOUT).toUint64();
        // 0 is valid
    }

    function _withdrawalOrderTimeout(OrderBookStorage storage orderBook) internal view returns (uint64 timeout) {
        timeout = orderBook.configTable.getUint256(MCO_MARKET_ORDER_TIMEOUT).toUint64();
        // 0 is valid
    }

    function _cancelCoolDown(OrderBookStorage storage orderBook) internal view returns (uint64 timeout) {
        timeout = orderBook.configTable.getUint256(MCO_CANCEL_COOL_DOWN).toUint64();
        // 0 is valid
    }

    function _lotSize(OrderBookStorage storage orderBook, bytes32 marketId) internal view returns (uint256 lotSize) {
        lotSize = IFacetReader(orderBook.mux3Facet).marketConfigValue(marketId, MM_LOT_SIZE).toUint256();
        require(lotSize > 0, "MM_LOT_SIZE not set");
    }

    function _liquidityLockPeriod(OrderBookStorage storage orderBook) internal view returns (uint64 timeout) {
        timeout = orderBook.configTable.getUint256(MCO_LIQUIDITY_LOCK_PERIOD).toUint64();
        // 0 is valid
    }

    function _minLiquidityOrderUsd(OrderBookStorage storage orderBook) internal view returns (uint256 minUsd) {
        minUsd = orderBook.configTable.getUint256(MCO_MIN_LIQUIDITY_ORDER_USD);
        require(minUsd > 0, "MCO_MIN_LIQUIDITY_ORDER_USD not set");
    }

    function _isPoolDraining(address poolAddress) internal view returns (bool isDraining) {
        isDraining = ICollateralPool(poolAddress).configValue(MCP_IS_DRAINING).toBoolean();
    }

    function _positionFeeRate(
        OrderBookStorage storage orderBook,
        bytes32 marketId
    ) internal view returns (uint256 positionFeeRate) {
        positionFeeRate = IFacetReader(orderBook.mux3Facet)
            .marketConfigValue(marketId, MM_POSITION_FEE_RATE)
            .toUint256();
    }

    function _isMarketLong(OrderBookStorage storage orderBook, bytes32 marketId) internal view returns (bool isLong) {
        (, isLong) = IFacetReader(orderBook.mux3Facet).marketState(marketId);
    }

    function _collateralToWad(
        OrderBookStorage storage orderBook,
        address collateralToken,
        uint256 rawAmount
    ) internal view returns (uint256 wadAmount) {
        (bool isExist, uint8 decimals, ) = IFacetReader(orderBook.mux3Facet).getCollateralToken(collateralToken);
        require(isExist, "Collateral token not enabled");
        if (decimals <= 18) {
            wadAmount = rawAmount * (10 ** (18 - decimals));
        } else {
            wadAmount = rawAmount / (10 ** (decimals - 18));
        }
    }

    function _collateralToRaw(
        OrderBookStorage storage orderBook,
        address collateralToken,
        uint256 wadAmount
    ) internal view returns (uint256 rawAmount) {
        (bool isExist, uint8 decimals, ) = IFacetReader(orderBook.mux3Facet).getCollateralToken(collateralToken);
        require(isExist, "Collateral token not enabled");
        if (decimals <= 18) {
            rawAmount = wadAmount / 10 ** (18 - decimals);
        } else {
            rawAmount = wadAmount * 10 ** (decimals - 18);
        }
    }

    function _priceOf(OrderBookStorage storage orderBook, address tokenAddress) internal view returns (uint256 price) {
        price = IFacetReader(orderBook.mux3Facet).priceOf(tokenAddress);
    }

    function _priceOf(OrderBookStorage storage orderBook, bytes32 oracleId) internal view returns (uint256 price) {
        price = IFacetReader(orderBook.mux3Facet).priceOf(oracleId);
    }

    function _marketOracleId(
        OrderBookStorage storage orderBook,
        bytes32 marketId
    ) internal view returns (bytes32 oracleId) {
        oracleId = IFacetReader(orderBook.mux3Facet).marketConfigValue(marketId, MM_ORACLE_ID);
        require(oracleId != bytes32(0), "EssentialConfigNotSet MM_ORACLE_ID");
    }

    function _feeDistributor(OrderBookStorage storage orderBook) internal view returns (address feeDistributor) {
        feeDistributor = IFacetReader(orderBook.mux3Facet).configValue(MC_FEE_DISTRIBUTOR).toAddress();
        require(feeDistributor != address(0), "EssentialConfigNotSet MC_FEE_DISTRIBUTOR");
    }

    /**
     * @dev When an order is executed, a fixed gas fee is charged from the Trader/LP regardless of order
     *      complexity (e.g. tp/sl orders, swaps, etc). this is to simplify the contract.
     */
    function _deductGasFee(OrderBookStorage storage orderBook, address trader, uint64 gasFeeGwei) internal {
        if (gasFeeGwei > 0) {
            uint256 gasFee = gasFeeGwei * 1 gwei;
            require(orderBook.gasBalances[trader] >= gasFee, "Insufficient gas fee");
            orderBook.gasBalances[trader] -= gasFee;
        }
    }

    function _payGasFee(OrderBookStorage storage orderBook, OrderData memory orderData, address broker) internal {
        uint256 gasFeeGwei = orderData.gasFeeGwei;
        if (gasFeeGwei > 0) {
            uint256 gasFee = gasFeeGwei * 1 gwei;
            _transferOut(orderBook, orderBook.weth, broker, gasFee, true);
        }
    }

    function _refundGasFee(OrderBookStorage storage orderBook, OrderData memory orderData) internal {
        uint256 gasFeeGwei = orderData.gasFeeGwei;
        if (gasFeeGwei > 0) {
            uint256 gasFee = gasFeeGwei * 1 gwei;
            _transferOut(orderBook, orderBook.weth, orderData.account, gasFee, true);
        }
    }

    function _orderGasFeeGwei(OrderBookStorage storage orderBook) internal view returns (uint64) {
        uint256 gasGwei = orderBook.configTable.getUint256(MCO_ORDER_GAS_FEE_GWEI);
        require(gasGwei <= type(uint64).max, "Gas fee overflow");
        return uint64(gasGwei);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";

import "../interfaces/ICollateralPool.sol";
import "../interfaces/IMux3Core.sol";
import "../interfaces/IMux3FeeDistributor.sol";
import "../interfaces/IMarket.sol";
import "../interfaces/IOrderBook.sol";
import "../interfaces/IWETH9.sol";
import "../interfaces/ICallback.sol";
import "../interfaces/ICallbackRegister.sol";
import "../libraries/LibCodec.sol";
import "../libraries/LibConfigMap.sol";
import "../libraries/LibEthUnwrapper.sol";
import "../libraries/LibOrder.sol";
import "../libraries/LibOrderBook.sol";

library LibOrderBook2 {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using LibTypeCast for bytes32;
    using LibTypeCast for uint256;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
    using LibConfigMap for mapping(bytes32 => bytes32);

    function placeLiquidityOrder(
        OrderBookStorage storage orderBook,
        LiquidityOrderParams memory orderParams,
        address account,
        uint64 blockTimestamp
    ) external {
        require(orderParams.rawAmount != 0, "Zero amount");
        LibOrderBook._validatePool(orderBook, orderParams.poolAddress);
        if (orderParams.isAdding) {
            require(!LibOrderBook._isPoolDraining(orderParams.poolAddress), "Draining pool");
            address collateralAddress = ICollateralPool(orderParams.poolAddress).collateralToken();
            LibOrderBook._transferIn(orderBook, collateralAddress, orderParams.rawAmount); // collateral
            address collateralToken = ICollateralPool(orderParams.poolAddress).collateralToken();
            require(orderParams.token == collateralToken, "Token mismatch");
        } else {
            LibOrderBook._transferIn(orderBook, orderParams.poolAddress, orderParams.rawAmount); // share
            LibOrderBook._validateCollateral(orderBook, orderParams.token);
        }
        uint64 orderId = orderBook.nextOrderId++;
        uint64 gasFeeGwei = LibOrderBook._orderGasFeeGwei(orderBook);
        LibOrderBook._deductGasFee(orderBook, account, gasFeeGwei);
        OrderData memory orderData = LibOrder.encodeLiquidityOrder(
            orderParams,
            orderId,
            account,
            blockTimestamp,
            gasFeeGwei
        );
        LibOrderBook._appendOrder(orderBook, orderData);
        emit IOrderBook.NewLiquidityOrder(account, orderId, orderParams);
    }

    function fillLiquidityOrder(
        OrderBookStorage storage orderBook,
        uint64 orderId,
        IFacetOpen.ReallocatePositionArgs[] memory reallocateArgs,
        uint64 blockTimestamp
    ) external returns (uint256 outAmount) {
        require(orderBook.orders.contains(orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[orderId];
        LibOrderBook._removeOrder(orderBook, orderData);
        require(orderData.orderType == OrderType.LiquidityOrder, "Order type mismatch");
        // fill
        LiquidityOrderParams memory orderParams = LibOrder.decodeLiquidityOrder(orderData);
        uint256 lockPeriod = LibOrderBook._liquidityLockPeriod(orderBook);
        require(blockTimestamp >= orderData.placeOrderTime + lockPeriod, "Liquidity order is under lock period");
        uint256 lpPrice;
        uint256 collateralPrice;
        if (orderParams.isAdding) {
            require(!LibOrderBook._isPoolDraining(orderParams.poolAddress), "Draining pool");
            (outAmount, lpPrice, collateralPrice) = _fillAddLiquidityOrder(
                orderBook,
                orderData,
                orderParams,
                reallocateArgs
            );
        } else {
            (outAmount, lpPrice, collateralPrice) = _fillRemoveLiquidityOrder(
                orderBook,
                orderData,
                orderParams,
                reallocateArgs
            );
        }
        emit IOrderBook.FillOrder(orderData.account, orderId, orderData);
        // gas
        LibOrderBook._payGasFee(orderBook, orderData, msg.sender);
        _runLiquidityCallback(orderBook, orderId, orderData, orderParams, outAmount, lpPrice, collateralPrice);
    }

    function _runLiquidityCallback(
        OrderBookStorage storage orderBook,
        uint64 orderId,
        OrderData memory orderData,
        LiquidityOrderParams memory orderParams,
        uint256 outAmount,
        uint256 lpPrice,
        uint256 collateralPrice
    ) internal {
        address callback = orderData.account;
        address register = _callbackRegister(orderBook);
        if (!ICallbackRegister(register).isCallbackRegistered(callback)) {
            return;
        }
        uint256 assetAmount = orderParams.isAdding ? orderParams.rawAmount : outAmount;
        uint256 lpAmount = orderParams.isAdding ? outAmount : orderParams.rawAmount;
        try
            ICallback(callback).afterLiquidityOrderFilled{ gas: _callbackGasLimit(orderBook) }(
                orderId,
                assetAmount,
                lpAmount,
                collateralPrice,
                lpPrice
            )
        {} catch (bytes memory reason) {
            emit IOrderBook.CallbackFailed(callback, orderId, reason);
        }
    }

    function _callbackRegister(OrderBookStorage storage orderBook) internal view returns (address register) {
        register = orderBook.configTable.getAddress(MCO_CALLBACK_REGISTER);
        require(register != address(0), "Callback register not set");
    }

    function _callbackGasLimit(OrderBookStorage storage orderBook) internal view returns (uint256) {
        uint256 callbackGasLimit = orderBook.configTable.getUint256(MCO_CALLBACK_GAS_LIMIT);
        return callbackGasLimit == 0 ? gasleft() : callbackGasLimit;
    }

    function _fillAddLiquidityOrder(
        OrderBookStorage storage orderBook,
        OrderData memory orderData,
        LiquidityOrderParams memory orderParams,
        IFacetOpen.ReallocatePositionArgs[] memory reallocateArgs
    ) internal returns (uint256 outAmount, uint256 lpPrice, uint256 collateralPrice) {
        // reallocate
        // when adding liquidity to a pool (called focusPool), its utilization will decrease.
        // the focusPool is eager to receive positions from other pools.
        // however, since this is not absolutely necessary, reallocate is temporarily not supported here for simplicity.
        require(reallocateArgs.length == 0, "addLiquidity + reallocate is not supported");
        // min order protection
        address collateralAddress = ICollateralPool(orderParams.poolAddress).collateralToken();
        {
            uint256 price = LibOrderBook._priceOf(orderBook, collateralAddress);
            uint256 value = (LibOrderBook._collateralToWad(orderBook, collateralAddress, orderParams.rawAmount) *
                price) / 1e18;
            uint256 minUsd = LibOrderBook._minLiquidityOrderUsd(orderBook);
            require(value >= minUsd, "Min liquidity order value");
        }
        // send collateral
        LibOrderBook._transferOut(
            orderBook,
            collateralAddress, // token
            orderParams.poolAddress, // receipt
            orderParams.rawAmount,
            false // isUnwrapWeth. CollateralPool never accepts ETH
        );
        // add liquidity
        ICollateralPool.AddLiquidityResult memory result = ICollateralPool(orderParams.poolAddress).addLiquidity(
            ICollateralPool.AddLiquidityArgs({
                account: orderData.account,
                rawCollateralAmount: orderParams.rawAmount,
                isUnwrapWeth: orderParams.isUnwrapWeth
            })
        );
        outAmount = result.shares;
        lpPrice = result.lpPrice;
        collateralPrice = result.collateralPrice;
    }

    function _fillRemoveLiquidityOrder(
        OrderBookStorage storage orderBook,
        OrderData memory orderData,
        LiquidityOrderParams memory orderParams,
        IFacetOpen.ReallocatePositionArgs[] memory reallocateArgs
    ) internal returns (uint256 outAmount, uint256 lpPrice, uint256 collateralPrice) {
        // reallocate
        // when removing liquidity from a pool (called focusPool), its utilization will increase.
        // the focusPool is eager to send positions to other pools.
        // 1. reallocate from focusPool to any pools, where `fromPool` = focusPool = orderParams.poolAddress
        // 2. the current lp will pay the position fees of the `toPool`
        // 3. removeLiquidity will deduct fees from returned collateral, the fees temporarily saved in `fromPool`
        // 4. `fromPool` then transfer fees to `toPool`
        uint256[] memory reallocateFeeCollaterals;
        uint256 totalReallocateFeeCollateral;
        if (reallocateArgs.length > 0) {
            (reallocateFeeCollaterals, totalReallocateFeeCollateral) = _getReallocatePositionFees(
                orderBook,
                reallocateArgs,
                orderParams.poolAddress, // focusPool
                false
            );
            for (uint256 i = 0; i < reallocateArgs.length; i++) {
                IFacetOpen(orderBook.mux3Facet).reallocatePosition(reallocateArgs[i]);
            }
        }
        // send share
        LibOrderBook._transferOut(
            orderBook,
            orderParams.poolAddress, // mlp
            orderParams.poolAddress, // receipt
            orderParams.rawAmount,
            false
        );
        // remove liquidity
        ICollateralPool.RemoveLiquidityResult memory result = ICollateralPool(orderParams.poolAddress).removeLiquidity(
            ICollateralPool.RemoveLiquidityArgs({
                account: orderData.account,
                shares: orderParams.rawAmount,
                token: orderParams.token,
                isUnwrapWeth: orderParams.isUnwrapWeth,
                extraFeeCollateral: totalReallocateFeeCollateral
            })
        );
        outAmount = result.rawCollateralAmount;
        lpPrice = result.lpPrice;
        collateralPrice = result.collateralPrice;
        // min order protection
        address collateralAddress = ICollateralPool(orderParams.poolAddress).collateralToken();
        {
            uint256 price = LibOrderBook._priceOf(orderBook, collateralAddress);
            uint256 value = (LibOrderBook._collateralToWad(orderBook, collateralAddress, outAmount) * price) / 1e18;
            uint256 minUsd = LibOrderBook._minLiquidityOrderUsd(orderBook);
            require(value >= minUsd, "Min liquidity order value");
        }
        // reallocate fees are temporarily held in OrderBook, now transfer them to other pools
        if (reallocateArgs.length > 0) {
            uint256 rawReallocationFee = LibOrderBook._collateralToRaw(
                orderBook,
                collateralAddress,
                totalReallocateFeeCollateral
            );
            address feeDistributor = LibOrderBook._feeDistributor(orderBook);
            if (rawReallocationFee > 0) {
                LibOrderBook._transferIn(orderBook, collateralAddress, rawReallocationFee);
                LibOrderBook._transferOut(orderBook, collateralAddress, feeDistributor, rawReallocationFee, false);
            }
            for (uint256 i = 0; i < reallocateArgs.length; i++) {
                address toPool = reallocateArgs[i].toPool;
                require(!LibOrderBook._isPoolDraining(toPool), "toPool is draining");
                require(toPool != reallocateArgs[i].fromPool, "fromPool and toPool should be different");
                uint256 rawFee = LibOrderBook._collateralToRaw(
                    orderBook,
                    collateralAddress,
                    reallocateFeeCollaterals[i]
                );
                if (rawFee > 0) {
                    IMux3FeeDistributor(LibOrderBook._feeDistributor(orderBook)).updateLiquidityFees(
                        orderData.account, // lp
                        toPool, // pool = the other pool
                        collateralAddress,
                        rawFee,
                        orderParams.isUnwrapWeth
                    );
                }
            }
        }
    }

    function _getReallocatePositionFees(
        OrderBookStorage storage orderBook,
        IFacetOpen.ReallocatePositionArgs[] memory reallocateArgs,
        address focusPool,
        bool isAdding
    )
        internal
        view
        returns (
            uint256[] memory feeCollaterals, // 1e18
            uint256 totalFeeCollateral // 1e18
        )
    {
        // the focusPool is always paying positionFee
        uint256 collateralPrice;
        {
            address collateralAddress = ICollateralPool(focusPool).collateralToken();
            collateralPrice = LibOrderBook._priceOf(orderBook, collateralAddress);
        }
        // the opposite pool
        feeCollaterals = new uint256[](reallocateArgs.length);
        for (uint256 i = 0; i < reallocateArgs.length; i++) {
            IFacetOpen.ReallocatePositionArgs memory arg = reallocateArgs[i];
            if (isAdding) {
                require(arg.toPool == focusPool, "toPool should be the focusPool");
            } else {
                require(arg.fromPool == focusPool, "fromPool should be the focusPool");
            }
            // feeCollateral = marketPrice * size * positionFeeRate / collateralPrice
            uint256 positionFeeCollateral = arg.size;
            {
                uint256 price = LibOrderBook._priceOf(orderBook, LibOrderBook._marketOracleId(orderBook, arg.marketId));
                positionFeeCollateral = (positionFeeCollateral * price) / 1e18;
            }
            {
                uint256 positionFeeRate = LibOrderBook._positionFeeRate(orderBook, arg.marketId);
                positionFeeCollateral = (positionFeeCollateral * positionFeeRate) / collateralPrice;
            }
            feeCollaterals[i] = positionFeeCollateral;
            totalFeeCollateral += positionFeeCollateral;
        }
    }

    function depositCollateral(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        address collateralToken,
        uint256 collateralAmount
    ) external {
        require(collateralAmount != 0, "Zero collateral");
        LibOrderBook._transferIn(orderBook, collateralToken, collateralAmount);
        LibOrderBook._transferOut(orderBook, collateralToken, address(orderBook.mux3Facet), collateralAmount, false);
        IFacetPositionAccount(orderBook.mux3Facet).deposit(positionId, collateralToken, collateralAmount);
    }

    function updateBorrowingFee(
        OrderBookStorage storage orderBook,
        bytes32 positionId,
        bytes32 marketId,
        address lastConsumedToken,
        bool isUnwrapWeth
    ) external {
        IFacetPositionAccount(orderBook.mux3Facet).updateBorrowingFee(
            positionId,
            marketId,
            lastConsumedToken,
            isUnwrapWeth
        );
    }

    function placeRebalanceOrder(
        OrderBookStorage storage orderBook,
        address rebalancer,
        RebalanceOrderParams memory orderParams,
        uint64 blockTimestamp
    ) external returns (uint64 newOrderId) {
        require(orderParams.rawAmount0 != 0, "Zero amount");
        newOrderId = orderBook.nextOrderId++;
        OrderData memory orderData = LibOrder.encodeRebalanceOrder(orderParams, newOrderId, blockTimestamp, rebalancer);
        LibOrderBook._appendOrder(orderBook, orderData);
        emit IOrderBook.NewRebalanceOrder(rebalancer, newOrderId, orderParams);
    }

    function fillRebalanceOrder(OrderBookStorage storage orderBook, uint64 orderId) external {
        require(orderBook.orders.contains(orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[orderId];
        LibOrderBook._removeOrder(orderBook, orderData);
        require(orderData.orderType == OrderType.RebalanceOrder, "Order type mismatch");
        RebalanceOrderParams memory orderParams = LibOrder.decodeRebalanceOrder(orderData);
        ICollateralPool(orderParams.poolAddress).rebalance(
            orderData.account,
            orderParams.token0,
            orderParams.rawAmount0,
            orderParams.maxRawAmount1,
            orderParams.userData
        );
        emit IOrderBook.FillOrder(orderData.account, orderId, orderData);
    }

    function withdrawAllCollateral(
        OrderBookStorage storage orderBook,
        WithdrawAllOrderParams memory orderParams
    ) external {
        require(
            LibOrderBook._isPositionAccountFullyClosed(orderBook, orderParams.positionId),
            "Position account is not fully closed"
        );
        IFacetPositionAccount(orderBook.mux3Facet).withdrawAll(
            IFacetPositionAccount.WithdrawAllArgs({
                positionId: orderParams.positionId,
                isUnwrapWeth: orderParams.isUnwrapWeth,
                // OrderBook.withdrawAll can not support swap. because OrderBook.withdrawAll is not called by broker,
                // so there is no reference prices.
                withdrawSwapToken: address(0),
                withdrawSwapSlippage: 0
            })
        );
    }

    function placeWithdrawalOrder(
        OrderBookStorage storage orderBook,
        WithdrawalOrderParams memory orderParams,
        uint64 blockTimestamp
    ) external {
        LibOrderBook._validateCollateral(orderBook, orderParams.tokenAddress);
        if (orderParams.lastConsumedToken != address(0)) {
            LibOrderBook._validateCollateral(orderBook, orderParams.lastConsumedToken);
        }
        if (orderParams.withdrawSwapToken != address(0)) {
            LibOrderBook._validateCollateral(orderBook, orderParams.withdrawSwapToken);
        }
        require(orderParams.rawAmount != 0, "Zero amount");
        require(orderParams.withdrawSwapSlippage <= 1e18, "withdrawSwapSlippage too large");
        (address withdrawAccount, ) = LibCodec.decodePositionId(orderParams.positionId);
        uint64 newOrderId = orderBook.nextOrderId++;
        uint64 gasFeeGwei = LibOrderBook._orderGasFeeGwei(orderBook);
        LibOrderBook._deductGasFee(orderBook, withdrawAccount, gasFeeGwei);
        OrderData memory orderData = LibOrder.encodeWithdrawalOrder(
            orderParams,
            newOrderId,
            blockTimestamp,
            withdrawAccount,
            gasFeeGwei
        );
        LibOrderBook._appendOrder(orderBook, orderData);
        emit IOrderBook.NewWithdrawalOrder(withdrawAccount, newOrderId, orderParams);
    }

    function fillWithdrawalOrder(OrderBookStorage storage orderBook, uint64 orderId, uint64 blockTimestamp) external {
        require(orderBook.orders.contains(orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[orderId];
        LibOrderBook._removeOrder(orderBook, orderData);
        require(orderData.orderType == OrderType.WithdrawalOrder, "Order type mismatch");
        WithdrawalOrderParams memory orderParams = LibOrder.decodeWithdrawalOrder(orderData);
        uint64 deadline = orderData.placeOrderTime + LibOrderBook._withdrawalOrderTimeout(orderBook);
        require(blockTimestamp <= deadline, "Order expired");
        // fill
        IFacetPositionAccount(orderBook.mux3Facet).withdraw(
            IFacetPositionAccount.WithdrawArgs({
                positionId: orderParams.positionId,
                collateralToken: orderParams.tokenAddress,
                amount: orderParams.rawAmount,
                lastConsumedToken: orderParams.lastConsumedToken,
                isUnwrapWeth: orderParams.isUnwrapWeth,
                withdrawSwapToken: orderParams.withdrawSwapToken,
                withdrawSwapSlippage: orderParams.withdrawSwapSlippage
            })
        );
        emit IOrderBook.FillOrder(orderData.account, orderId, orderData);
        // gas
        LibOrderBook._payGasFee(orderBook, orderData, msg.sender);
    }

    function modifyPositionOrder(
        OrderBookStorage storage orderBook,
        ModifyPositionOrderParams memory modifyParams,
        uint64 blockTimestamp
    ) external {
        require(orderBook.orders.contains(modifyParams.orderId), "No such orderId");
        OrderData memory orderData = orderBook.orderData[modifyParams.orderId];
        // verify order
        require(orderData.orderType == OrderType.PositionOrder, "Order type mismatch");
        PositionOrderParams memory orderParams = LibOrder.decodePositionOrder(orderData);
        require(modifyParams.positionId == orderParams.positionId, "PositionId mismatch");
        // check cool down and expiration
        uint256 coolDown = LibOrderBook._cancelCoolDown(orderBook);
        require(blockTimestamp >= orderData.placeOrderTime + coolDown, "Cool down");
        uint256 deadline = MathUpgradeable
            .min(
                orderData.placeOrderTime + LibOrderBook._positionOrderTimeout(orderBook, orderParams),
                orderParams.expiration
            )
            .toUint64();
        require(blockTimestamp <= deadline, "Order expired");
        // modify limitPrice
        if (modifyParams.limitPrice > 0) {
            orderParams.limitPrice = modifyParams.limitPrice;
        }
        // modify tp/sl
        if (LibOrder.isOpenPosition(orderParams)) {
            bool isLong = LibOrderBook._isMarketLong(orderBook, orderParams.marketId);
            // open position
            if (modifyParams.tpPriceDiff > 0) {
                require(orderParams.tpPriceDiff > 0, "Original order has no tp");
                if (!isLong) {
                    // close a short means buy, tp means limitPrice = tradingPrice * (1 - tpPriceDiff)
                    require(modifyParams.tpPriceDiff < 1e18, "tpPriceDiff too large");
                }
                orderParams.tpPriceDiff = modifyParams.tpPriceDiff;
            }
            if (modifyParams.slPriceDiff > 0) {
                require(orderParams.slPriceDiff > 0, "Original order has no sl");
                if (isLong) {
                    // close a long means sell, sl means limitPrice = tradingPrice * (1 - slPriceDiff)
                    require(modifyParams.slPriceDiff < 1e18, "slPriceDiff too large");
                }
                orderParams.slPriceDiff = modifyParams.slPriceDiff;
            }
        } else {
            // tp/sl strategy is not supported
            require(
                modifyParams.tpPriceDiff == 0 && modifyParams.slPriceDiff == 0,
                "Place multiple close-position orders instead"
            );
        }
        // done
        orderBook.orderData[orderData.id] = LibOrder.encodePositionOrder(
            orderParams,
            orderData.id,
            orderData.account,
            orderData.placeOrderTime,
            orderData.gasFeeGwei
        );
        emit IOrderBook.ModifyPositionOrder(orderData.account, modifyParams.orderId, modifyParams);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

library LibTypeCast {
    bytes32 private constant ADDRESS_GUARD_MASK = 0x0000000000000000000000000000000000000000ffffffffffffffffffffffff;

    function toAddress(bytes32 v) internal pure returns (address) {
        require(v & ADDRESS_GUARD_MASK == 0, "LibTypeCast::INVALID_ADDRESS");
        return address(bytes20(v));
    }

    function toBytes32(address v) internal pure returns (bytes32) {
        return bytes32(bytes20(v));
    }

    function toUint256(bytes32 v) internal pure returns (uint256) {
        return uint256(v);
    }

    function toUint256(int256 v) internal pure returns (uint256) {
        require(v >= 0, "LibTypeCast::UNDERFLOW");
        return uint256(v);
    }

    function toBytes32(int256 v) internal pure returns (bytes32) {
        return bytes32(uint256(v));
    }

    function toInt256(bytes32 v) internal pure returns (int256) {
        return int256(uint256(v));
    }

    function toBytes32(uint256 v) internal pure returns (bytes32) {
        return bytes32(v);
    }

    function toBoolean(bytes32 v) internal pure returns (bool) {
        uint256 n = toUint256(v);
        require(n == 0 || n == 1, "LibTypeCast::INVALID_BOOLEAN");
        return n == 1;
    }

    function toBytes32(bool v) internal pure returns (bytes32) {
        return toBytes32(v ? 1 : 0);
    }

    function toInt256(uint256 n) internal pure returns (int256) {
        require(n <= uint256(type(int256).max), "LibTypeCast::OVERFLOW");
        return int256(n);
    }

    function toUint96(uint256 n) internal pure returns (uint96) {
        require(n <= uint256(type(uint96).max), "LibTypeCast::OVERFLOW");
        return uint96(n);
    }

    function toUint64(uint256 n) internal pure returns (uint64) {
        require(n <= uint256(type(uint64).max), "LibTypeCast::OVERFLOW");
        return uint64(n);
    }

    function negInt256(int256 n) internal pure returns (uint256) {
        if (n >= 0) {
            return uint256(n);
        }
        require(n != type(int256).min, "LibTypeCast::UNDERFLOW");
        return uint256(-n);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

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

import "../interfaces/IOrderBook.sol";
import "../interfaces/IConstants.sol";
import "../libraries/LibConfigMap.sol";
import "./OrderBookStore.sol";

contract OrderBookGetter is OrderBookStore, IOrderBookGetter {
    using LibConfigMap for mapping(bytes32 => bytes32);
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;

    /**
     * @notice Get the next orderId before placing an order
     */
    function nextOrderId() external view override returns (uint64) {
        return _storage.nextOrderId;
    }

    /**
     * @notice A number to hint off-chain programs that the state has changed.
     *         note 1: may not increment by 1 each time
     *         note 2: will be 0 after 0xffffffff
     */
    function sequence() external view override returns (uint32) {
        return _storage.sequence;
    }

    function configValue(bytes32 key) external view override returns (bytes32) {
        return _storage.configTable.getBytes32(key);
    }

    function gasBalanceOf(address user) external view returns (uint256) {
        return _storage.gasBalances[user];
    }

    /**
     * @notice Get an Order by orderId
     */
    function getOrder(uint64 orderId) external view override returns (OrderData memory, bool) {
        return (_storage.orderData[orderId], _storage.orderData[orderId].version > 0);
    }

    /**
     * @notice Get Order List for all Traders
     */
    function getOrders(
        uint256 begin,
        uint256 end
    ) external view override returns (OrderData[] memory orderDataArray, uint256 totalCount) {
        totalCount = _storage.orders.length();
        if (begin >= end || begin >= totalCount) {
            return (orderDataArray, totalCount);
        }
        end = end <= totalCount ? end : totalCount;
        uint256 size = end - begin;
        orderDataArray = new OrderData[](size);
        for (uint256 i = 0; i < size; i++) {
            uint64 orderId = uint64(_storage.orders.at(i + begin));
            orderDataArray[i] = _storage.orderData[orderId];
        }
    }

    /**
     * @notice Get Order List for a User
     */
    function getOrdersOf(
        address user,
        uint256 begin,
        uint256 end
    ) external view override returns (OrderData[] memory orderDataArray, uint256 totalCount) {
        EnumerableSetUpgradeable.UintSet storage orders = _storage.userOrders[user];
        totalCount = orders.length();
        if (begin >= end || begin >= totalCount) {
            return (orderDataArray, totalCount);
        }
        end = end <= totalCount ? end : totalCount;
        uint256 size = end - begin;
        orderDataArray = new OrderData[](size);
        for (uint256 i = 0; i < size; i++) {
            uint64 orderId = uint64(orders.at(i + begin));
            orderDataArray[i] = _storage.orderData[orderId];
        }
    }

    /**
     * @notice Get tp/sl orders of a position + marketId
     */
    function getTpslOrders(bytes32 positionId, bytes32 marketId) external view returns (uint64[] memory orderIds) {
        EnumerableSetUpgradeable.UintSet storage ids = _storage.tpslOrders[positionId][marketId];
        orderIds = new uint64[](ids.length());
        for (uint256 i = 0; i < ids.length(); i++) {
            orderIds[i] = LibTypeCast.toUint64(ids.at(i));
        }
    }

    function _isBroker(address broker) internal view returns (bool) {
        return hasRole(BROKER_ROLE, broker);
    }

    function _isDelegator(address delegator) internal view returns (bool) {
        return hasRole(DELEGATOR_ROLE, delegator);
    }

    function _isOrderPaused(OrderType orderType) internal view returns (bool paused) {
        if (orderType == OrderType.PositionOrder) {
            paused = _storage.configTable.getBoolean(MCO_POSITION_ORDER_PAUSED);
        } else if (orderType == OrderType.LiquidityOrder) {
            paused = _storage.configTable.getBoolean(MCO_LIQUIDITY_ORDER_PAUSED);
        } else if (orderType == OrderType.WithdrawalOrder) {
            paused = _storage.configTable.getBoolean(MCO_WITHDRAWAL_ORDER_PAUSED);
        } else if (orderType == OrderType.RebalanceOrder) {
            paused = _storage.configTable.getBoolean(MCO_REBALANCE_ORDER_PAUSED);
        } else if (orderType == OrderType.AdlOrder) {
            paused = _storage.configTable.getBoolean(MCO_ADL_ORDER_PAUSED);
        } else if (orderType == OrderType.LiquidateOrder) {
            paused = _storage.configTable.getBoolean(MCO_LIQUIDATE_ORDER_PAUSED);
        }
    }

    function _referralManager() internal view returns (address ref) {
        ref = _storage.configTable.getAddress(MCO_REFERRAL_MANAGER);
        // 0 is valid
    }

    function _cancelCoolDown() internal view returns (uint256 timeout) {
        timeout = _storage.configTable.getUint256(MCO_CANCEL_COOL_DOWN);
        // 0 is valid
    }

    function _orderGasFeeGwei() internal view returns (uint256 gasFee) {
        gasFee = _storage.configTable.getUint256(MCO_ORDER_GAS_FEE_GWEI);
        // 0 is valid
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";

import "../interfaces/IOrderBook.sol";
import "../libraries/LibTypeCast.sol";

contract OrderBookStore is Initializable, AccessControlEnumerableUpgradeable {
    mapping(bytes32 => bytes32) internal _deprecated0;
    OrderBookStorage internal _storage; // should be the last variable before __gap
    bytes32[50] __gap;

    function __OrderBookStore_init() internal onlyInitializing {
        __AccessControlEnumerable_init();
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.28;

import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "../interfaces/IMux3Core.sol";
import "./OrderBookStore.sol";

struct PriceRawData {
    bytes32 id;
    address provider;
    bytes rawData;
}

contract PriceProvider is OrderBookStore {
    using AddressUpgradeable for address;

    function setPrices(PriceRawData[] memory priceData) external onlyRole(BROKER_ROLE) {
        require(priceData.length > 0, "PriceProvider: priceData is empty");
        for (uint256 i = 0; i < priceData.length; i++) {
            IFacetManagement(_storage.mux3Facet).setPrice(priceData[i].id, priceData[i].provider, priceData[i].rawData);
        }
    }
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 200,
    "details": {
      "yulDetails": {}
    }
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/libraries/LibOrderBook.sol": {
      "LibOrderBook": "0xeafab0d20d8403d2562a8661d3e274ad14a21cd4"
    },
    "contracts/libraries/LibOrderBook2.sol": {
      "LibOrderBook2": "0xaf0d89034d3f82a7101f5d8f485347ed5b1a5730"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"reason","type":"bytes"}],"name":"CallbackFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"account","type":"address"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint64","name":"placeOrderTime","type":"uint64"},{"internalType":"uint64","name":"gasFeeGwei","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"indexed":false,"internalType":"struct OrderData","name":"orderData","type":"tuple"}],"name":"CancelOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"indexed":false,"internalType":"struct AdlOrderParams","name":"params","type":"tuple"}],"name":"FillAdlOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"account","type":"address"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint64","name":"placeOrderTime","type":"uint64"},{"internalType":"uint64","name":"gasFeeGwei","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"indexed":false,"internalType":"struct OrderData","name":"orderData","type":"tuple"}],"name":"FillOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"uint64","name":"orderId","type":"uint64"},{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"uint256","name":"limitPrice","type":"uint256"},{"internalType":"uint256","name":"tpPriceDiff","type":"uint256"},{"internalType":"uint256","name":"slPriceDiff","type":"uint256"}],"indexed":false,"internalType":"struct ModifyPositionOrderParams","name":"params","type":"tuple"}],"name":"ModifyPositionOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"bool","name":"isAdding","type":"bool"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"indexed":false,"internalType":"struct LiquidityOrderParams","name":"params","type":"tuple"}],"name":"NewLiquidityOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"uint256","name":"limitPrice","type":"uint256"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawUsd","type":"uint256"},{"internalType":"address","name":"withdrawSwapToken","type":"address"},{"internalType":"uint256","name":"withdrawSwapSlippage","type":"uint256"},{"internalType":"uint256","name":"tpPriceDiff","type":"uint256"},{"internalType":"uint256","name":"slPriceDiff","type":"uint256"},{"internalType":"uint64","name":"tpslExpiration","type":"uint64"},{"internalType":"uint256","name":"tpslFlags","type":"uint256"},{"internalType":"address","name":"tpslWithdrawSwapToken","type":"address"},{"internalType":"uint256","name":"tpslWithdrawSwapSlippage","type":"uint256"}],"indexed":false,"internalType":"struct PositionOrderParams","name":"params","type":"tuple"}],"name":"NewPositionOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebalancer","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"uint256","name":"rawAmount0","type":"uint256"},{"internalType":"uint256","name":"maxRawAmount1","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"}],"indexed":false,"internalType":"struct RebalanceOrderParams","name":"params","type":"tuple"}],"name":"NewRebalanceOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"address","name":"withdrawSwapToken","type":"address"},{"internalType":"uint256","name":"withdrawSwapSlippage","type":"uint256"}],"indexed":false,"internalType":"struct WithdrawalOrderParams","name":"params","type":"tuple"}],"name":"NewWithdrawalOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"key","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"SetValue","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"sequence","type":"uint32"}],"name":"UpdateSequence","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"}],"name":"cancelOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"configValue","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"depositCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositGas","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"address","name":"collateralAddress","type":"address"},{"internalType":"uint256","name":"rawAmount","type":"uint256"}],"name":"donateLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"bool","name":"isWithdrawAllIfEmpty","type":"bool"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"name":"fillAdlOrder","outputs":[{"internalType":"uint256","name":"tradingPrice","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"},{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"address","name":"fromPool","type":"address"},{"internalType":"address","name":"toPool","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"internalType":"struct IFacetOpen.ReallocatePositionArgs[]","name":"reallocateArgs","type":"tuple[]"}],"name":"fillLiquidityOrder","outputs":[{"internalType":"uint256","name":"outAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"}],"name":"fillPositionOrder","outputs":[{"internalType":"uint256","name":"tradingPrice","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"}],"name":"fillRebalanceOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"}],"name":"fillWithdrawalOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"gasBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"orderId","type":"uint64"}],"name":"getOrder","outputs":[{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"account","type":"address"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint64","name":"placeOrderTime","type":"uint64"},{"internalType":"uint64","name":"gasFeeGwei","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct OrderData","name":"","type":"tuple"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getOrders","outputs":[{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"account","type":"address"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint64","name":"placeOrderTime","type":"uint64"},{"internalType":"uint64","name":"gasFeeGwei","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct OrderData[]","name":"orderDataArray","type":"tuple[]"},{"internalType":"uint256","name":"totalCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"begin","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getOrdersOf","outputs":[{"components":[{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"address","name":"account","type":"address"},{"internalType":"enum OrderType","name":"orderType","type":"uint8"},{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint64","name":"placeOrderTime","type":"uint64"},{"internalType":"uint64","name":"gasFeeGwei","type":"uint64"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct OrderData[]","name":"orderDataArray","type":"tuple[]"},{"internalType":"uint256","name":"totalCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"}],"name":"getTpslOrders","outputs":[{"internalType":"uint64[]","name":"orderIds","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"mux3Facet","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"bool","name":"isWithdrawAllIfEmpty","type":"bool"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"orderId","type":"uint64"},{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"uint256","name":"limitPrice","type":"uint256"},{"internalType":"uint256","name":"tpPriceDiff","type":"uint256"},{"internalType":"uint256","name":"slPriceDiff","type":"uint256"}],"internalType":"struct ModifyPositionOrderParams","name":"orderParams","type":"tuple"}],"name":"modifyPositionOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"proxyCalls","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"nextOrderId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"bool","name":"isAdding","type":"bool"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"internalType":"struct LiquidityOrderParams","name":"orderParams","type":"tuple"}],"name":"placeLiquidityOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"uint256","name":"limitPrice","type":"uint256"},{"internalType":"uint64","name":"expiration","type":"uint64"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawUsd","type":"uint256"},{"internalType":"address","name":"withdrawSwapToken","type":"address"},{"internalType":"uint256","name":"withdrawSwapSlippage","type":"uint256"},{"internalType":"uint256","name":"tpPriceDiff","type":"uint256"},{"internalType":"uint256","name":"slPriceDiff","type":"uint256"},{"internalType":"uint64","name":"tpslExpiration","type":"uint64"},{"internalType":"uint256","name":"tpslFlags","type":"uint256"},{"internalType":"address","name":"tpslWithdrawSwapToken","type":"address"},{"internalType":"uint256","name":"tpslWithdrawSwapSlippage","type":"uint256"}],"internalType":"struct PositionOrderParams","name":"orderParams","type":"tuple"},{"internalType":"bytes32","name":"referralCode","type":"bytes32"}],"name":"placePositionOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"uint256","name":"rawAmount0","type":"uint256"},{"internalType":"uint256","name":"maxRawAmount1","type":"uint256"},{"internalType":"bytes","name":"userData","type":"bytes"}],"internalType":"struct RebalanceOrderParams","name":"orderParams","type":"tuple"}],"name":"placeRebalanceOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"rawAmount","type":"uint256"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"address","name":"withdrawSwapToken","type":"address"},{"internalType":"uint256","name":"withdrawSwapSlippage","type":"uint256"}],"internalType":"struct WithdrawalOrderParams","name":"orderParams","type":"tuple"}],"name":"placeWithdrawalOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"address","name":"fromPool","type":"address"},{"internalType":"address","name":"toPool","type":"address"},{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"name":"reallocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sequence","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"uint256","name":"initialLeverage","type":"uint256"}],"name":"setInitialLeverage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"bytes","name":"rawData","type":"bytes"}],"internalType":"struct PriceRawData[]","name":"priceData","type":"tuple[]"}],"name":"setPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferTokenFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bytes32","name":"marketId","type":"bytes32"},{"internalType":"address","name":"lastConsumedToken","type":"address"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"name":"updateBorrowingFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"positionId","type":"bytes32"},{"internalType":"bool","name":"isUnwrapWeth","type":"bool"}],"internalType":"struct WithdrawAllOrderParams","name":"orderParams","type":"tuple"}],"name":"withdrawAllCollateral","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawGas","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"wrapNative","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60808060405234601557613e63908161001a8239f35b5f80fdfe6080806040526004361015610059575b50361561001a575f80fd5b60d2546001600160a01b0316330361002e57005b606460405162461bcd60e51b81526020600482015260046024820152630ae8aa8960e31b6044820152fd5b5f905f3560e01c90816301ffc9a714612af2575080631072cbea14612ab5578063248a9ca314612a825780632a58b33014612a595780632df13b7614612a305780632e515113146128e95780632f2ff15d1461280e57806332404484146127e457806336568abe146127515780633f27c55e14612630578063407b466d146124f557806342ac97a114612425578063485cc9551461216c5780634d0e15fa146120265780634dd9b8af14611f40578063523eb53714611e79578063529d15cc14611e5557806352cccdb314611d685780635c6111c814611c8e5780636225100714611a5c578063671b7a7c1461193d57806367e7f425146119045780636f2655f71461183c578063729119c31461177c57806372d588921461152b578063740b0cb61461114c57806376c1d66814610fb6578063813f3f4414610f075780638f72fc7714610ed35780639010d07c14610e8d5780639169d83314610de157806391d1485414610d9757806395e5ef6714610ca05780639a297bae14610bca578063a217fddf14610bae578063ac9650d8146109f3578063ad2211951461097f578063b5acfa33146106d0578063b673a97114610574578063ca15c8731461054a578063cdf9d07c146104d2578063d1fd27b314610364578063d547741f1461031b5763e9135e360361000f5760403660031901126102f0578061025a612b5f565b6102626132ca565b61026b3361325f565b15610302575b73eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe5760405163b9f6cd5160e01b815260ca600482015260248035908201526001600160a01b039091166044820152908290829060649082905af480156102f3576102db575b5060016101385580f35b816102e591612d22565b6102f057805f6102d1565b80fd5b6040513d84823e3d90fd5b5050fd5b6103166001600160a01b038216331461315b565b610271565b50346102f05760403660031901126102f05761036160043561033b612b75565b9061035c610355825f526065602052600160405f20015490565b3390613957565b61359f565b80f35b50346102f05761037336612d61565b5f516020613e0e5f395f51905f528352606560209081526040808520335f908152925290205460ff161561041457816040917f926b2cda941e79b7fc4ae2533b0a876ab25766c11d4903f47ffb33bedfc1e67393855260d5602052808386205582519182526020820152a15f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6104ce60206104ae60118661042833613a08565b906037856104425f516020613e0e5f395f51905f52613aee565b60405197889576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b828801528051918291018588015e8501907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e010190838201520301601f198101835282612d22565b60405162461bcd60e51b8152602060048201529182916024830190612bcb565b0390fd5b50346102f05760203660031901126102f0576105409061052b60ff60406001600160401b036104ff612d77565b610507612e60565b50169384815260cb60205281812094815260cb602052205460e81c16151591612f1a565b90604051928392604084526040840190612bef565b9060208301520390f35b50346102f05760203660031901126102f05760406020916004358152609783522054604051908152f35b5060a03660031901126102f0578060405161058e81612cec565b610596612d77565b81526020810190602435825260408101906044358252606081016064358152608082019160843583526105d06105ca6134f9565b15613126565b845160601c6105de3361325f565b156106b6575b5073af0d89034d3f82a7101f5d8f485347ed5b1a5730610603426136a2565b94813b156106b25787956001600160401b039560e49587936040519a8b998a98635cd3f5e360e11b8a5260ca60048b0152511660248901525160448801525160648701525160848601525160a48501521660c48301525af480156102f35761069d575b505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b816106a791612d22565b6102f057805f610666565b8780fd5b6106ca906001600160a01b0316331461315b565b5f6105e4565b50346102f05760403660031901126102f0576106ea612d77565b90602435916001600160401b03831161097b573660238401121561097b5782600401359061071782612d8d565b936107256040519586612d22565b8285526020850190602460e08395028201019036821161097757929592602401915b8183106108ec5750505061075a33613862565b6107656105ca6134a6565b61076d6132ca565b610776426136a2565b9160405194859263da2ede8360e01b84526001600160401b03608485019260ca6004870152166024850152608060448501525180915260a483019190855b81811061087d5750505081906001600160401b03602094166064830152038173af0d89034d3f82a7101f5d8f485347ed5b1a57305af4908115610871579061083a575b6020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b506020813d602011610869575b8161085460209383612d22565b8101031261086557602090516107f7565b5f80fd5b3d9150610847565b604051903d90823e3d90fd5b825180518552602081810151818701526040808301516001600160a01b03908116918801919091526060808401518216908801526080808401519088015260a0808401519091169087015260c09182015115159186019190915288955060e090940193909201916001016107b4565b60e0839794973603126109775760405161090581612d07565b833581526020840135602082015261091f60408501612bb7565b604082015261093060608501612bb7565b60608201526080840135608082015261094b60a08501612bb7565b60a082015260c084013580151581036106b25760c0820152815292959260e09290920191602001610747565b8580fd5b5080fd5b5061098936612e05565b906109926132ca565b61099b3361325f565b156109bd576109b49230916001600160a01b0316613322565b60016101385580f35b60405162461bcd60e51b815260206004820152600e60248201526d44656c656761746f72206f6e6c7960901b6044820152606490fd5b5060203660031901126102f0576004356001600160401b03811161097b573660238201121561097b5780600401356001600160401b038111610baa573660248260051b84010111610baa5790610a4882612d8d565b90610a566040519283612d22565b828252601f19610a6584612d8d565b01845b818110610b97575050368190036042190190845b84811015610b305760248160051b8301013583811215610b2c57820190866024830135926001600160401b03841161097b57604401833603811361097b57838291600195604051928392833781018381520390305af4610b0f610add61329b565b9182604091610aee83519384612d22565b600f83526e1b5d5b1d1a58d85b1b11985a5b1959608a1b6020840152613850565b50610b1a8287612ef2565b52610b258186612ef2565b5001610a7c565b8680fd5b83866040519182916020830160208452825180915260408401602060408360051b870101940192905b828210610b6857505050500390f35b91936001919395506020610b878192603f198a82030186528851612bcb565b9601920192018594939192610b59565b6060602082860181019190915201610a68565b8280fd5b50346102f057806003193601126102f057602090604051908152f35b50346102f05760203660031901126102f057610be4612d77565b90610bee33613862565b610bf66132ca565b610c016105ca6134f9565b6001600160401b03610c12426136a2565b60405163fa5ff05360e01b815260ca6004820152938216602485015216604483015260208260648173eafab0d20d8403d2562a8661d3e274ad14a21cd45af4908115610871579061083a576020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b50346102f05760803660031901126102f05780610cbb612b8b565b610cc3612d43565b90610ccd33613862565b610cd56132ca565b73af0d89034d3f82a7101f5d8f485347ed5b1a573091823b15610d925760a48492604051948593849263058a587b60e41b845260ca60048501526004356024850152602435604485015260018060a01b03166064840152151560848301525af480156102f357610d7d575b505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b81610d8791612d22565b6102f057805f610d40565b505050fd5b50346102f05760403660031901126102f0576040610db3612b75565b9160043581526065602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b5060203660031901126102f057600435610df96132ca565b80151580610e83575b15610e485760d2548291906001600160a01b0316803b156102fe578290600460405180948193630d0e30db60e41b83525af180156102f3576102db575060016101385580f35b60405162461bcd60e51b8152602060048201526013602482015272125b9d985b1a59081ddc985c08185b5bdd5b9d606a1b6044820152606490fd5b5034811115610e02565b50346102f05760403660031901126102f057610eba602091600435815260978352604060243591206139ab565b905460405160039290921b1c6001600160a01b03168152f35b50346102f05760403660031901126102f057610ef36024356004356131a7565b90610f0360405192839283612c87565b0390f35b5060403660031901126102f05780610f1d612b5f565b610f256132ca565b610f2e3361325f565b15610f9d575b73eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe5760405163fb70959360e01b815260ca600482015260248035908201526001600160a01b039091166044820152908290829060649082905af480156102f3576102db575060016101385580f35b610fb16001600160a01b038216331461315b565b610f34565b5060e03660031901126102f05780604051610fd081612d07565b6004358152610fdd612b75565b906020810191825260408101916044358352610ff7612d43565b6060830190815291608435906001600160a01b03821682036109775760808101918252611022612ba1565b9360a0820194855260c082019060c435825261103f6105ca613453565b825160601c61104d3361325f565b15611132575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091611073426136a2565b90833b1561112e5760405163e84159bd60e01b815260ca60048201529451602486015295516001600160a01b039081166044860152975160648501529051151560848401529251861660a4830152935190941660c4850152905160e48401526001600160401b0316610104830152829082906101249082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b8980fd5b611146906001600160a01b0316331461315b565b5f611053565b503660031901610260811261097b57610240136102f057604051906102408201918083106001600160401b038411176115175781926040526004358152602435602082015260443560408201526064356060820152608435608082015260a4356001600160401b03811681036102fe5760a082015260c4356001600160a01b03811681036102fe5760c082015260e4356001600160a01b03811681036102fe5760e08201526101043561010082015261012435610120820152610144356001600160a01b03811681036102fe5761014082015261016435610160820152610184356101808201526101a4356101a08201526101c4356001600160401b03811681036102fe576101c08201526101e4356101e0820152610204356001600160a01b03811681036102fe5761020082015261022435610220820152610244356112946105ca6134f9565b815160601c6112a23361325f565b156114fe575b7f35fed1456b0c505766e16a64bdecac785e2312157bef15ea3fc5d628067301e4845260d56020526112dd6040852054613d27565b821515806114ec575b611470575b5050506112f7426136a2565b73eafab0d20d8403d2562a8661d3e274ad14a21cd43b156102fe5760408051630bdd990960e01b815260ca6004820152835160248201526020840151604482015290830151606482015260608301516084820152608083015160a482015260a08301516001600160401b0390811660c483015260c08401516001600160a01b0390811660e484015260e0850151811661010484015261010085015161012484015261012085015161014484015261014085015181166101648401526101608501516101848401526101808501516101a48401526101a08501516101c48401526101c085015182166101e48401526101e08501516102048401526102008501511661022483015261022090930151610244820152911661026482015281816102848173eafab0d20d8403d2562a8661d3e274ad14a21cd45af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6001600160a01b031691823b156114e85760405163bfb7d70d60e01b81526001600160a01b03909216600483015260248201529083908290604490829084905af19081156114dd5783916114c5575b806112eb565b816114cf91612d22565b6114da57815f6114bf565b50fd5b6040513d85823e3d90fd5b8480fd5b506001600160a01b03811615156112e6565b6115126001600160a01b038216331461315b565b6112a8565b634e487b7160e01b82526041600452602482fd5b50346102f05760203660031901126102f0576004356001600160401b03811161097b5760a0600319823603011261097b5760405161156881612cec565b61157482600401612bb7565b815261158260248301612bb7565b90602081019182526040810192604481013584526060820190606481013582526084810135906001600160401b038211610b2c5760046115c59236920101612dbf565b608083019081527fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f468652606560209081526040808820335f908152925290205460ff161561173b57906001600160401b0361169b60209594936116296105ca61354c565b611632426136a2565b60405163481d404160e01b815260ca60048201523360248201526080604482015295516001600160a01b039081166084880152965190961660a4860152965160c4850152915160e4840152945160a0610104840152919485938493909290610124850190612bcb565b91166064830152038173af0d89034d3f82a7101f5d8f485347ed5b1a57305af480156102f3576116fc57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6020813d602011611733575b8161171560209383612d22565b8101031261097b57516001600160401b038116036102f0575f610666565b3d9150611708565b6104ce60206104ae60118961174f33613a08565b906037856104427fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f46613aee565b50346102f0578061178c36612e05565b919061179f61179a336136f9565b61315b565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4803b156114e857604051636c5b671f60e11b815260ca60048201526001600160a01b03938416602482015291909216604482015260648101929092528290829060849082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b50346102f05760203660031901126102f05780611857612d77565b61186033613862565b6118686132ca565b6118736105ca61354c565b73af0d89034d3f82a7101f5d8f485347ed5b1a573090813b156102fe576001600160401b03604484926040519485938492630b8b97cf60e01b845260ca60048501521660248301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f05760203660031901126102f0576020906040906001600160a01b0361192c612b5f565b16815260d483522054604051908152f35b50346102f05760803660031901126102f05780611958612b75565b604435908115158092036102fe5761196e612d43565b9161197833613862565b6119806132ca565b7ff0ff655187c1289babeb45eede8a90da4fc42fd6fe87835be936126432469c46845260d56020526119b86105ca6040862054613b90565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4803b156114e857849260a49160405195869485936307f2409960e31b855260ca6004860152600435602486015260018060a01b031660448501526064840152151560848301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f05760203660031901126102f0576004356001600160401b03811161097b573660238201121561097b578060040135611a9881612d8d565b91611aa66040519384612d22565b8183526024602084019260051b820101903682116114e85760248101925b828410611be9578585611ad633613862565b805115611b9a57815b8151811015611b965760ca5483906001600160a01b0316611b008385612ef2565b51516001600160a01b036020611b168688612ef2565b51015116916040611b278688612ef2565b51015190803b156114e857611b68938580946040519687958694859363480ce81160e11b855260048501526024840152606060448401526064830190612bcb565b03925af180156102f357611b81575b5050600101611adf565b81611b8b91612d22565b610baa578284611b77565b8280f35b60405162461bcd60e51b815260206004820152602160248201527f507269636550726f76696465723a2070726963654461746120697320656d70746044820152607960f81b6064820152608490fd5b83356001600160401b038111610b2c57820160606023198236030112610b2c5760405191606083018381106001600160401b03821117611c7a5760405260248201358352611c3960448301612bb7565b60208401526064820135926001600160401b038411611c7657611c66602094936024869536920101612dbf565b6040820152815201930192611ac4565b8880fd5b634e487b7160e01b89526041600452602489fd5b50346102f05760203660031901126102f05780611ca9612d77565b611cb233613862565b611cba6132ca565b611cc56105ca613453565b73af0d89034d3f82a7101f5d8f485347ed5b1a5730611ce3426136a2565b91813b15610d92576040516339561c4360e21b815260ca60048201526001600160401b039182166024820152921660448301528290829060649082905af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b5060603660031901126102f05780600435611d81612b75565b90611d8a6132ca565b8060601c611d973361325f565b15611e3b575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091823b15610d925760848492604051948593849263202bcf3160e01b845260ca6004850152602484015260018060a01b0316604483015260443560648301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b611e4f906001600160a01b0316331461315b565b5f611d9d565b50346102f057806003193601126102f057602063ffffffff60d05416604051908152f35b5060203660031901126102f05780611e8f612d77565b611e976132ca565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4611eb5426136a2565b91813b15610d925760405163e96da09960e01b815260ca60048201526001600160401b039182166024820152921660448301523360648301528290829060849082905af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f057611f4f36612d61565b90825260cf60205260408220908252602052604081208054611f7081612d8d565b90611f7e6040519283612d22565b808252611f8a81612d8d565b602083019390601f1901368537845b828110611fee5750505090604051928392602084019060208552518091526040840192915b818110611fcc575050500390f35b82516001600160401b0316845285945060209384019390920191600101611fbe565b8061200a611ffe600193856139ab565b90549060031b1c6136a2565b6001600160401b0361201c8388612ef2565b9116905201611f99565b50346102f05760e03660031901126102f057612040612b8b565b6064356001600160a01b03811690819003610baa5761205d612ba1565b60c435918215158093036114e85761207433613862565b61207c6132ca565b6120876105ca6134f9565b604051936346b0110160e11b855260ca60048601526004356024860152602435604486015260018060a01b03166064850152608484015260843560a484015260018060a01b031660c483015260e48201526020816101048173eafab0d20d8403d2562a8661d3e274ad14a21cd45af480156102f35761213d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b61215e9060203d602011612165575b6121568183612d22565b810190613198565b505f610d40565b503d61214c565b50346102f05760403660031901126102f057612186612b5f565b61218e612b75565b82549160ff8360081c161592838094612418575b8015612401575b156123a55760ff198116600117855583612394575b506121ea60ff855460081c166121d381613642565b6121dc81613642565b6121e581613642565b613642565b60016101385560ca80546001600160a01b039283166001600160a01b03199182161790915560d28054939092169216919091179055818052606560209081526040808420335f908152925290205460ff1615612345575b81805260976020526122563360408420613bf3565b505f516020613e0e5f395f51905f528252606560209081526040808420335f908152925290205460ff16156122e7575b5f516020613e0e5f395f51905f52825260976020526122a83360408420613bf3565b506122b05780f35b61ff001981541681557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a180f35b5f516020613e0e5f395f51905f52808352606560209081526040808520335f8181529190935220805460ff191660011790559081907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8580a4612286565b818052606560209081526040808420335f8181529190935220805460ff1916600117905580837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4612241565b61ffff19166101011784555f6121be565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b1580156121a95750600160ff8216146121a9565b50600160ff8216106121a2565b5060603660031901126102f057806004358060601c6124433361325f565b156124db575b5073eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe578290608460405180948193639f0abdf560e01b835260ca60048401526024830152602435604483015260443560648301525af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6124ef906001600160a01b0316331461315b565b5f612449565b5060a03660031901126102f0578060405161250f81612cec565b612517612b5f565b8152612521612b75565b90602081019182526040810191604435835261253b612d43565b916060810192835261254b612d52565b906080810191825261255e6105ca6134a6565b73af0d89034d3f82a7101f5d8f485347ed5b1a573061257c426136a2565b94813b156106b257604051632d1ef89d60e11b815260ca600482015292516001600160a01b039081166024850152945190941660448301529451606482015291511515608483015251151560a48201523360c48201526001600160401b0390911660e482015290829082906101049082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b50346102f05760a03660031901126102f05761264a612b8b565b90612653612d43565b61265b612d52565b9061266533613862565b61266d6132ca565b7fe1dcfe90f760d04e605b4db8b553331a44069fbfc060bcb28d982176d0b9f163835260d56020526126a56105ca6040852054613b90565b6040519363da35254960e01b855260ca60048601526004356024860152602435604486015260018060a01b0316606485015215156084840152151560a483015260208260c48173eafab0d20d8403d2562a8661d3e274ad14a21cd45af4908115610871579061083a576020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b50346102f05760403660031901126102f05761276b612b75565b336001600160a01b03821603612787576103619060043561359f565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b50346102f05760203660031901126102f0576040602091600435815260d583522054604051908152f35b50346102f05760403660031901126102f05761288c60043561282e612b75565b90612848610355825f526065602052600160405f20015490565b80845260656020526040842060018060a01b0383165f5260205260ff60405f20541615612890575b83526097602052604083206001600160a01b0390911690613bf3565b5080f35b8084526065602090815260408086206001600160a01b0385165f8181529190935220805460ff191660011790553390827f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8780a4612870565b50604036600319011261086557604051604081018181106001600160401b03821117612a1c57604052600435815260243590811515820361086557602081019182526129336132ca565b61293e6105ca613453565b805160601c61294c3361325f565b15612a02575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091823b156108655760645f92604051948593849263361ae7f360e01b845260ca600485015251602484015251151560448301525af480156129f7576129e457505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b6129f091505f90612d22565b5f5f610d40565b6040513d5f823e3d90fd5b612a16906001600160a01b0316331461315b565b5f612952565b634e487b7160e01b5f52604160045260245ffd5b3461086557606036600319011261086557610ef3612a4c612b5f565b604435906024359061305e565b34610865575f3660031901126108655760206001600160401b0360ca5460a01c16604051908152f35b34610865576020366003190112610865576020612aad6004355f526065602052600160405f20015490565b604051908152f35b604036600319011261086557612aea612acc612b5f565b612ad46132ca565b60243590309033906001600160a01b0316613322565b600161013855005b34610865576020366003190112610865576004359063ffffffff60e01b821680920361086557602091635a05180f60e01b8114908115612b34575b5015158152f35b637965db0b60e01b811491508115612b4e575b5083612b2d565b6301ffc9a760e01b14905083612b47565b600435906001600160a01b038216820361086557565b602435906001600160a01b038216820361086557565b604435906001600160a01b038216820361086557565b60a435906001600160a01b038216820361086557565b35906001600160a01b038216820361086557565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b80516001600160401b031682526020808201516001600160a01b031690830152604081015191906007831015612c735760c060e091612c7094604085015260ff60608201511660608501526001600160401b0360808201511660808501526001600160401b0360a08201511660a08501520151918160c08201520190612bcb565b90565b634e487b7160e01b5f52602160045260245ffd5b92919060408401906040855280518092526060850191602060608260051b8801019201925f905b828210612cc15750505060209150930152565b909192602080612cdd600193605f198c82030186528851612bef565b96019201920190939291612cae565b60a081019081106001600160401b03821117612a1c57604052565b60e081019081106001600160401b03821117612a1c57604052565b90601f801991011681019081106001600160401b03821117612a1c57604052565b60643590811515820361086557565b60843590811515820361086557565b6040906003190112610865576004359060243590565b600435906001600160401b038216820361086557565b6001600160401b038111612a1c5760051b60200190565b6001600160401b038111612a1c57601f01601f191660200190565b81601f8201121561086557803590612dd682612da4565b92612de46040519485612d22565b8284526020838301011161086557815f926020809301838601378301015290565b6060906003190112610865576004356001600160a01b038116810361086557906024356001600160a01b0381168103610865579060443590565b91908203918211612e4c57565b634e487b7160e01b5f52601160045260245ffd5b60405190612e6d82612d07565b606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b90612ea082612d8d565b612ead6040519182612d22565b8281528092612ebe601f1991612d8d565b01905f5b828110612ece57505050565b602090612ed9612e60565b82828501015201612ec2565b91908201809211612e4c57565b8051821015612f065760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b90604051612f2781612d07565b80928054906001600160401b038216835260018060a01b038260401c16602084015260ff8260e01c166007811015612c735760029260ff91604086015260e81c1660608401526001600160401b036001820154818116608086015260401c1660a08401520190604051915f8154918260011c9060018416938415613054575b6020831085146130405782875286949081156130195750600114612fda575b505060c09291612fd6910384612d22565b0152565b5f908152602081209092505b818310612ffd575050810160200181612fd6612fc5565b6020919350806001915483858901015201910190918492612fe6565b60ff191660208681019190915292151560051b85019092019250839150612fd69050612fc5565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612fa6565b6001600160a01b03165f90815260ce6020526040902080549193919280851080159061311c575b61311457848482116131095761309a91612e3f565b906130a482612e96565b945f5b8381106130b45750505050565b806001600160401b036130d26130cc85600195612ee5565b866139ab565b90549060031b1c165f5260cb6020526130ed60405f20612f1a565b6130f7828a612ef2565b526131028189612ef2565b50016130a7565b61309a915084612e3f565b506060935050565b5083851015613085565b1561312d57565b60405162461bcd60e51b815260206004820152600660248201526514185d5cd95960d21b6044820152606490fd5b1561316257565b60405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606490fd5b90816020910312610865575190565b9160cc5491808410801590613255575b61324e5783838211613243576131cc91612e3f565b926131d684612e96565b935f9160cc54925b8281106131eb5750505050565b6131f58282612ee5565b9084821015612f06576001600160401b0360019260cc5f5260205f200154165f5260cb60205261322760405f20612f1a565b613231828a612ef2565b5261323c8189612ef2565b50016131de565b6131cc915083612e3f565b5060609250565b50828410156131b7565b6001600160a01b03165f9081527fb9eda38d3b4963a3851578208ac76852f2fd7c29cc63b0c858ddf95e8617347c602052604090205460ff1690565b3d156132c5573d906132ac82612da4565b916132ba6040519384612d22565b82523d5f602084013e565b606090565b600261013854146132dd57600261013855565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6040516323b872dd60e01b602082019081526001600160a01b0393841660248301529290931660448401526064808401949094529282526133c2925f90819061336c608486612d22565b60018060a01b03169260405194613384604087612d22565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af16133bc61329b565b91613d85565b8051908115918215613430575b5050156133d857565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b819250906020918101031261086557602001518015158103610865575f806133cf565b7f9328e29a14cee8cc1de73d9dcdc6cf7fc66a69681fa4e17e61876a05fd5aaa8a5f5260d56020527f29f5dff74cdba420026d1f355f3094af4bf6d4513353cad055891d738a8e7da954612c7090613b90565b7f9aaa64ff860ee47a0464f727c54600373c133a2d9065df7a14037d1acc78e1765f5260d56020527fa03e3c6860802415a8538423b5b4e963dd97037884be89dee42c5cc27d7c3eb054612c7090613b90565b7f712ed76dcd0d23c46937bf241db09d7cf50075eccf294171d32810d419d650295f5260d56020527f43f3648b0dbdd8d93acb984fdf2bc0655642f76a28a6126f7369170b36b501dd54612c7090613b90565b7fb07ac9ce47eaa05f5a58db2d158d61a72dc71a98b089ed0c8da65f6aec260e0c5f5260d56020527f3cf8718fac1840ae546c9e4108607892524dbe6b1e895cc3ccc71c4f4426eec554612c7090613b90565b906135e991805f52606560205260405f2060018060a01b0383165f5260205260ff60405f2054166135ec575b5f9081526097602052604090206001600160a01b0390911690613c62565b50565b5f8181526065602090815260408083206001600160a01b03861680855292528220805460ff19169055339183907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a46135cb565b1561364957565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b6001600160401b0381116136bc576001600160401b031690565b60405162461bcd60e51b81526020600482015260156024820152744c696254797065436173743a3a4f564552464c4f5760581b6044820152606490fd5b60ca54604051630c90112160e21b81527f11371db1863897581fa5ab4629f9161670f4dcd0da392b411b5ee2bb70eb08af600482015290602090829060249082906001600160a01b03165afa9081156129f7575f9161381e575b506001600160a01b039061376690613d27565b1680156137c8576001600160a01b038216146137c2576001600160a01b03165f9081527fbf11d18b89b41ad20fc1b135dfd494c6d1b14d33459b1575c12bece53af3bcbc602052604090205460ff166137bd575f90565b600190565b50600190565b60405162461bcd60e51b815260206004820152602860248201527f457373656e7469616c436f6e6669674e6f74536574204d435f4645455f4449536044820152672a2924a12aaa27a960c11b6064820152608490fd5b90506020813d602011613848575b8161383960209383612d22565b8101031261086557515f613753565b3d915061382c565b9091901561385c575090565b906139c0565b6001600160a01b0381165f9081527fb2f7eabb536c6e4777097e97a666b03e14e8be5a30142403e71df0906f515d06602052604090205460ff16156138a45750565b60206104ae60116138bf6104ce9460018060a01b0316613a08565b6037846138eb7ffdae3ca625b7a6a67b1c85ddb9efb8e92252215d4a6ec7177e622584b5b236e8613aee565b60405196879476020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b828701528051918291018587015e8401907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e01015f838201520301601f198101835282612d22565b90815f52606560205260405f2060018060a01b0382165f5260205260ff60405f20541615613983575050565b6104ae60116104ce93603760206138eb6139a5829760018060a01b0316613a08565b93613aee565b8054821015612f06575f5260205f2001905f90565b8051909190156139d35750805190602001fd5b60405162461bcd60e51b8152602060048201529081906104ce906024830190612bcb565b908151811015612f06570160200190565b613a12602a612da4565b90613a206040519283612d22565b602a8252613a2e602a612da4565b6020830190601f1901368237825115612f065760309053815160011015612f06576078602183015360295b60018111613aad5750613a695790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b90600f81166010811015612f06576f181899199a1a9b1b9c1cb0b131b232b360811b901a613adb83856139f7565b5360041c908015612e4c575f1901613a59565b613af86042612da4565b90613b066040519283612d22565b60428252613b146042612da4565b6020830190601f1901368237825115612f065760309053815160011015612f06576078602183015360415b60018111613b4f5750613a695790565b90600f81166010811015612f06576f181899199a1a9b1b9c1cb0b131b232b360811b901a613b7d83856139f7565b5360041c908015612e4c575f1901613b3f565b80158015613be9575b15613ba45760011490565b60405162461bcd60e51b815260206004820152601c60248201527f4c696254797065436173743a3a494e56414c49445f424f4f4c45414e000000006044820152606490fd5b5060018114613b99565b6001810190825f528160205260405f2054155f14613c5b57805468010000000000000000811015612a1c57613c48613c328260018794018555846139ab565b819391549060031b91821b915f19901b19161790565b905554915f5260205260405f2055600190565b5050505f90565b906001820191815f528260205260405f20548015155f14613d1f575f198101818111612e4c5782545f19810191908211612e4c57808203613cea575b50505080548015613cd6575f190190613cb782826139ab565b8154905f199060031b1b19169055555f526020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b613d0a613cfa613c3293866139ab565b90549060031b1c928392866139ab565b90555f528360205260405f20555f8080613c9e565b505050505f90565b6bffffffffffffffffffffffff8116613d405760601c90565b60405162461bcd60e51b815260206004820152601c60248201527f4c696254797065436173743a3a494e56414c49445f41444452455353000000006044820152606490fd5b91929015613de75750815115613d99575090565b3b15613da25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b826139c056fe974288fc79dc8b8b1fe9f1f0b8f5738873e53e07d3122c5e3c02b834c018d6cb339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95a2646970667358221220a338685c3305b8b74e973509cbf51effd8159e8f3a99739e9af59708695ace9a64736f6c634300081c0033

Deployed Bytecode

0x6080806040526004361015610059575b50361561001a575f80fd5b60d2546001600160a01b0316330361002e57005b606460405162461bcd60e51b81526020600482015260046024820152630ae8aa8960e31b6044820152fd5b5f905f3560e01c90816301ffc9a714612af2575080631072cbea14612ab5578063248a9ca314612a825780632a58b33014612a595780632df13b7614612a305780632e515113146128e95780632f2ff15d1461280e57806332404484146127e457806336568abe146127515780633f27c55e14612630578063407b466d146124f557806342ac97a114612425578063485cc9551461216c5780634d0e15fa146120265780634dd9b8af14611f40578063523eb53714611e79578063529d15cc14611e5557806352cccdb314611d685780635c6111c814611c8e5780636225100714611a5c578063671b7a7c1461193d57806367e7f425146119045780636f2655f71461183c578063729119c31461177c57806372d588921461152b578063740b0cb61461114c57806376c1d66814610fb6578063813f3f4414610f075780638f72fc7714610ed35780639010d07c14610e8d5780639169d83314610de157806391d1485414610d9757806395e5ef6714610ca05780639a297bae14610bca578063a217fddf14610bae578063ac9650d8146109f3578063ad2211951461097f578063b5acfa33146106d0578063b673a97114610574578063ca15c8731461054a578063cdf9d07c146104d2578063d1fd27b314610364578063d547741f1461031b5763e9135e360361000f5760403660031901126102f0578061025a612b5f565b6102626132ca565b61026b3361325f565b15610302575b73eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe5760405163b9f6cd5160e01b815260ca600482015260248035908201526001600160a01b039091166044820152908290829060649082905af480156102f3576102db575b5060016101385580f35b816102e591612d22565b6102f057805f6102d1565b80fd5b6040513d84823e3d90fd5b5050fd5b6103166001600160a01b038216331461315b565b610271565b50346102f05760403660031901126102f05761036160043561033b612b75565b9061035c610355825f526065602052600160405f20015490565b3390613957565b61359f565b80f35b50346102f05761037336612d61565b5f516020613e0e5f395f51905f528352606560209081526040808520335f908152925290205460ff161561041457816040917f926b2cda941e79b7fc4ae2533b0a876ab25766c11d4903f47ffb33bedfc1e67393855260d5602052808386205582519182526020820152a15f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6104ce60206104ae60118661042833613a08565b906037856104425f516020613e0e5f395f51905f52613aee565b60405197889576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b828801528051918291018588015e8501907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e010190838201520301601f198101835282612d22565b60405162461bcd60e51b8152602060048201529182916024830190612bcb565b0390fd5b50346102f05760203660031901126102f0576105409061052b60ff60406001600160401b036104ff612d77565b610507612e60565b50169384815260cb60205281812094815260cb602052205460e81c16151591612f1a565b90604051928392604084526040840190612bef565b9060208301520390f35b50346102f05760203660031901126102f05760406020916004358152609783522054604051908152f35b5060a03660031901126102f0578060405161058e81612cec565b610596612d77565b81526020810190602435825260408101906044358252606081016064358152608082019160843583526105d06105ca6134f9565b15613126565b845160601c6105de3361325f565b156106b6575b5073af0d89034d3f82a7101f5d8f485347ed5b1a5730610603426136a2565b94813b156106b25787956001600160401b039560e49587936040519a8b998a98635cd3f5e360e11b8a5260ca60048b0152511660248901525160448801525160648701525160848601525160a48501521660c48301525af480156102f35761069d575b505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b816106a791612d22565b6102f057805f610666565b8780fd5b6106ca906001600160a01b0316331461315b565b5f6105e4565b50346102f05760403660031901126102f0576106ea612d77565b90602435916001600160401b03831161097b573660238401121561097b5782600401359061071782612d8d565b936107256040519586612d22565b8285526020850190602460e08395028201019036821161097757929592602401915b8183106108ec5750505061075a33613862565b6107656105ca6134a6565b61076d6132ca565b610776426136a2565b9160405194859263da2ede8360e01b84526001600160401b03608485019260ca6004870152166024850152608060448501525180915260a483019190855b81811061087d5750505081906001600160401b03602094166064830152038173af0d89034d3f82a7101f5d8f485347ed5b1a57305af4908115610871579061083a575b6020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b506020813d602011610869575b8161085460209383612d22565b8101031261086557602090516107f7565b5f80fd5b3d9150610847565b604051903d90823e3d90fd5b825180518552602081810151818701526040808301516001600160a01b03908116918801919091526060808401518216908801526080808401519088015260a0808401519091169087015260c09182015115159186019190915288955060e090940193909201916001016107b4565b60e0839794973603126109775760405161090581612d07565b833581526020840135602082015261091f60408501612bb7565b604082015261093060608501612bb7565b60608201526080840135608082015261094b60a08501612bb7565b60a082015260c084013580151581036106b25760c0820152815292959260e09290920191602001610747565b8580fd5b5080fd5b5061098936612e05565b906109926132ca565b61099b3361325f565b156109bd576109b49230916001600160a01b0316613322565b60016101385580f35b60405162461bcd60e51b815260206004820152600e60248201526d44656c656761746f72206f6e6c7960901b6044820152606490fd5b5060203660031901126102f0576004356001600160401b03811161097b573660238201121561097b5780600401356001600160401b038111610baa573660248260051b84010111610baa5790610a4882612d8d565b90610a566040519283612d22565b828252601f19610a6584612d8d565b01845b818110610b97575050368190036042190190845b84811015610b305760248160051b8301013583811215610b2c57820190866024830135926001600160401b03841161097b57604401833603811361097b57838291600195604051928392833781018381520390305af4610b0f610add61329b565b9182604091610aee83519384612d22565b600f83526e1b5d5b1d1a58d85b1b11985a5b1959608a1b6020840152613850565b50610b1a8287612ef2565b52610b258186612ef2565b5001610a7c565b8680fd5b83866040519182916020830160208452825180915260408401602060408360051b870101940192905b828210610b6857505050500390f35b91936001919395506020610b878192603f198a82030186528851612bcb565b9601920192018594939192610b59565b6060602082860181019190915201610a68565b8280fd5b50346102f057806003193601126102f057602090604051908152f35b50346102f05760203660031901126102f057610be4612d77565b90610bee33613862565b610bf66132ca565b610c016105ca6134f9565b6001600160401b03610c12426136a2565b60405163fa5ff05360e01b815260ca6004820152938216602485015216604483015260208260648173eafab0d20d8403d2562a8661d3e274ad14a21cd45af4908115610871579061083a576020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b50346102f05760803660031901126102f05780610cbb612b8b565b610cc3612d43565b90610ccd33613862565b610cd56132ca565b73af0d89034d3f82a7101f5d8f485347ed5b1a573091823b15610d925760a48492604051948593849263058a587b60e41b845260ca60048501526004356024850152602435604485015260018060a01b03166064840152151560848301525af480156102f357610d7d575b505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b81610d8791612d22565b6102f057805f610d40565b505050fd5b50346102f05760403660031901126102f0576040610db3612b75565b9160043581526065602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b5060203660031901126102f057600435610df96132ca565b80151580610e83575b15610e485760d2548291906001600160a01b0316803b156102fe578290600460405180948193630d0e30db60e41b83525af180156102f3576102db575060016101385580f35b60405162461bcd60e51b8152602060048201526013602482015272125b9d985b1a59081ddc985c08185b5bdd5b9d606a1b6044820152606490fd5b5034811115610e02565b50346102f05760403660031901126102f057610eba602091600435815260978352604060243591206139ab565b905460405160039290921b1c6001600160a01b03168152f35b50346102f05760403660031901126102f057610ef36024356004356131a7565b90610f0360405192839283612c87565b0390f35b5060403660031901126102f05780610f1d612b5f565b610f256132ca565b610f2e3361325f565b15610f9d575b73eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe5760405163fb70959360e01b815260ca600482015260248035908201526001600160a01b039091166044820152908290829060649082905af480156102f3576102db575060016101385580f35b610fb16001600160a01b038216331461315b565b610f34565b5060e03660031901126102f05780604051610fd081612d07565b6004358152610fdd612b75565b906020810191825260408101916044358352610ff7612d43565b6060830190815291608435906001600160a01b03821682036109775760808101918252611022612ba1565b9360a0820194855260c082019060c435825261103f6105ca613453565b825160601c61104d3361325f565b15611132575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091611073426136a2565b90833b1561112e5760405163e84159bd60e01b815260ca60048201529451602486015295516001600160a01b039081166044860152975160648501529051151560848401529251861660a4830152935190941660c4850152905160e48401526001600160401b0316610104830152829082906101249082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b8980fd5b611146906001600160a01b0316331461315b565b5f611053565b503660031901610260811261097b57610240136102f057604051906102408201918083106001600160401b038411176115175781926040526004358152602435602082015260443560408201526064356060820152608435608082015260a4356001600160401b03811681036102fe5760a082015260c4356001600160a01b03811681036102fe5760c082015260e4356001600160a01b03811681036102fe5760e08201526101043561010082015261012435610120820152610144356001600160a01b03811681036102fe5761014082015261016435610160820152610184356101808201526101a4356101a08201526101c4356001600160401b03811681036102fe576101c08201526101e4356101e0820152610204356001600160a01b03811681036102fe5761020082015261022435610220820152610244356112946105ca6134f9565b815160601c6112a23361325f565b156114fe575b7f35fed1456b0c505766e16a64bdecac785e2312157bef15ea3fc5d628067301e4845260d56020526112dd6040852054613d27565b821515806114ec575b611470575b5050506112f7426136a2565b73eafab0d20d8403d2562a8661d3e274ad14a21cd43b156102fe5760408051630bdd990960e01b815260ca6004820152835160248201526020840151604482015290830151606482015260608301516084820152608083015160a482015260a08301516001600160401b0390811660c483015260c08401516001600160a01b0390811660e484015260e0850151811661010484015261010085015161012484015261012085015161014484015261014085015181166101648401526101608501516101848401526101808501516101a48401526101a08501516101c48401526101c085015182166101e48401526101e08501516102048401526102008501511661022483015261022090930151610244820152911661026482015281816102848173eafab0d20d8403d2562a8661d3e274ad14a21cd45af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6001600160a01b031691823b156114e85760405163bfb7d70d60e01b81526001600160a01b03909216600483015260248201529083908290604490829084905af19081156114dd5783916114c5575b806112eb565b816114cf91612d22565b6114da57815f6114bf565b50fd5b6040513d85823e3d90fd5b8480fd5b506001600160a01b03811615156112e6565b6115126001600160a01b038216331461315b565b6112a8565b634e487b7160e01b82526041600452602482fd5b50346102f05760203660031901126102f0576004356001600160401b03811161097b5760a0600319823603011261097b5760405161156881612cec565b61157482600401612bb7565b815261158260248301612bb7565b90602081019182526040810192604481013584526060820190606481013582526084810135906001600160401b038211610b2c5760046115c59236920101612dbf565b608083019081527fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f468652606560209081526040808820335f908152925290205460ff161561173b57906001600160401b0361169b60209594936116296105ca61354c565b611632426136a2565b60405163481d404160e01b815260ca60048201523360248201526080604482015295516001600160a01b039081166084880152965190961660a4860152965160c4850152915160e4840152945160a0610104840152919485938493909290610124850190612bcb565b91166064830152038173af0d89034d3f82a7101f5d8f485347ed5b1a57305af480156102f3576116fc57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6020813d602011611733575b8161171560209383612d22565b8101031261097b57516001600160401b038116036102f0575f610666565b3d9150611708565b6104ce60206104ae60118961174f33613a08565b906037856104427fccc64574297998b6c3edf6078cc5e01268465ff116954e3af02ff3a70a730f46613aee565b50346102f0578061178c36612e05565b919061179f61179a336136f9565b61315b565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4803b156114e857604051636c5b671f60e11b815260ca60048201526001600160a01b03938416602482015291909216604482015260648101929092528290829060849082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b50346102f05760203660031901126102f05780611857612d77565b61186033613862565b6118686132ca565b6118736105ca61354c565b73af0d89034d3f82a7101f5d8f485347ed5b1a573090813b156102fe576001600160401b03604484926040519485938492630b8b97cf60e01b845260ca60048501521660248301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f05760203660031901126102f0576020906040906001600160a01b0361192c612b5f565b16815260d483522054604051908152f35b50346102f05760803660031901126102f05780611958612b75565b604435908115158092036102fe5761196e612d43565b9161197833613862565b6119806132ca565b7ff0ff655187c1289babeb45eede8a90da4fc42fd6fe87835be936126432469c46845260d56020526119b86105ca6040862054613b90565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4803b156114e857849260a49160405195869485936307f2409960e31b855260ca6004860152600435602486015260018060a01b031660448501526064840152151560848301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f05760203660031901126102f0576004356001600160401b03811161097b573660238201121561097b578060040135611a9881612d8d565b91611aa66040519384612d22565b8183526024602084019260051b820101903682116114e85760248101925b828410611be9578585611ad633613862565b805115611b9a57815b8151811015611b965760ca5483906001600160a01b0316611b008385612ef2565b51516001600160a01b036020611b168688612ef2565b51015116916040611b278688612ef2565b51015190803b156114e857611b68938580946040519687958694859363480ce81160e11b855260048501526024840152606060448401526064830190612bcb565b03925af180156102f357611b81575b5050600101611adf565b81611b8b91612d22565b610baa578284611b77565b8280f35b60405162461bcd60e51b815260206004820152602160248201527f507269636550726f76696465723a2070726963654461746120697320656d70746044820152607960f81b6064820152608490fd5b83356001600160401b038111610b2c57820160606023198236030112610b2c5760405191606083018381106001600160401b03821117611c7a5760405260248201358352611c3960448301612bb7565b60208401526064820135926001600160401b038411611c7657611c66602094936024869536920101612dbf565b6040820152815201930192611ac4565b8880fd5b634e487b7160e01b89526041600452602489fd5b50346102f05760203660031901126102f05780611ca9612d77565b611cb233613862565b611cba6132ca565b611cc56105ca613453565b73af0d89034d3f82a7101f5d8f485347ed5b1a5730611ce3426136a2565b91813b15610d92576040516339561c4360e21b815260ca60048201526001600160401b039182166024820152921660448301528290829060649082905af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b5060603660031901126102f05780600435611d81612b75565b90611d8a6132ca565b8060601c611d973361325f565b15611e3b575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091823b15610d925760848492604051948593849263202bcf3160e01b845260ca6004850152602484015260018060a01b0316604483015260443560648301525af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b611e4f906001600160a01b0316331461315b565b5f611d9d565b50346102f057806003193601126102f057602063ffffffff60d05416604051908152f35b5060203660031901126102f05780611e8f612d77565b611e976132ca565b73eafab0d20d8403d2562a8661d3e274ad14a21cd4611eb5426136a2565b91813b15610d925760405163e96da09960e01b815260ca60048201526001600160401b039182166024820152921660448301523360648301528290829060849082905af480156102f357610d7d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b50346102f057611f4f36612d61565b90825260cf60205260408220908252602052604081208054611f7081612d8d565b90611f7e6040519283612d22565b808252611f8a81612d8d565b602083019390601f1901368537845b828110611fee5750505090604051928392602084019060208552518091526040840192915b818110611fcc575050500390f35b82516001600160401b0316845285945060209384019390920191600101611fbe565b8061200a611ffe600193856139ab565b90549060031b1c6136a2565b6001600160401b0361201c8388612ef2565b9116905201611f99565b50346102f05760e03660031901126102f057612040612b8b565b6064356001600160a01b03811690819003610baa5761205d612ba1565b60c435918215158093036114e85761207433613862565b61207c6132ca565b6120876105ca6134f9565b604051936346b0110160e11b855260ca60048601526004356024860152602435604486015260018060a01b03166064850152608484015260843560a484015260018060a01b031660c483015260e48201526020816101048173eafab0d20d8403d2562a8661d3e274ad14a21cd45af480156102f35761213d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b61215e9060203d602011612165575b6121568183612d22565b810190613198565b505f610d40565b503d61214c565b50346102f05760403660031901126102f057612186612b5f565b61218e612b75565b82549160ff8360081c161592838094612418575b8015612401575b156123a55760ff198116600117855583612394575b506121ea60ff855460081c166121d381613642565b6121dc81613642565b6121e581613642565b613642565b60016101385560ca80546001600160a01b039283166001600160a01b03199182161790915560d28054939092169216919091179055818052606560209081526040808420335f908152925290205460ff1615612345575b81805260976020526122563360408420613bf3565b505f516020613e0e5f395f51905f528252606560209081526040808420335f908152925290205460ff16156122e7575b5f516020613e0e5f395f51905f52825260976020526122a83360408420613bf3565b506122b05780f35b61ff001981541681557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498602060405160018152a180f35b5f516020613e0e5f395f51905f52808352606560209081526040808520335f8181529190935220805460ff191660011790559081907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8580a4612286565b818052606560209081526040808420335f8181529190935220805460ff1916600117905580837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4612241565b61ffff19166101011784555f6121be565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b1580156121a95750600160ff8216146121a9565b50600160ff8216106121a2565b5060603660031901126102f057806004358060601c6124433361325f565b156124db575b5073eafab0d20d8403d2562a8661d3e274ad14a21cd490813b156102fe578290608460405180948193639f0abdf560e01b835260ca60048401526024830152602435604483015260443560648301525af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b6124ef906001600160a01b0316331461315b565b5f612449565b5060a03660031901126102f0578060405161250f81612cec565b612517612b5f565b8152612521612b75565b90602081019182526040810191604435835261253b612d43565b916060810192835261254b612d52565b906080810191825261255e6105ca6134a6565b73af0d89034d3f82a7101f5d8f485347ed5b1a573061257c426136a2565b94813b156106b257604051632d1ef89d60e11b815260ca600482015292516001600160a01b039081166024850152945190941660448301529451606482015291511515608483015251151560a48201523360c48201526001600160401b0390911660e482015290829082906101049082905af480156102f35761069d57505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a180f35b50346102f05760a03660031901126102f05761264a612b8b565b90612653612d43565b61265b612d52565b9061266533613862565b61266d6132ca565b7fe1dcfe90f760d04e605b4db8b553331a44069fbfc060bcb28d982176d0b9f163835260d56020526126a56105ca6040852054613b90565b6040519363da35254960e01b855260ca60048601526004356024860152602435604486015260018060a01b0316606485015215156084840152151560a483015260208260c48173eafab0d20d8403d2562a8661d3e274ad14a21cd45af4908115610871579061083a576020905f516020613dee5f395f51905f528260d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a1600161013855604051908152f35b50346102f05760403660031901126102f05761276b612b75565b336001600160a01b03821603612787576103619060043561359f565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b50346102f05760203660031901126102f0576040602091600435815260d583522054604051908152f35b50346102f05760403660031901126102f05761288c60043561282e612b75565b90612848610355825f526065602052600160405f20015490565b80845260656020526040842060018060a01b0383165f5260205260ff60405f20541615612890575b83526097602052604083206001600160a01b0390911690613bf3565b5080f35b8084526065602090815260408086206001600160a01b0385165f8181529190935220805460ff191660011790553390827f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8780a4612870565b50604036600319011261086557604051604081018181106001600160401b03821117612a1c57604052600435815260243590811515820361086557602081019182526129336132ca565b61293e6105ca613453565b805160601c61294c3361325f565b15612a02575b5073af0d89034d3f82a7101f5d8f485347ed5b1a573091823b156108655760645f92604051948593849263361ae7f360e01b845260ca600485015251602484015251151560448301525af480156129f7576129e457505f516020613dee5f395f51905f52602060d05463ffffffff60018183160116809163ffffffff19161760d055604051908152a160016101385580f35b6129f091505f90612d22565b5f5f610d40565b6040513d5f823e3d90fd5b612a16906001600160a01b0316331461315b565b5f612952565b634e487b7160e01b5f52604160045260245ffd5b3461086557606036600319011261086557610ef3612a4c612b5f565b604435906024359061305e565b34610865575f3660031901126108655760206001600160401b0360ca5460a01c16604051908152f35b34610865576020366003190112610865576020612aad6004355f526065602052600160405f20015490565b604051908152f35b604036600319011261086557612aea612acc612b5f565b612ad46132ca565b60243590309033906001600160a01b0316613322565b600161013855005b34610865576020366003190112610865576004359063ffffffff60e01b821680920361086557602091635a05180f60e01b8114908115612b34575b5015158152f35b637965db0b60e01b811491508115612b4e575b5083612b2d565b6301ffc9a760e01b14905083612b47565b600435906001600160a01b038216820361086557565b602435906001600160a01b038216820361086557565b604435906001600160a01b038216820361086557565b60a435906001600160a01b038216820361086557565b35906001600160a01b038216820361086557565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b80516001600160401b031682526020808201516001600160a01b031690830152604081015191906007831015612c735760c060e091612c7094604085015260ff60608201511660608501526001600160401b0360808201511660808501526001600160401b0360a08201511660a08501520151918160c08201520190612bcb565b90565b634e487b7160e01b5f52602160045260245ffd5b92919060408401906040855280518092526060850191602060608260051b8801019201925f905b828210612cc15750505060209150930152565b909192602080612cdd600193605f198c82030186528851612bef565b96019201920190939291612cae565b60a081019081106001600160401b03821117612a1c57604052565b60e081019081106001600160401b03821117612a1c57604052565b90601f801991011681019081106001600160401b03821117612a1c57604052565b60643590811515820361086557565b60843590811515820361086557565b6040906003190112610865576004359060243590565b600435906001600160401b038216820361086557565b6001600160401b038111612a1c5760051b60200190565b6001600160401b038111612a1c57601f01601f191660200190565b81601f8201121561086557803590612dd682612da4565b92612de46040519485612d22565b8284526020838301011161086557815f926020809301838601378301015290565b6060906003190112610865576004356001600160a01b038116810361086557906024356001600160a01b0381168103610865579060443590565b91908203918211612e4c57565b634e487b7160e01b5f52601160045260245ffd5b60405190612e6d82612d07565b606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b90612ea082612d8d565b612ead6040519182612d22565b8281528092612ebe601f1991612d8d565b01905f5b828110612ece57505050565b602090612ed9612e60565b82828501015201612ec2565b91908201809211612e4c57565b8051821015612f065760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b90604051612f2781612d07565b80928054906001600160401b038216835260018060a01b038260401c16602084015260ff8260e01c166007811015612c735760029260ff91604086015260e81c1660608401526001600160401b036001820154818116608086015260401c1660a08401520190604051915f8154918260011c9060018416938415613054575b6020831085146130405782875286949081156130195750600114612fda575b505060c09291612fd6910384612d22565b0152565b5f908152602081209092505b818310612ffd575050810160200181612fd6612fc5565b6020919350806001915483858901015201910190918492612fe6565b60ff191660208681019190915292151560051b85019092019250839150612fd69050612fc5565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612fa6565b6001600160a01b03165f90815260ce6020526040902080549193919280851080159061311c575b61311457848482116131095761309a91612e3f565b906130a482612e96565b945f5b8381106130b45750505050565b806001600160401b036130d26130cc85600195612ee5565b866139ab565b90549060031b1c165f5260cb6020526130ed60405f20612f1a565b6130f7828a612ef2565b526131028189612ef2565b50016130a7565b61309a915084612e3f565b506060935050565b5083851015613085565b1561312d57565b60405162461bcd60e51b815260206004820152600660248201526514185d5cd95960d21b6044820152606490fd5b1561316257565b60405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606490fd5b90816020910312610865575190565b9160cc5491808410801590613255575b61324e5783838211613243576131cc91612e3f565b926131d684612e96565b935f9160cc54925b8281106131eb5750505050565b6131f58282612ee5565b9084821015612f06576001600160401b0360019260cc5f5260205f200154165f5260cb60205261322760405f20612f1a565b613231828a612ef2565b5261323c8189612ef2565b50016131de565b6131cc915083612e3f565b5060609250565b50828410156131b7565b6001600160a01b03165f9081527fb9eda38d3b4963a3851578208ac76852f2fd7c29cc63b0c858ddf95e8617347c602052604090205460ff1690565b3d156132c5573d906132ac82612da4565b916132ba6040519384612d22565b82523d5f602084013e565b606090565b600261013854146132dd57600261013855565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6040516323b872dd60e01b602082019081526001600160a01b0393841660248301529290931660448401526064808401949094529282526133c2925f90819061336c608486612d22565b60018060a01b03169260405194613384604087612d22565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65646020870152519082855af16133bc61329b565b91613d85565b8051908115918215613430575b5050156133d857565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b819250906020918101031261086557602001518015158103610865575f806133cf565b7f9328e29a14cee8cc1de73d9dcdc6cf7fc66a69681fa4e17e61876a05fd5aaa8a5f5260d56020527f29f5dff74cdba420026d1f355f3094af4bf6d4513353cad055891d738a8e7da954612c7090613b90565b7f9aaa64ff860ee47a0464f727c54600373c133a2d9065df7a14037d1acc78e1765f5260d56020527fa03e3c6860802415a8538423b5b4e963dd97037884be89dee42c5cc27d7c3eb054612c7090613b90565b7f712ed76dcd0d23c46937bf241db09d7cf50075eccf294171d32810d419d650295f5260d56020527f43f3648b0dbdd8d93acb984fdf2bc0655642f76a28a6126f7369170b36b501dd54612c7090613b90565b7fb07ac9ce47eaa05f5a58db2d158d61a72dc71a98b089ed0c8da65f6aec260e0c5f5260d56020527f3cf8718fac1840ae546c9e4108607892524dbe6b1e895cc3ccc71c4f4426eec554612c7090613b90565b906135e991805f52606560205260405f2060018060a01b0383165f5260205260ff60405f2054166135ec575b5f9081526097602052604090206001600160a01b0390911690613c62565b50565b5f8181526065602090815260408083206001600160a01b03861680855292528220805460ff19169055339183907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a46135cb565b1561364957565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b6001600160401b0381116136bc576001600160401b031690565b60405162461bcd60e51b81526020600482015260156024820152744c696254797065436173743a3a4f564552464c4f5760581b6044820152606490fd5b60ca54604051630c90112160e21b81527f11371db1863897581fa5ab4629f9161670f4dcd0da392b411b5ee2bb70eb08af600482015290602090829060249082906001600160a01b03165afa9081156129f7575f9161381e575b506001600160a01b039061376690613d27565b1680156137c8576001600160a01b038216146137c2576001600160a01b03165f9081527fbf11d18b89b41ad20fc1b135dfd494c6d1b14d33459b1575c12bece53af3bcbc602052604090205460ff166137bd575f90565b600190565b50600190565b60405162461bcd60e51b815260206004820152602860248201527f457373656e7469616c436f6e6669674e6f74536574204d435f4645455f4449536044820152672a2924a12aaa27a960c11b6064820152608490fd5b90506020813d602011613848575b8161383960209383612d22565b8101031261086557515f613753565b3d915061382c565b9091901561385c575090565b906139c0565b6001600160a01b0381165f9081527fb2f7eabb536c6e4777097e97a666b03e14e8be5a30142403e71df0906f515d06602052604090205460ff16156138a45750565b60206104ae60116138bf6104ce9460018060a01b0316613a08565b6037846138eb7ffdae3ca625b7a6a67b1c85ddb9efb8e92252215d4a6ec7177e622584b5b236e8613aee565b60405196879476020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b828701528051918291018587015e8401907001034b99036b4b9b9b4b733903937b6329607d1b84830152805192839101604883015e01015f838201520301601f198101835282612d22565b90815f52606560205260405f2060018060a01b0382165f5260205260ff60405f20541615613983575050565b6104ae60116104ce93603760206138eb6139a5829760018060a01b0316613a08565b93613aee565b8054821015612f06575f5260205f2001905f90565b8051909190156139d35750805190602001fd5b60405162461bcd60e51b8152602060048201529081906104ce906024830190612bcb565b908151811015612f06570160200190565b613a12602a612da4565b90613a206040519283612d22565b602a8252613a2e602a612da4565b6020830190601f1901368237825115612f065760309053815160011015612f06576078602183015360295b60018111613aad5750613a695790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b90600f81166010811015612f06576f181899199a1a9b1b9c1cb0b131b232b360811b901a613adb83856139f7565b5360041c908015612e4c575f1901613a59565b613af86042612da4565b90613b066040519283612d22565b60428252613b146042612da4565b6020830190601f1901368237825115612f065760309053815160011015612f06576078602183015360415b60018111613b4f5750613a695790565b90600f81166010811015612f06576f181899199a1a9b1b9c1cb0b131b232b360811b901a613b7d83856139f7565b5360041c908015612e4c575f1901613b3f565b80158015613be9575b15613ba45760011490565b60405162461bcd60e51b815260206004820152601c60248201527f4c696254797065436173743a3a494e56414c49445f424f4f4c45414e000000006044820152606490fd5b5060018114613b99565b6001810190825f528160205260405f2054155f14613c5b57805468010000000000000000811015612a1c57613c48613c328260018794018555846139ab565b819391549060031b91821b915f19901b19161790565b905554915f5260205260405f2055600190565b5050505f90565b906001820191815f528260205260405f20548015155f14613d1f575f198101818111612e4c5782545f19810191908211612e4c57808203613cea575b50505080548015613cd6575f190190613cb782826139ab565b8154905f199060031b1b19169055555f526020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b613d0a613cfa613c3293866139ab565b90549060031b1c928392866139ab565b90555f528360205260405f20555f8080613c9e565b505050505f90565b6bffffffffffffffffffffffff8116613d405760601c90565b60405162461bcd60e51b815260206004820152601c60248201527f4c696254797065436173743a3a494e56414c49445f41444452455353000000006044820152606490fd5b91929015613de75750815115613d99575090565b3b15613da25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b826139c056fe974288fc79dc8b8b1fe9f1f0b8f5738873e53e07d3122c5e3c02b834c018d6cb339759585899103d2ace64958e37e18ccb0504652c81d4a1b8aa80fe2126ab95a2646970667358221220a338685c3305b8b74e973509cbf51effd8159e8f3a99739e9af59708695ace9a64736f6c634300081c0033

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

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.