BERA Price: $3.61 (-1.95%)

Contract

0x44e94a373FfB5055a28831a23D61b0864F5c772d

Overview

BERA Balance

Berachain LogoBerachain LogoBerachain Logo0 BERA

BERA Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LimitOrderBook

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 100000000 runs

Other Settings:
paris EvmVersion
File 1 of 45 : LimitOrderBook.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IPerpetualManager.sol";
import "../interfaces/IPerpetualOrder.sol";
import "../interfaces/IClientOrder.sol";
import "../../libraries/Bytes32Pagination.sol";
import "../../libraries/OrderFlags.sol";
import "../functions/PerpetualHashFunctions.sol";

/**
 * @title Limit/Stop Order Book Proxy Contract.
 *
 * @notice A new perpetual limit order book contract.
 * Each order is sent to the perpetual-specific limit order book contract instance. Executions can be started by
 * any participant but only go via instances of this contract.
 * Orders can be posted by anyone but they require valid signatures if not submitted by the trader. This allows brokers
 * to offer a gas-free execution of orders to their traders (the broker pays for gas).
 * Orders cannot be replayed.
 * The order submitted by the client is of type ClientOrder that contains a
 * possible parent/child link. The ClientOrder data is transformed into a Order struct which
 * strips off the parent/child link and adds the submitted-block number.
 * The submitted block number is relevant to avoid early execution (front-running prevention).
 * The parent/child link is important for orders that are conditional on other orders.
 * Parent child structure is as follows:
 *  - relationship is reflected with the two order-digests parentChildDigest1 and parentChildDigest2
 *  - a child can only be executed if the linked parent: is executed/ is cancelled/ never existed
 *  - a parent can be executed if other conditions (e.g. limit price) hold
 *  - if a parent is cancelled due to some trade failure (e.g. Market Order hits slippage limit), children are cancelled too
 *  - parents can still be cancelled by trader which does not imply the children are cancelled
 *
 * */
contract LimitOrderBook is IPerpetualOrder, IClientOrder, Initializable {
    using Bytes32Pagination for bytes32[];
    using Address for address;
    using PerpetualHashFunctions for Order;

    uint256 private constant MAX_ORDERS_PER_TRADER = 50;
    uint32 public constant CALLBACK_GAS_LIMIT = 500_000;
    // Events
    event PerpetualLimitOrderCreated(
        uint24 indexed perpetualId,
        address indexed trader,
        address brokerAddr,
        Order order,
        bytes32 digest
    );

    event ExecutionFailed(
        uint24 indexed perpetualId,
        address indexed trader,
        bytes32 digest,
        string reason
    );

    // traders can add a callback address to their main order which will be called
    // on execution.
    event Callback(address callbackTarget, bool success, uint32 gasLimit);

    enum OrderStatus {
        CANCELED,
        EXECUTED,
        OPEN,
        UNKNOWN
    }
    struct OrderDependency {
        // Parents have either 2 non-zero entries,
        // or just the first !=0
        // A child has parentChildEntry1=0 and
        // stores the single parent in entry2
        bytes32 parentChildDigest1;
        bytes32 parentChildDigest2;
    }

    uint8 private iCancelDelaySec;

    // Stores perpetual id - specific to a perpetual
    uint24 public perpetualId;
    // timestamp when the market was observed to re-open.
    // used to prevent cancel orders being executed right after re-open.
    // marketCloseSwitchTimestamp>0: timestamp when the market was opened
    // marketCloseSwitchTimestamp<0: abs(.) timestamp when the market was closed
    int64 public marketCloseSwitchTimestamp;

    // Address of trader => digests (orders)
    mapping(address => bytes32[]) public digestsOfTrader;

    // Digest of an order => the order and its data
    mapping(bytes32 => IPerpetualOrder.Order) public orderOfDigest;

    // OrderDigest => Dependencies
    mapping(bytes32 => OrderDependency) public orderDependency;

    bytes32[] private actvDigests; // active order digests
    mapping(bytes32 => uint256) public actvDigestPos; // position of order digest in array

    // OrderDigest => callback function on execution/fail
    mapping(bytes32 => address) public callbackFunctions;

    // Stores last order digest
    bytes32 public lastOrderHash;

    // Perpetual Manager
    IPerpetualManager public perpManager;

    constructor() {
        _disableInitializers();
    }

    /**
     * @notice Creates the Perpetual Limit Order Book.
     * @dev Replacement of constructor by initialize function for Upgradable Contracts
     * This function will be called only once while deploying order book using Factory.
     * @param _perpetualManagerAddr the address of perpetual proxy manager.
     * @param _perpetualId The id of perpetual.
     * @param _iCancelDelaySec How many seconds do we need to wait for canceling to be allowed
     * */
    function initialize(
        address _perpetualManagerAddr,
        uint24 _perpetualId,
        uint8 _iCancelDelaySec
    ) external initializer {
        // payable constructor reduces contract size
        require(_perpetualManagerAddr != address(0), "perpetual manager invalid");
        require(_perpetualId != uint24(0), "perpetualId invalid");
        perpetualId = _perpetualId;
        perpManager = IPerpetualManager(_perpetualManagerAddr);
        iCancelDelaySec = _iCancelDelaySec;
        marketCloseSwitchTimestamp = int64(uint64(block.timestamp));
    }

    /**
     * @notice Creates Limit/Stop Orders using an array of order objects with the following fields:
     * iPerpetualId  global id for perpetual
     * traderAddr    address of trader
     * fAmount       amount in base currency to be traded
     * fLimitPrice   limit price
     * fTriggerPrice trigger price, non-zero for stop orders
     * iDeadline     deadline for price (seconds timestamp)
     * executorAddr  address of abstract executor
     * flags         trade flags
     * @param _orders the orders' details.
     * @param _signatures The traders signatures. Required if broker submits order, otherwise it can be bytes32(0)
     * */
    function postOrders(ClientOrder[] calldata _orders, bytes[] calldata _signatures) external {
        require(_orders.length > 0 && _orders.length == _signatures.length, "arrays mismatch");
        _handleMarketOpening();
        for (uint256 i; i < _orders.length; ) {
            _postOrder(_orders[i], _signatures[i]);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * Internal version of postOrder
     * @param _order the order details.
     * @param _signature The traders signature. Required if broker submits order, otherwise it can be bytes32(0)
     * @return digest of the order
     */
    function _postOrder(
        ClientOrder calldata _order,
        bytes memory _signature
    ) internal returns (bytes32 digest) {
        // Validations
        require(perpetualId == _order.iPerpetualId, "wrong order book");
        require(_order.traderAddr != address(0), "invalid-trader");
        require(_order.fAmount != 0, "invalid amount");
        require(_order.iDeadline > block.timestamp, "invalid-deadline");
        // executionTimestamp prior to now+7 days
        require(
            _order.executionTimestamp < _order.iDeadline &&
                _order.executionTimestamp < block.timestamp + 604800,
            "invalid exec ts"
        );
        if (OrderFlags.isStopOrder(_order.flags)) {
            require(_order.fTriggerPrice > 0, "invalid trigger price");
        }

        // copy client-order into a more lean perp-order
        Order memory perpOrder;
        perpOrder.flags = _order.flags;
        perpOrder.iPerpetualId = _order.iPerpetualId;
        perpOrder.brokerFeeTbps = _order.brokerFeeTbps;
        perpOrder.traderAddr = _order.traderAddr;
        perpOrder.brokerAddr = _order.brokerAddr;
        perpOrder.brokerSignature = _order.brokerSignature;
        perpOrder.fAmount = _order.fAmount;
        perpOrder.fLimitPrice = _order.fLimitPrice;
        perpOrder.fTriggerPrice = _order.fTriggerPrice;
        perpOrder.leverageTDR = _order.leverageTDR;
        perpOrder.iDeadline = _order.iDeadline;
        perpOrder.executionTimestamp = _order.executionTimestamp;
        perpOrder.submittedTimestamp = uint32(block.timestamp);

        if (_order.brokerSignature.length > 0) {
            address signatory = ECDSA.recover(
                perpOrder._getBrokerDigest(address(perpManager)),
                _order.brokerSignature
            );
            require(signatory != address(0), "invalid broker sig");
            require(signatory == _order.brokerAddr, "invalid broker sig");
        }
        digest = perpOrder._getDigest(address(perpManager), true);
        // register the dependency between orders
        if (_order.parentChildDigest1 != bytes32(0) || _order.parentChildDigest2 != bytes32(0)) {
            // no link to itself
            require(
                _order.parentChildDigest1 != digest && _order.parentChildDigest2 != digest,
                "order self-linked"
            );
            orderDependency[digest] = OrderDependency({
                parentChildDigest1: _order.parentChildDigest1,
                parentChildDigest2: _order.parentChildDigest2
            });
        }
        if (_order.callbackTarget != address(0) && _order.callbackTarget.isContract()) {
            callbackFunctions[digest] = _order.callbackTarget;
        }
        // if broker submits the trader on behalf of the trader,
        // the trader needs to have a signature in the order
        if (
            msg.sender != _order.traderAddr &&
            !perpManager.isDelegate(_order.traderAddr, msg.sender)
        ) {
            //if no signature reverts with ECDSA: invalid signature length
            address signatory = ECDSA.recover(digest, _signature);
            //Verify address is not null and PK is not null either.
            require(signatory != address(0), "invalid signature");
            require(signatory == perpOrder.traderAddr, "invalid signature");
        }

        require(orderOfDigest[digest].traderAddr == address(0), "order-exists");
        require(
            digestsOfTrader[perpOrder.traderAddr].length < MAX_ORDERS_PER_TRADER,
            "too many orders"
        );
        // prevent clogging order books through replay of adversary, immunefy 9652
        require(!perpManager.isOrderExecuted(digest), "order executed");
        require(!perpManager.isOrderCanceled(digest), "order canceled");

        // register
        orderOfDigest[digest] = perpOrder;
        digestsOfTrader[_order.traderAddr].push(digest);
        // add order to orderbook linked list
        actvDigests.push(digest);
        // the position entry is position + 1
        // (first element 1 so we can distinguish from not existing and pos 0)
        actvDigestPos[digest] = actvDigests.length;

        emit PerpetualLimitOrderCreated(
            _order.iPerpetualId,
            _order.traderAddr,
            _order.brokerAddr,
            perpOrder,
            digest
        );
        return digest;
    }

    function _perpOrderToClientOrder(
        Order storage _order,
        bytes32 _orderDigest
    ) internal view returns (ClientOrder memory clientOrder) {
        clientOrder.flags = _order.flags;
        clientOrder.iPerpetualId = _order.iPerpetualId;
        clientOrder.brokerFeeTbps = _order.brokerFeeTbps;
        clientOrder.traderAddr = _order.traderAddr;
        clientOrder.brokerAddr = _order.brokerAddr;
        clientOrder.brokerSignature = _order.brokerSignature;
        clientOrder.fAmount = _order.fAmount;
        clientOrder.fLimitPrice = _order.fLimitPrice;
        clientOrder.fTriggerPrice = _order.fTriggerPrice;
        clientOrder.leverageTDR = _order.leverageTDR;
        clientOrder.iDeadline = _order.iDeadline;
        clientOrder.executionTimestamp = _order.executionTimestamp;
        clientOrder.parentChildDigest1 = orderDependency[_orderDigest].parentChildDigest1;
        clientOrder.parentChildDigest2 = orderDependency[_orderDigest].parentChildDigest2;
        return clientOrder;
    }

    /**
     * marketCloseSwitchBlock stores the last block that the market was
     * observed to be closed or opened. If the market was closed,
     * the sign is negative, if the market is open, the sign is positive.
     * Example 123, this means the market was first observed to be opened
     *  at block 123
     * Example -345, this means the market was first observed to be closed
     *  at block 345
     */
    function _handleMarketOpening() internal {
        bool isClosed = perpManager.isPerpMarketClosed(perpetualId);
        if (marketCloseSwitchTimestamp > 0 && isClosed) {
            // the market was open marketCloseSwitchBlock>0, but
            // is closed now
            marketCloseSwitchTimestamp = -int64(uint64(block.timestamp));
        } else if (marketCloseSwitchTimestamp < 0 && !isClosed) {
            // the market was closed marketCloseSwitchBlock<0, but
            // is open now
            marketCloseSwitchTimestamp = int64(uint64(block.timestamp));
        }
    }

    /**
     * @notice Execute Orders or cancel & remove them (if expired).
     * @dev Interacts with the PerpetualTradeManager.
     * @param _digests hash of the order.
     * @param _executorAddr address that will receive referral rebate
     * */
    function executeOrders(
        bytes32[] calldata _digests,
        address _executorAddr,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable {
        uint256 numOrders = _digests.length;
        require(numOrders > 0, "no orders");

        // oracle: reverts if data is invalid (too old, wrong feeds)
        uint256 tSub = orderOfDigest[_digests[0]].submittedTimestamp;
        uint256 tExc = orderOfDigest[_digests[0]].executionTimestamp;
        uint256 timeLimit = tSub > tExc ? tSub : tExc;
        uint256 shortestTimeElapsed = timeLimit < block.timestamp
            ? block.timestamp - timeLimit
            : 0;
        for (uint256 i = 1; i < numOrders; ) {
            tSub = orderOfDigest[_digests[i]].submittedTimestamp;
            tExc = orderOfDigest[_digests[i]].executionTimestamp;
            timeLimit = tSub > tExc ? tSub : tExc;
            uint256 age = timeLimit < block.timestamp ? block.timestamp - timeLimit : 0;
            shortestTimeElapsed = shortestTimeElapsed < age ? shortestTimeElapsed : age;
            unchecked {
                ++i;
            }
        }
        // set 'maxAcceptableFeedAge' to the shortest time elapsed between order posting and now
        // to ensure the price is newer than the order.
        if (_updateData.length > 0) {
            perpManager.updatePriceFeeds{ value: msg.value }(
                perpetualId,
                _updateData,
                _publishTimes,
                shortestTimeElapsed
            );
        }
        require(timeLimit <= perpManager.getOracleUpdateTime(perpetualId), "outdated oracles");

        // update state if needed (closed -> open)
        _handleMarketOpening();
        // execute orders one by one
        for (uint256 i; i < numOrders; ) {
            _executeOrder(_digests[i], _executorAddr);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Cancels limit/stop order
     * @dev Order can be cancelled by the trader himself or it can be
     * removed by the relayer if it has expired.
     * @param _digest hash of the order.
     * @param _signature signed cancel-order; 0 if order expired
     * */
    function cancelOrder(
        bytes32 _digest,
        bytes calldata _signature,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable {
        Order memory order = orderOfDigest[_digest];
        require(perpetualId == order.iPerpetualId, "order not found");
        perpManager.updatePriceFeeds{ value: msg.value }(
            perpetualId,
            _updateData,
            _publishTimes,
            0
        );
        _handleMarketOpening();

        // market is open:
        uint64 tsStart = marketCloseSwitchTimestamp >= 0
            ? uint64(marketCloseSwitchTimestamp)
            : uint64(-marketCloseSwitchTimestamp);
        if (tsStart < order.submittedTimestamp) {
            tsStart = order.submittedTimestamp;
        }
        // cannot cancel when market is closed, or not sufficient delay since close
        if (marketCloseSwitchTimestamp < 0 || block.timestamp < tsStart + iCancelDelaySec) {
            emit ExecutionFailed(perpetualId, order.traderAddr, _digest, "cancel delay required");
        } else {
            // allow only signed cancel if cancel was not executed by trader
            if (
                msg.sender != order.traderAddr &&
                !perpManager.isDelegate(order.traderAddr, msg.sender)
            ) {
                address signatory = ECDSA.recover(
                    order._getDigest(address(perpManager), false),
                    _signature
                );
                // order.traderAddr cannot be zero (_postOrder would have reverted)
                // --> it suffices to check that signatory = trader
                require(signatory == order.traderAddr, "trader must sign");
            }
            perpManager.executeCancelOrder(perpetualId, _digest);
            _removeOrder(_digest);
            _invokeCallback(_digest, false);
        }
    }

    /**
     * @notice Execute Order or cancel & remove it
     * @param _digest order Id (hash of the order)
     * @param _executorAddr Address to credit for the order execution
     */
    function _executeOrder(bytes32 _digest, address _executorAddr) internal {
        Order memory order = orderOfDigest[_digest];
        address trader = order.traderAddr;
        require(trader != address(0x0), "order not found");
        order.executorAddr = _executorAddr;
        // check whether this is a child order that has an outstanding dependency,
        // if so revert (orders stay in order book)
        require(!_hasOutstandingDependency(_digest), "dpcy not fulfilled");
        // Remove the order (locally) if it has expired
        bool removeDependency; // = false;
        bool isExecuted; // = false;
        require(block.timestamp >= order.executionTimestamp, "exec too early");
        try perpManager.tradeViaOrderBook(order, msg.sender) returns (bool orderSuccess) {
            if (!orderSuccess) {
                // if tradeViaOrderBook returns false then the order execution failed
                // this happens if:
                if (order.iDeadline <= block.timestamp) {
                    // 1) order is expired
                    //    this is to have expired orders removed from the order book
                    emit ExecutionFailed(perpetualId, trader, _digest, "deadline");
                } else {
                    // 2) order price exceeds limit and the order is market type
                    //    this is to have a "fill-or-kill" behavior for market orders with slippage protection
                    emit ExecutionFailed(perpetualId, trader, _digest, "price exceeds limit");
                }
                // remove dependent orders if parent order expired or execution failed due to slippage
                removeDependency = true;
            } else {
                // if tradeViaOrderBook returns true then either
                // 1) execution was successful
                // 2) or order is close only and there is no position to close
                isExecuted = perpManager.isOrderExecuted(_digest);
            }
        } catch Error(string memory reason) {
            /*
                    order should be removed in the following cases
                        "already closed" <- REMOVE THIS IN CONTRACT AND TESTS
                        "trade amount too small" <- no rebate
                        "position too small" <- no rebate
                        "no amount to close" <-  REMOVE THIS IN CONTRACT AND TESTS; no rebate
                        "trader has no position to close" <- no rebate
                        "allowance not enough" <- no rebate
                        "balance not enough" <- no rebate
                        "margin not enough" <- no rebate
                        "state should be NORMAL" <- no rebate (rare)
                        "cannot be closing if no exposure" <- indifferent
                        "Trade amt>max amt for trader/AMM" <- rebate
                        Do not delete dependencies:
                            "order cancelled" <- no rebate    <- don't delete children
                            "order executed" <- no rebate <- don't delete children
                            market order: "price exceeds limit" <- we end up in !orderSuccess above
                    order should remain in order book (=revert) if
                        "Trade amt>max amt for trader/AMM"
                        "delay required"
                        "market is closed"
                        "trade is close only"
                        limit/stop order: "price exceeds limit"
                        "trigger cond not met"
                    isFillOrKill -> always remove order, except when delay was not met
                */
            if (_isStringEqual(reason, "Trade amt>max amt for trader/AMM")) {
                removeDependency = true;
            } else if (
                _isStringEqual(reason, "delay required") ||
                (!OrderFlags.isFillOrKill(order.flags) &&
                    (_isStringEqual(reason, "market is closed") ||
                        _isStringEqual(reason, "trigger cond not met") ||
                        (!OrderFlags.isMarketOrder(order.flags) &&
                            (_isStringEqual(reason, "trade is close only") ||
                                _isStringEqual(reason, "price exceeds limit") ||
                                _isStringEqual(reason, "outdated oracles")))))
            ) {
                // order should remain in order book, re-throw error
                revert(reason);
            } else if (
                _isStringEqual(reason, "order cancelled") ||
                _isStringEqual(reason, "order executed")
            ) {
                removeDependency = false;
            } else {
                removeDependency = true;
            }
            // emit event
            emit ExecutionFailed(perpetualId, trader, _digest, reason);
        }

        // if expired or executed or caught in "orderly fail", we remove the order
        // if the order does not match with prices, there is a revert so
        // we do not end up here
        _wipeOrder(_digest, removeDependency);
        _invokeCallback(_digest, isExecuted);
    }

    /**
     * Check whether there is a dependent parent order that has not been executed
     * Returns false if the order is a parent order,
     * returns false if the order has no parent order,
     * returns false if parent order was executed or cancelled
     * returns false if the parent order does not exist
     * @param _digest order digest for the order we check
     * @return boolean whether has dependent parent order (that is not executed or cancelled)
     */
    function _hasOutstandingDependency(bytes32 _digest) internal view returns (bool) {
        OrderDependency storage dpcy = orderDependency[_digest];
        if (dpcy.parentChildDigest1 != bytes32(0)) {
            // is a parent order
            return false;
        }
        bytes32 parentDigest = dpcy.parentChildDigest2;
        if (
            parentDigest == bytes32(0) || // no parent, Jesus
            perpManager.isOrderCanceled(parentDigest) || // parent order was cancelled
            perpManager.isOrderExecuted(parentDigest) || // parent order was executed
            orderOfDigest[parentDigest].traderAddr == address(0) // parent order does not exist
        ) {
            return false;
        }
        return true;
    }

    /**
     * Remove order from this order book and add to cancel list in perpetual manager proxy
     * @param _digest   order digest
     */
    function _wipeOrder(bytes32 _digest, bool _removeDependency) internal {
        // remove from this order book
        _removeOrder(_digest);
        // ensure order cannot be replayed: add to cancel list in perpetual manager
        if (!perpManager.isOrderExecuted(_digest) && !perpManager.isOrderCanceled(_digest)) {
            perpManager.executeCancelOrder(perpetualId, _digest);
        }
        // remove dependent orders if this was a parent order
        if (_removeDependency) {
            _wipeDependentOrders(_digest);
        }
        delete orderDependency[_digest];
    }

    /**
     * If _parentDigest is indeed a parent order, children will be
     * removed.
     * Delete orderDependency for a given parent order.
     * Delete child orders and their dependency entries.
     * @param _parentDigest orderId of parent order
     */
    function _wipeDependentOrders(bytes32 _parentDigest) internal {
        (bytes32 child1, bytes32 child2) = _getValidChildren(_parentDigest);
        if (child1 != bytes32(0)) {
            _wipeOrder(child1, false);
        }
        if (child2 != bytes32(0)) {
            _wipeOrder(child2, false);
        }
    }

    /**
     * Parent can have one or two children, child only has 1 parent, hence:
     * Parent: _dpcy.parentChildEntry1!=0
     * Child: _dpcy.parentChildEntry2!=0 && _dpcy.parentChildEntry1==0
     * @param _dpcy dependency struct
     */
    function _isParentOrder(OrderDependency storage _dpcy) internal view returns (bool) {
        return _dpcy.parentChildDigest1 != bytes32(0);
    }

    /**
     * Return digests of 2 children (or 0)
     * @param _parent digest of parent order
     * @return bytes32(0) or digest of child order 1
     * @return bytes32(0) or digest of child order 2
     */
    function _getValidChildren(bytes32 _parent) internal view returns (bytes32, bytes32) {
        OrderDependency storage dpcy = orderDependency[_parent];
        if (!_isParentOrder(dpcy)) {
            return (bytes32(0), bytes32(0));
        }
        bytes32 child1 = dpcy.parentChildDigest1;
        if (orderDependency[child1].parentChildDigest2 != _parent) {
            // child order does not have parent entry
            child1 = bytes32(0);
        }
        if (dpcy.parentChildDigest2 == bytes32(0)) {
            // parent only has 1 child entry
            return (child1, bytes32(0));
        }
        bytes32 child2 = dpcy.parentChildDigest2;
        if (orderDependency[child2].parentChildDigest2 != _parent) {
            // child order does not have parent entry
            return (child1, bytes32(0));
        }
        return (child1, child2);
    }

    /**
     * Get number of active orders
     */
    function orderCount() external view returns (uint256) {
        return actvDigests.length;
    }

    /**
     * @notice Internal function to remove order from order book.
     * @dev We do not remove entry from orderDependency.
     * */
    function _removeOrder(bytes32 _digest) internal {
        if (_digest == bytes32(0)) {
            // zero digest is always empty
            return;
        }
        // remove from trader's order-array 'orderOfDigest'
        // Order storage order = orderOfDigest[_digest];
        bytes32[] storage orderArr = digestsOfTrader[orderOfDigest[_digest].traderAddr];
        // done if nothing to remove
        if (orderArr.length == 0) {
            return;
        }
        uint256 k;
        while (k < orderArr.length) {
            if (orderArr[k] == _digest) {
                orderArr[k] = orderArr[orderArr.length - 1];
                orderArr.pop();
                k = MAX_ORDERS_PER_TRADER;
            }
            unchecked {
                ++k;
            }
        }
        // remove order
        delete orderOfDigest[_digest];
        // remove from active orders
        // set position of currently last element in activeDigests array to the
        // position of the element we want to delete
        uint256 pos = actvDigestPos[_digest];
        bytes32 last = actvDigests[actvDigests.length - 1];
        actvDigestPos[last] = pos;
        actvDigestPos[_digest] = 0;
        actvDigests[pos - 1] = last;
        actvDigests.pop();
    }

    /**
     * Return the order status: OPEN, EXECUTED, CANCELED, UNKNOWN order
     * @param _digest   order identifier
     */
    function getOrderStatus(bytes32 _digest) external view returns (OrderStatus) {
        if (perpManager.isOrderCanceled(_digest)) {
            return OrderStatus.CANCELED;
        }
        if (perpManager.isOrderExecuted(_digest)) {
            return OrderStatus.EXECUTED;
        }
        if (orderOfDigest[_digest].traderAddr == address(0)) {
            return OrderStatus.UNKNOWN;
        }
        return OrderStatus.OPEN;
    }

    /**
     * @notice Returns the number of (active) limit orders of a trader
     * @param trader address of trader.
     * */
    function numberOfDigestsOfTrader(address trader) external view returns (uint256) {
        return digestsOfTrader[trader].length;
    }

    // /**
    //  * @notice Returns the number of all limit orders - including those
    //  * that are cancelled/removed.
    //  * */
    // function numberOfAllDigests() external view returns (uint256) {
    //     return allDigests.length;
    // }

    /**
     * @notice Returns an array of digests of orders of a trader
     * @param trader address of trader.
     * @param page start/offset.
     * @param limit count.
     * */
    function limitDigestsOfTrader(
        address trader,
        uint256 page,
        uint256 limit
    ) external view returns (bytes32[] memory) {
        return digestsOfTrader[trader].paginate(page, limit);
    }

    // /**
    //  * @notice Returns an array of all digests - including those
    //  * that are cancelled/removed.
    //  * */
    // function allLimitDigests(uint256 page, uint256 limit)
    //     external
    //     view
    //     returns (bytes32[] memory)
    // {
    //     return allDigests.paginate(page, limit);
    // }

    /**
     * @notice Returns the address of trader for an order digest
     * @param digest order digest.
     * @return trader address
     * */
    function getTrader(bytes32 digest) external view returns (address) {
        return orderOfDigest[digest].traderAddr;
    }

    /**
     * @notice Returns all orders(specified by offset/start and limit/count) of a trader
     * @param trader address of trader.
     * @param offset start.
     * @param limit count.
     * @return orders : array of orders
     * */
    function getOrders(
        address trader,
        uint256 offset,
        uint256 limit
    ) external view returns (ClientOrder[] memory orders) {
        orders = new ClientOrder[](limit);
        bytes32[] storage digests = digestsOfTrader[trader];
        for (uint256 i = 0; i < limit; ) {
            if (i + offset < digests.length) {
                bytes32 digest = digests[i + offset];
                orders[i] = _perpOrderToClientOrder(orderOfDigest[digest], digest);
            }
            unchecked {
                ++i;
            }
        }
    }

    /**
     * Polls all orders in the given range. Return arrays
     * always have length numElements with zero entries if not
     * available
     * @param _from start index
     * @param _numElements end index
     * @return orders client orders found
     * @return orderHashes corresponding order hashes
     * @return submittedTs corresponding submitted timestamps
     */
    function pollRange(
        uint256 _from,
        uint256 _numElements
    )
        external
        view
        returns (
            ClientOrder[] memory orders,
            bytes32[] memory orderHashes,
            uint32[] memory submittedTs
        )
    {
        uint256 to = _from + _numElements;
        if (to > actvDigests.length) {
            to = actvDigests.length;
        }
        // for backward compatibility we return zero elements
        // if no more orders available
        orders = new ClientOrder[](_numElements);
        orderHashes = new bytes32[](_numElements);
        submittedTs = new uint32[](_numElements);

        for (uint256 k = _from; k < to; ) {
            uint256 j = k - _from;
            orderHashes[j] = actvDigests[k];
            orders[j] = _perpOrderToClientOrder(orderOfDigest[orderHashes[j]], orderHashes[j]);
            submittedTs[j] = orderOfDigest[orderHashes[j]].submittedTimestamp;
            unchecked {
                k++;
            }
        }
    }

    function _isStringEqual(string memory _a, string memory _b) internal pure returns (bool) {
        return
            (bytes(_a).length == bytes(_b).length) &&
            (keccak256(bytes(_a)) == keccak256(bytes(_b)));
    }

    /**
     * Invokes the callback function
     * @param _orderDigest order identifier
     * @param _wasExecuted true if order was executed successfully, false if order removed
     */
    function _invokeCallback(bytes32 _orderDigest, bool _wasExecuted) internal {
        address callbackTarget = callbackFunctions[_orderDigest];
        delete callbackFunctions[_orderDigest];
        if (callbackTarget == address(0)) {
            return;
        }

        if (CALLBACK_GAS_LIMIT == 0) {
            return;
        }

        bool success;
        try
            ID8XExecutionCallbackReceiver(callbackTarget).d8xExecutionCallback{
                gas: CALLBACK_GAS_LIMIT
            }(_orderDigest, _wasExecuted)
        {
            success = true;
        } catch {}
        emit Callback(callbackTarget, success, CALLBACK_GAS_LIMIT);
    }
}

File 2 of 45 : Initializable.sol
// 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;
    }
}

File 3 of 45 : AddressUpgradeable.sol
// 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);
        }
    }
}

File 4 of 45 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

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

pragma solidity ^0.8.0;

/**
 * @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 ReentrancyGuard {
    // 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;

    constructor() {
        _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;
    }
}

File 6 of 45 : Address.sol
// 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 Address {
    /**
     * @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);
        }
    }
}

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

pragma solidity ^0.8.0;

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

File 8 of 45 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

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

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 9 of 45 : Math.sol
// 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 Math {
    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);
        }
    }
}

File 10 of 45 : SignedMath.sol
// 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 SignedMath {
    /**
     * @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);
        }
    }
}

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

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    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 = Math.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(SignedMath.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, Math.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));
    }
}

File 12 of 45 : Maintainable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
// D8X, 2022

pragma solidity 0.8.21;

/**
 * This is a modified version of the OpenZeppelin ownable contract
 * Modifications
 * - instead of an owner, we have two actors: maintainer and governance
 * - maintainer can have certain priviledges but cannot transfer maintainer mandate
 * - governance can exchange maintainer and exchange itself
 * - renounceOwnership is removed
 *
 *
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Maintainable {
    address private _maintainer;
    address private _governance;

    event MaintainerTransferred(address indexed previousMaintainer, address indexed newMaintainer);
    event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);

    /**
     * @dev Initializes the contract setting the deployer as the initial maintainer.
     */
    constructor() {
        _transferMaintainer(msg.sender);
        _transferGovernance(msg.sender);
    }

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyMaintainer() {
        require(maintainer() == msg.sender, "only maintainer");
        _;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyGovernance() {
        require(governance() == msg.sender, "only governance");
        _;
    }

    /**
     * @dev Transfers maintainer mandate of the contract to a new account (`newMaintainer`).
     * Can only be called by the governance.
     */
    function transferMaintainer(address newMaintainer) public virtual {
        require(msg.sender == _governance, "only governance");
        require(newMaintainer != address(0), "zero address");
        _transferMaintainer(newMaintainer);
    }

    /**
     * @dev Transfers governance mandate of the contract to a new account (`newGovernance`).
     * Can only be called by the governance.
     */
    function transferGovernance(address newGovernance) public virtual {
        require(msg.sender == _governance, "only governance");
        require(newGovernance != address(0), "zero address");
        _transferGovernance(newGovernance);
    }

    /**
     * @dev Transfers maintainer of the contract to a new account (`newMaintainer`).
     * Internal function without access restriction.
     */
    function _transferMaintainer(address newMaintainer) internal virtual {
        address oldM = _maintainer;
        _maintainer = newMaintainer;
        emit MaintainerTransferred(oldM, newMaintainer);
    }

    /**
     * @dev Transfers governance of the contract to a new account (`newGovernance`).
     * Internal function without access restriction.
     */
    function _transferGovernance(address newGovernance) internal virtual {
        address oldG = _governance;
        _governance = newGovernance;
        emit GovernanceTransferred(oldG, newGovernance);
    }
}

File 13 of 45 : IShareTokenFactory.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

interface IShareTokenFactory {
    function createShareToken(uint8 _poolId, address _marginTokenAddr) external returns (address);
}

File 14 of 45 : ISpotOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

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

    function setMarketClosed(bool _marketClosed) external;

    /**
     *  Spot price, confidence, timestamp.
     */
    function getSpotPrice() external view returns (int128, uint64, uint256);

    /**
     *  Ema price, confidence, timestamp.
     */
    function getEmaPrice() external view returns (int128, uint64, uint256);

    /**
     * Get base currency symbol.
     */
    function getBaseCurrency() external view returns (bytes4);

    /**
     * Get quote currency symbol.
     */
    function getQuoteCurrency() external view returns (bytes4);

    /**
     * Price Id
     */
    function priceId() external view returns (bytes32);

    /**
     * Address of the underlying feed.
     */
    function priceFeed() external view returns (address);

    /**
     * Conservative update period of this feed in seconds.
     */
    function feedPeriod() external view returns (uint256);
}

File 15 of 45 : ABDKMath64x64.sol
// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity 0.8.21;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /**
     * Convert signed 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromInt(int256 x) internal pure returns (int128) {
        require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromInt");
        return int128(x << 64);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 64-bit integer number
     * rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64-bit integer number
     */
    function toInt(int128 x) internal pure returns (int64) {
        return int64(x >> 64);
    }

    /**
     * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
     * number.  Revert on overflow.
     *
     * @param x unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function fromUInt(uint256 x) internal pure returns (int128) {
        require(x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromUInt");
        return int128(int256(x << 64));
    }

    /**
     * Convert signed 64.64 fixed point number into unsigned 64-bit integer
     * number rounding down.  Revert on underflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return unsigned 64-bit integer number
     */
    function toUInt(int128 x) internal pure returns (uint64) {
        require(x >= 0, "ABDK.toUInt");
        return uint64(uint128(x >> 64));
    }

    /**
     * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
     * number rounding down.  Revert on overflow.
     *
     * @param x signed 128.128-bin fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function from128x128(int256 x) internal pure returns (int128) {
        int256 result = x >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.from128x128");
        return int128(result);
    }

    /**
     * Convert signed 64.64 fixed point number into signed 128.128 fixed point
     * number.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 128.128 fixed point number
     */
    function to128x128(int128 x) internal pure returns (int256) {
        return int256(x) << 64;
    }

    /**
     * Calculate x + y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function add(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) + y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.add");
        return int128(result);
    }

    /**
     * Calculate x - y.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sub(int128 x, int128 y) internal pure returns (int128) {
        int256 result = int256(x) - y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.sub");
        return int128(result);
    }

    /**
     * Calculate x * y rounding down.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function mul(int128 x, int128 y) internal pure returns (int128) {
        int256 result = (int256(x) * y) >> 64;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.mul");
        return int128(result);
    }

    /**
     * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
     * number and y is signed 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y signed 256-bit integer number
     * @return signed 256-bit integer number
     */
    function muli(int128 x, int256 y) internal pure returns (int256) {
        if (x == MIN_64x64) {
            require(
                y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
                    y <= 0x1000000000000000000000000000000000000000000000000,
                "ABDK.muli-1"
            );
            return -y << 63;
        } else {
            bool negativeResult = false;
            if (x < 0) {
                x = -x;
                negativeResult = true;
            }
            if (y < 0) {
                y = -y;
                // We rely on overflow behavior here
                negativeResult = !negativeResult;
            }
            uint256 absoluteResult = mulu(x, uint256(y));
            if (negativeResult) {
                require(
                    absoluteResult <=
                        0x8000000000000000000000000000000000000000000000000000000000000000,
                    "ABDK.muli-2"
                );
                return -int256(absoluteResult);
                // We rely on overflow behavior here
            } else {
                require(
                    absoluteResult <=
                        0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
                    "ABDK.muli-3"
                );
                return int256(absoluteResult);
            }
        }
    }

    /**
     * Calculate x * y rounding down, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64 fixed point number
     * @param y unsigned 256-bit integer number
     * @return unsigned 256-bit integer number
     */
    function mulu(int128 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) return 0;

        require(x >= 0, "ABDK.mulu-1");

        uint256 lo = (uint256(int256(x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
        uint256 hi = uint256(int256(x)) * (y >> 128);

        require(hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.mulu-2");
        hi <<= 64;

        require(
            hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo,
            "ABDK.mulu-3"
        );
        return hi + lo;
    }

    /**
     * Calculate x / y rounding towards zero.  Revert on overflow or when y is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function div(int128 x, int128 y) internal pure returns (int128) {
        require(y != 0, "ABDK.div-1");
        int256 result = (int256(x) << 64) / y;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.div-2");
        return int128(result);
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are signed 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x signed 256-bit integer number
     * @param y signed 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divi(int256 x, int256 y) internal pure returns (int128) {
        require(y != 0, "ABDK.divi-1");

        bool negativeResult = false;
        if (x < 0) {
            x = -x;
            // We rely on overflow behavior here
            negativeResult = true;
        }
        if (y < 0) {
            y = -y;
            // We rely on overflow behavior here
            negativeResult = !negativeResult;
        }
        uint128 absoluteResult = divuu(uint256(x), uint256(y));
        if (negativeResult) {
            require(absoluteResult <= 0x80000000000000000000000000000000, "ABDK.divi-2");
            return -int128(absoluteResult);
            // We rely on overflow behavior here
        } else {
            require(absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divi-3");
            return int128(absoluteResult);
            // We rely on overflow behavior here
        }
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return signed 64.64-bit fixed point number
     */
    function divu(uint256 x, uint256 y) internal pure returns (int128) {
        require(y != 0, "ABDK.divu-1");
        uint128 result = divuu(x, y);
        require(result <= uint128(MAX_64x64), "ABDK.divu-2");
        return int128(result);
    }

    /**
     * Calculate -x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function neg(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64, "ABDK.neg");
        return -x;
    }

    /**
     * Calculate |x|.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function abs(int128 x) internal pure returns (int128) {
        require(x != MIN_64x64, "ABDK.abs");
        return x < 0 ? -x : x;
    }

    /**
     * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
     * zero.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function inv(int128 x) internal pure returns (int128) {
        require(x != 0, "ABDK.inv-1");
        int256 result = int256(0x100000000000000000000000000000000) / x;
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.inv-2");
        return int128(result);
    }

    /**
     * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function avg(int128 x, int128 y) internal pure returns (int128) {
        return int128((int256(x) + int256(y)) >> 1);
    }

    /**
     * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
     * Revert on overflow or in case x * y is negative.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function gavg(int128 x, int128 y) internal pure returns (int128) {
        int256 m = int256(x) * int256(y);
        require(m >= 0, "ABDK.gavg-1");
        require(
            m < 0x4000000000000000000000000000000000000000000000000000000000000000,
            "ABDK.gavg-2"
        );
        return int128(sqrtu(uint256(m)));
    }

    /**
     * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
     * and y is unsigned 256-bit integer number.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @param y uint256 value
     * @return signed 64.64-bit fixed point number
     */
    function pow(int128 x, uint256 y) internal pure returns (int128) {
        bool negative = x < 0 && y & 1 == 1;

        uint256 absX = uint128(x < 0 ? -x : x);
        uint256 absResult;
        absResult = 0x100000000000000000000000000000000;

        if (absX <= 0x10000000000000000) {
            absX <<= 63;
            while (y != 0) {
                if (y & 0x1 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x2 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x4 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                if (y & 0x8 != 0) {
                    absResult = (absResult * absX) >> 127;
                }
                absX = (absX * absX) >> 127;

                y >>= 4;
            }

            absResult >>= 64;
        } else {
            uint256 absXShift = 63;
            if (absX < 0x1000000000000000000000000) {
                absX <<= 32;
                absXShift -= 32;
            }
            if (absX < 0x10000000000000000000000000000) {
                absX <<= 16;
                absXShift -= 16;
            }
            if (absX < 0x1000000000000000000000000000000) {
                absX <<= 8;
                absXShift -= 8;
            }
            if (absX < 0x10000000000000000000000000000000) {
                absX <<= 4;
                absXShift -= 4;
            }
            if (absX < 0x40000000000000000000000000000000) {
                absX <<= 2;
                absXShift -= 2;
            }
            if (absX < 0x80000000000000000000000000000000) {
                absX <<= 1;
                absXShift -= 1;
            }

            uint256 resultShift;
            while (y != 0) {
                require(absXShift < 64, "ABDK.pow-1");

                if (y & 0x1 != 0) {
                    absResult = (absResult * absX) >> 127;
                    resultShift += absXShift;
                    if (absResult > 0x100000000000000000000000000000000) {
                        absResult >>= 1;
                        resultShift += 1;
                    }
                }
                absX = (absX * absX) >> 127;
                absXShift <<= 1;
                if (absX >= 0x100000000000000000000000000000000) {
                    absX >>= 1;
                    absXShift += 1;
                }

                y >>= 1;
            }

            require(resultShift < 64, "ABDK.pow-2");
            absResult >>= 64 - resultShift;
        }
        int256 result = negative ? -int256(absResult) : int256(absResult);
        require(result >= MIN_64x64 && result <= MAX_64x64, "ABDK.pow-3");
        return int128(result);
    }

    /**
     * Calculate sqrt (x) rounding down.  Revert if x < 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function sqrt(int128 x) internal pure returns (int128) {
        require(x >= 0, "ABDK.sqrt");
        return int128(sqrtu(uint256(int256(x)) << 64));
    }

    /**
     * Calculate binary logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function log_2(int128 x) internal pure returns (int128) {
        require(x > 0, "ABDK.log_2");

        int256 msb;
        int256 xc = x;
        if (xc >= 0x10000000000000000) {
            xc >>= 64;
            msb += 64;
        }
        if (xc >= 0x100000000) {
            xc >>= 32;
            msb += 32;
        }
        if (xc >= 0x10000) {
            xc >>= 16;
            msb += 16;
        }
        if (xc >= 0x100) {
            xc >>= 8;
            msb += 8;
        }
        if (xc >= 0x10) {
            xc >>= 4;
            msb += 4;
        }
        if (xc >= 0x4) {
            xc >>= 2;
            msb += 2;
        }
        if (xc >= 0x2) msb += 1;
        // No need to shift xc anymore

        int256 result = (msb - 64) << 64;
        uint256 ux = uint256(int256(x)) << uint256(127 - msb);
        for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
            ux *= ux;
            uint256 b = ux >> 255;
            ux >>= 127 + b;
            result += bit * int256(b);
        }

        return int128(result);
    }

    /**
     * Calculate natural logarithm of x.  Revert if x <= 0.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function ln(int128 x) internal pure returns (int128) {
        unchecked {
            require(x > 0, "ABDK.ln");

            return
                int128(
                    int256((uint256(int256(log_2(x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128)
                );
        }
    }

    /**
     * Calculate binary exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp_2(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000, "ABDK.exp_2-1");
        // Overflow

        if (x < -0x400000000000000000) return 0;
        // Underflow

        uint256 result = 0x80000000000000000000000000000000;

        if (x & 0x8000000000000000 > 0)
            result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128;
        if (x & 0x4000000000000000 > 0)
            result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDEC) >> 128;
        if (x & 0x2000000000000000 > 0)
            result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A791F) >> 128;
        if (x & 0x1000000000000000 > 0)
            result = (result * 0x10B5586CF9890F6298B92B71842A98363) >> 128;
        if (x & 0x800000000000000 > 0)
            result = (result * 0x1059B0D31585743AE7C548EB68CA417FD) >> 128;
        if (x & 0x400000000000000 > 0)
            result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8) >> 128;
        if (x & 0x200000000000000 > 0)
            result = (result * 0x10163DA9FB33356D84A66AE336DCDFA3F) >> 128;
        if (x & 0x100000000000000 > 0)
            result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9543) >> 128;
        if (x & 0x80000000000000 > 0)
            result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679B) >> 128;
        if (x & 0x40000000000000 > 0)
            result = (result * 0x1002C605E2E8CEC506D21BFC89A23A00F) >> 128;
        if (x & 0x20000000000000 > 0)
            result = (result * 0x100162F3904051FA128BCA9C55C31E5DF) >> 128;
        if (x & 0x10000000000000 > 0)
            result = (result * 0x1000B175EFFDC76BA38E31671CA939725) >> 128;
        if (x & 0x8000000000000 > 0)
            result = (result * 0x100058BA01FB9F96D6CACD4B180917C3D) >> 128;
        if (x & 0x4000000000000 > 0)
            result = (result * 0x10002C5CC37DA9491D0985C348C68E7B3) >> 128;
        if (x & 0x2000000000000 > 0)
            result = (result * 0x1000162E525EE054754457D5995292026) >> 128;
        if (x & 0x1000000000000 > 0)
            result = (result * 0x10000B17255775C040618BF4A4ADE83FC) >> 128;
        if (x & 0x800000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB) >> 128;
        if (x & 0x400000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9) >> 128;
        if (x & 0x200000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128;
        if (x & 0x100000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128;
        if (x & 0x80000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1628) >> 128;
        if (x & 0x40000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2B) >> 128;
        if (x & 0x20000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A5) >> 128;
        if (x & 0x10000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFE) >> 128;
        if (x & 0x8000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2EF) >> 128;
        if (x & 0x4000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737A) >> 128;
        if (x & 0x2000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F06) >> 128;
        if (x & 0x1000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44F9) >> 128;
        if (x & 0x800000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC823) >> 128;
        if (x & 0x400000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE50) >> 128;
        if (x & 0x200000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFCF) >> 128;
        if (x & 0x100000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128;
        if (x & 0x80000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AD) >> 128;
        if (x & 0x40000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CC) >> 128;
        if (x & 0x20000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128;
        if (x & 0x10000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AE) >> 128;
        if (x & 0x8000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCE) >> 128;
        if (x & 0x4000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0D) >> 128;
        if (x & 0x2000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128;
        if (x & 0x1000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94C) >> 128;
        if (x & 0x800000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33D) >> 128;
        if (x & 0x400000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26945) >> 128;
        if (x & 0x200000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388C) >> 128;
        if (x & 0x100000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D40) >> 128;
        if (x & 0x80000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDE) >> 128;
        if (x & 0x40000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77E) >> 128;
        if (x & 0x20000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C2) >> 128;
        if (x & 0x10000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E1) >> 128;
        if (x & 0x8000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F0) >> 128;
        if (x & 0x4000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA38) >> 128;
        if (x & 0x2000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128;
        if (x & 0x1000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128;
        if (x & 0x800 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128;
        if (x & 0x400 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128;
        if (x & 0x200 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D91) >> 128;
        if (x & 0x100 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128;
        if (x & 0x80 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE544) >> 128;
        if (x & 0x40 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128;
        if (x & 0x20 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128;
        if (x & 0x10 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128;
        if (x & 0x8 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6D) >> 128;
        if (x & 0x4 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B2) >> 128;
        if (x & 0x2 > 0) result = (result * 0x1000000000000000162E42FEFA39EF358) >> 128;
        if (x & 0x1 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AB) >> 128;

        result >>= uint256(int256(63 - (x >> 64)));
        require(result <= uint256(int256(MAX_64x64)), "ABDK.exp_2-2");

        return int128(int256(result));
    }

    /**
     * Calculate natural exponent of x.  Revert on overflow.
     *
     * @param x signed 64.64-bit fixed point number
     * @return signed 64.64-bit fixed point number
     */
    function exp(int128 x) internal pure returns (int128) {
        require(x < 0x400000000000000000, "ABDK.exp");
        // Overflow

        if (x < -0x400000000000000000) return 0;
        // Underflow

        return exp_2(int128((int256(x) * 0x171547652B82FE1777D0FFDA0D23A7D12) >> 128));
    }

    /**
     * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
     * integer numbers.  Revert on overflow or when y is zero.
     *
     * @param x unsigned 256-bit integer number
     * @param y unsigned 256-bit integer number
     * @return unsigned 64.64-bit fixed point number
     */
    function divuu(uint256 x, uint256 y) private pure returns (uint128) {
        require(y != 0, "ABDK.divuu-1");

        uint256 result;

        if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) result = (x << 64) / y;
        else {
            uint256 msb = 192;
            uint256 xc = x >> 192;
            if (xc >= 0x100000000) {
                xc >>= 32;
                msb += 32;
            }
            if (xc >= 0x10000) {
                xc >>= 16;
                msb += 16;
            }
            if (xc >= 0x100) {
                xc >>= 8;
                msb += 8;
            }
            if (xc >= 0x10) {
                xc >>= 4;
                msb += 4;
            }
            if (xc >= 0x4) {
                xc >>= 2;
                msb += 2;
            }
            if (xc >= 0x2) msb += 1;
            // No need to shift xc anymore

            result = (x << (255 - msb)) / (((y - 1) >> (msb - 191)) + 1);
            require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-2");

            uint256 hi = result * (y >> 128);
            uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

            uint256 xh = x >> 192;
            uint256 xl = x << 64;

            if (xl < lo) xh -= 1;
            xl -= lo;
            // We rely on overflow behavior here
            lo = hi << 128;
            if (xl < lo) xh -= 1;
            xl -= lo;
            // We rely on overflow behavior here

            assert(xh == hi >> 128);

            result += xl / y;
        }

        require(result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "ABDK.divuu-3");
        return uint128(result);
    }

    /**
     * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
     * number.
     *
     * @param x unsigned 256-bit integer number
     * @return unsigned 128-bit integer number
     */
    function sqrtu(uint256 x) private pure returns (uint128) {
        if (x == 0) return 0;
        else {
            uint256 xx = x;
            uint256 r = 1;
            if (xx >= 0x100000000000000000000000000000000) {
                xx >>= 128;
                r <<= 64;
            }
            if (xx >= 0x10000000000000000) {
                xx >>= 64;
                r <<= 32;
            }
            if (xx >= 0x100000000) {
                xx >>= 32;
                r <<= 16;
            }
            if (xx >= 0x10000) {
                xx >>= 16;
                r <<= 8;
            }
            if (xx >= 0x100) {
                xx >>= 8;
                r <<= 4;
            }
            if (xx >= 0x10) {
                xx >>= 4;
                r <<= 2;
            }
            if (xx >= 0x8) {
                r <<= 1;
            }
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            r = (r + x / r) >> 1;
            // Seven iterations should be enough
            uint256 r1 = x / r;
            return uint128(r < r1 ? r : r1);
        }
    }
}

File 16 of 45 : Bytes32Pagination.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

library Bytes32Pagination {
    function paginate(
        bytes32[] memory hashes,
        uint256 page,
        uint256 limit
    ) internal pure returns (bytes32[] memory result) {
        result = new bytes32[](limit);
        for (uint256 i = 0; i < limit; i++) {
            if (page * limit + i < hashes.length) {
                result[i] = hashes[page * limit + i];
            } else {
                break;
            }
        }
    }
}

File 17 of 45 : ConverterDec18.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./ABDKMath64x64.sol";

library ConverterDec18 {
    using ABDKMath64x64 for int128;
    /*
     * Minimum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

    /*
     * Maximum value signed 64.64-bit fixed point number may have.
     */
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    int256 private constant DECIMALS = 10**18;

    int128 private constant ONE_64x64 = 0x010000000000000000;

    int128 public constant HALF_TBPS = 92233720368548; //1e-5 * 0.5 * 2**64
    int128 public constant HALF_BPS = 922337203685478; //1e-4 * 0.5 * 2**64

    // convert tenth of basis point to dec 18:
    uint256 public constant TBPSTODEC18 = 0x9184e72a000; // hex(10^18 * 10^-5)=(10^13)
    // convert tenth of basis point to ABDK 64x64:
    int128 public constant TBPSTOABDK = 0xa7c5ac471b48; // hex(2^64 * 10^-5)
      // convert basis point to ABDK 64x64:
    int128 public constant BPSTOABDK = 0x68db8bac710cb; // hex(2^64 * 10^-4)
    // convert two-digit integer reprentation to ABDK
    int128 public constant TDRTOABDK = 0x28f5c28f5c28f5c; // hex(2^64 * 10^-2)

    function tbpsToDec18(uint16 Vtbps) internal pure returns (uint256) {
        return TBPSTODEC18 * uint256(Vtbps);
    }

    function tbpsToABDK(uint16 Vtbps) internal pure returns (int128) {
        return int128(uint128(TBPSTOABDK) * uint128(Vtbps));
    }

     function bpsToABDK(uint16 Vtbps) internal pure returns (int128) {
        return int128(uint128(BPSTOABDK) * uint128(Vtbps));
    }

    function TDRToABDK(uint16 V2Tdr) internal pure returns (int128) {
        return int128(uint128(TDRTOABDK) * uint128(V2Tdr));
    }

    function ABDKToTbps(int128 Vabdk) internal pure returns (uint16) {
        // add 0.5 * 1e-5 to ensure correct rounding to tenth of bps
        return uint16(uint128(Vabdk.add(HALF_TBPS) / TBPSTOABDK));
    }

    function ABDKToBps(int128 Vabdk) internal pure returns (uint16) {
        // add 0.5 * 1e-4 to ensure correct rounding to tenth of bps
        return uint16(uint128(Vabdk.add(HALF_BPS) / BPSTOABDK));
    }

    function fromDec18(int256 x) internal pure returns (int128) {
        int256 result = (x * ONE_64x64) / DECIMALS;
        require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range");
        return int128(result);
    }

    function toDec18(int128 x) internal pure returns (int256) {
        return (int256(x) * DECIMALS) / ONE_64x64;
    }

    function toUDec18(int128 x) internal pure returns (uint256) {
        require(x >= 0, "negative value");
        return uint256(toDec18(x));
    }

    function toUDecN(int128 x, uint8 decimals) internal pure returns (uint256) {
        require(x >= 0, "negative value");
        return uint256((int256(x) * int256(10**decimals)) / ONE_64x64);
    }

    function fromDecN(int256 x, uint8 decimals) internal pure returns (int128) {
        int256 result = (x * ONE_64x64) / int256(10**decimals);
        require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range");
        return int128(result);
    }
}

File 18 of 45 : EnumerableBytes4Set.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

/**
 * @title Library for managing loan sets.
 *
 * @notice 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.
 *
 * Include with `using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set;`.
 * */
library EnumerableBytes4Set {
    struct Bytes4Set {
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes4 => uint256) index;
        bytes4[] values;
    }

    /**
     * @notice Add a value to a set. O(1).
     *
     * @param set The set of values.
     * @param value The new value to add.
     *
     * @return False if the value was already in the set.
     */
    function addBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {
        if (!contains(set, value)) {
            set.values.push(value);
            set.index[value] = set.values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @notice Remove a value from a set. O(1).
     *
     * @param set The set of values.
     * @param value The value to remove.
     *
     * @return False if the value was not present in the set.
     */
    function removeBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {
        if (contains(set, value)) {
            uint256 toDeleteIndex = set.index[value] - 1;
            uint256 lastIndex = set.values.length - 1;

            /// If the element we're deleting is the last one,
            /// we can just remove it without doing a swap.
            if (lastIndex != toDeleteIndex) {
                bytes4 lastValue = set.values[lastIndex];

                /// Move the last value to the index where the deleted value is.
                set.values[toDeleteIndex] = lastValue;

                /// Update the index for the moved value.
                set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based
            }

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

            /// Delete the old entry for the moved value.
            set.values.pop();

            return true;
        } else {
            return false;
        }
    }

    /**
     * @notice Find out whether a value exists in the set.
     *
     * @param set The set of values.
     * @param value The value to find.
     *
     * @return True if the value is in the set. O(1).
     */
    function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) {
        return set.index[value] != 0;
    }

    /**
     * @notice Get all set values.
     *
     * @param set The set of values.
     * @param start The offset of the returning set.
     * @param count The limit of number of values to return.
     *
     * @return output An array with all values in the set. O(N).
     *
     * @dev 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.
     *
     * WARNING: This function may run out of gas on large sets: use {length} and
     * {get} instead in these cases.
     */
    function enumerate(
        Bytes4Set storage set,
        uint256 start,
        uint256 count
    ) internal view returns (bytes4[] memory output) {
        uint256 end = start + count;
        require(end >= start, "addition overflow");
        end = set.values.length < end ? set.values.length : end;
        if (end == 0 || start >= end) {
            return output;
        }

        output = new bytes4[](end - start);
        for (uint256 i; i < end - start; i++) {
            output[i] = set.values[i + start];
        }
        return output;
    }

    /**
     * @notice Get the legth of the set.
     *
     * @param set The set of values.
     *
     * @return the number of elements on the set. O(1).
     */
    function length(Bytes4Set storage set) internal view returns (uint256) {
        return set.values.length;
    }

    /**
     * @notice Get an item from the set by its index.
     *
     * @dev 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}.
     *
     * @param set The set of values.
     * @param index The index of the value to return.
     *
     * @return the element stored at position `index` in the set. O(1).
     */
    function get(Bytes4Set storage set, uint256 index) internal view returns (bytes4) {
        return set.values[index];
    }
}

File 19 of 45 : EnumerableSetUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.21;

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

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

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

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

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

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

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

            bytes32 lastvalue = set._values[lastIndex];

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

    function enumerate(
        AddressSet storage set,
        uint256 start,
        uint256 count
    ) internal view returns (address[] memory output) {
        uint256 end = start + count;
        require(end >= start, "addition overflow");
        uint256 len = length(set);
        end = len < end ? len : end;
        if (end == 0 || start >= end) {
            return output;
        }

        output = new address[](end - start);
        for (uint256 i; i < end - start; i++) {
            output[i] = at(set, i + start);
        }
        return output;
    }

    function enumerateAll(AddressSet storage set) internal view returns (address[] memory output) {
        return enumerate(set, 0, length(set));
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

File 20 of 45 : OrderFlags.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

library OrderFlags {
    uint32 internal constant MASK_CLOSE_ONLY = 0x80000000;
    uint32 internal constant MASK_MARKET_ORDER = 0x40000000;
    uint32 internal constant MASK_STOP_ORDER = 0x20000000;
    uint32 internal constant MASK_FILL_OR_KILL = 0x10000000;
    uint32 internal constant MASK_KEEP_POS_LEVERAGE = 0x08000000;
    uint32 internal constant MASK_LIMIT_ORDER = 0x04000000;

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

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

    /**
     * @dev Check if the flags contain fill-or-kill flag
     * @param flags The flags
     * @return bool True if the flags contain fill-or-kill flag
     */
    function isFillOrKill(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_FILL_OR_KILL) > 0;
    }

    /**
     * @dev We keep the position leverage for a closing position, if we have
     * an order with the flag MASK_KEEP_POS_LEVERAGE, or if we have
     * a limit or stop order.
     * @param flags The flags
     * @return bool True if we should keep the position leverage on close
     */
    function keepPositionLeverageOnClose(uint32 flags) internal pure returns (bool) {
        return (flags & (MASK_KEEP_POS_LEVERAGE | MASK_STOP_ORDER | MASK_LIMIT_ORDER)) > 0;
    }

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

    /**
     * @dev Check if the flags contain limit-order flag
     * @param flags The flags
     * @return bool True if the flags contain limit-order flag
     */
    function isLimitOrder(uint32 flags) internal pure returns (bool) {
        return (flags & MASK_LIMIT_ORDER) > 0;
    }
}

File 21 of 45 : PerpStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

import "../../interface/IShareTokenFactory.sol";
import "../../libraries/ABDKMath64x64.sol";
import "./../functions/AMMPerpLogic.sol";
import "../../libraries/EnumerableSetUpgradeable.sol";
import "../../libraries/EnumerableBytes4Set.sol";
import "../../governance/Maintainable.sol";

/* solhint-disable max-states-count */
contract PerpStorage is Maintainable, Pausable, ReentrancyGuard {
    using ABDKMath64x64 for int128;
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set; // enumerable map of bytes4 or addresses
    /**
     * @notice  Perpetual state:
     *          - INVALID:      Uninitialized or not non-existent perpetual.
     *          - INITIALIZING: Only when LiquidityPoolData.isRunning == false. Traders cannot perform operations.
     *          - NORMAL:       Full functional state. Traders are able to perform all operations.
     *          - EMERGENCY:    Perpetual is unsafe and the perpetual needs to be settled.
     *          - SETTLE:       Perpetual ready to be settled
     *          - CLEARED:      All margin accounts are cleared. Traders can withdraw remaining margin balance.
     */
    enum PerpetualState {
        INVALID,
        INITIALIZING,
        NORMAL,
        EMERGENCY,
        SETTLE,
        CLEARED
    }

    // margin and liquidity pool are held in 'collateral currency' which can be either of
    // quote currency, base currency, or quanto currency
    // solhint-disable-next-line const-name-snakecase
    int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64
    int128 internal constant FUNDING_INTERVAL_SEC = 0x70800000000000000000; //3600 * 8 * 0x10000000000000000 = 8h in seconds scaled by 2^64 for ABDKMath64x64
    int128 internal constant MIN_NUM_LOTS_PER_POSITION = 0x0a0000000000000000; // 10, minimal position size in number of lots
    uint8 internal constant MASK_ORDER_CANCELLED = 0x1;
    uint8 internal constant MASK_ORDER_EXECUTED = 0x2;
    // at target, 1% of missing amount is transferred
    // at every rebalance
    uint8 internal iPoolCount;
    // delay required for trades to mitigate oracle front-running in seconds
    uint8 internal iTradeDelaySec;
    address internal ammPerpLogic;

    IShareTokenFactory internal shareTokenFactory;

    //pool id (incremental index, starts from 1) => pool data
    mapping(uint8 => LiquidityPoolData) internal liquidityPools;

    //perpetual id  => pool id
    mapping(uint24 => uint8) internal perpetualPoolIds;

    address internal orderBookFactory;

    /**
     * @notice  Data structure to store oracle price data.
     */
    struct PriceTimeData {
        int128 fPrice;
        uint64 time;
    }

    /**
     * @notice  Data structure to store user margin information.
     */
    struct MarginAccount {
        int128 fLockedInValueQC; // unrealized value locked-in when trade occurs
        int128 fCashCC; // cash in collateral currency (base, quote, or quanto)
        int128 fPositionBC; // position in base currency (e.g., 1 BTC for BTCUSD)
        int128 fUnitAccumulatedFundingStart; // accumulated funding rate
    }

    /**
     * @notice  Store information for a given perpetual market.
     */
    struct PerpetualData {
        // ------ 0
        uint8 poolId;
        uint24 id;
        int32 fInitialMarginRate; //parameter: initial margin
        int32 fSigma2; // parameter: volatility of base-quote pair
        uint32 iLastFundingTime; //timestamp since last funding rate payment
        int32 fDFCoverNRate; // parameter: cover-n rule for default fund. E.g., fDFCoverNRate=0.05 -> we try to cover 5% of active accounts with default fund
        int32 fMaintenanceMarginRate; // parameter: maintenance margin
        PerpetualState state; // Perpetual AMM state
        AMMPerpLogic.CollateralCurrency eCollateralCurrency; //parameter: in what currency is the collateral held?
        // ------ 1
        bytes4 S2BaseCCY; //base currency of S2
        bytes4 S2QuoteCCY; //quote currency of S2
        uint16 incentiveSpreadTbps; //parameter: maximum spread added to the PD
        uint16 minimalSpreadBps; //parameter: minimal spread between long and short perpetual price, in basis points
        bytes4 S3BaseCCY; //base currency of S3
        bytes4 S3QuoteCCY; //quote currency of S3
        int32 fSigma3; // parameter: volatility of quanto-quote pair
        int32 fRho23; // parameter: correlation of quanto/base returns
        uint16 liquidationPenaltyRateTbps; //parameter: penalty if AMM closes the position and not the trader
        //------- 2
        PriceTimeData currentMarkPremiumRate; //relative diff to index price EMA, used for markprice.
        //------- 3
        int128 premiumRatesEMA; // EMA of premium rate
        int128 fUnitAccumulatedFunding; //accumulated funding in collateral currency
        //------- 4
        int128 fOpenInterest; //open interest is the larger of the amount of long and short positions in base currency
        int128 fTargetAMMFundSize; //target liquidity pool weight to allocate to the AMM
        //------- 5
        int128 fCurrentTraderExposureEMA; // trade amounts (storing absolute value)
        int128 fCurrentFundingRate; // current instantaneous funding rate
        //------- 6
        int128 fLotSizeBC; //parameter: minimal trade unit (in base currency) to avoid dust positions
        int128 fReferralRebateCC; //parameter: referral rebate in collateral currency
        //------- 7
        int128 fTargetDFSize; // target default fund size
        int128 fkStar; // signed trade size that minimizes the AMM risk
        //------- 8
        int128 fAMMTargetDD; // parameter: target distance to default (=inverse of default probability), or for prediction markets the maturity ts
        int128 perpFlags; // flags for the perpetual
        //------- 9
        int128 fMinimalTraderExposureEMA; // parameter: minimal value for fCurrentTraderExposureEMA that we don't want to undershoot
        int128 fMinimalAMMExposureEMA; // parameter: minimal abs value for fCurrentAMMExposureEMA that we don't want to undershoot
        //------- 10
        int128 fSettlementS3PriceData; //quanto index
        int128 fSettlementS2PriceData; //base-quote pair. Used as last price in normal state.
        //------- 11
        int128 fParams; //used as total margin balance for for settlement (in collateral currency), otherwise slippage params
        int32 fMarkPriceEMALambda; // parameter: Lambda parameter for EMA used in mark-price for funding rates
        int32 fFundingRateClamp; // parameter: funding rate clamp between which we charge 1bps
        int32 fMaximalTradeSizeBumpUp; // parameter: >1, users can create a maximal position of size fMaximalTradeSizeBumpUp*fCurrentAMMExposureEMA
        uint32 iLastTargetPoolSizeTime; //timestamp (seconds) since last update of fTargetDFSize and fTargetAMMFundSize
        //------- 12

        //-------
        int128[2] fStressReturnS3; // parameter: negative and positive stress returns for quanto-quote asset
        int128[2] fDFLambda; // parameter: EMA lambda for AMM and trader exposure K,k: EMA*lambda + (1-lambda)*K. 0 regular lambda, 1 if current value exceeds past
        int128[2] fCurrentAMMExposureEMA; // 0: negative aggregated exposure (storing negative value), 1: positive
        int128[2] fStressReturnS2; // parameter: negative and positive stress returns for base-quote asset
        // -----
    }

    address internal oracleFactoryAddress;

    // users
    mapping(uint24 => EnumerableSetUpgradeable.AddressSet) internal activeAccounts; //perpetualId => traderAddressSet
    // accounts
    mapping(uint24 => mapping(address => MarginAccount)) internal marginAccounts;
    // delegates
    mapping(address => address) internal delegates;

    // broker maps: poolId -> brokeraddress-> lots contributed
    // contains non-zero entries for brokers. Brokers pay default fund contributions.
    mapping(uint8 => mapping(address => uint32)) internal brokerMap;

    struct LiquidityPoolData {
        bool isRunning; // state
        uint8 iPerpetualCount; // state
        uint8 id; // parameter: index, starts from 1
        int32 fCeilPnLShare; // parameter: cap on the share of PnL allocated to liquidity providers
        uint8 marginTokenDecimals; // parameter: decimals of margin token, inferred from token contract
        uint16 iTargetPoolSizeUpdateTime; //parameter: timestamp in seconds. How often we update the pool's target size
        address marginTokenAddress; //parameter: address of the margin token
        // -----
        uint64 prevAnchor; // state: keep track of timestamp since last withdrawal was initiated
        int128 fRedemptionRate; // state: used for settlement in case of AMM default
        address shareTokenAddress; // parameter
        // -----
        int128 fPnLparticipantsCashCC; // state: addLiquidity/withdrawLiquidity + profit/loss - rebalance
        int128 fTargetAMMFundSize; // state: target liquidity for all perpetuals in pool (sum of weights)
        // -----
        int128 fDefaultFundCashCC; // state: profit/loss
        int128 fTargetDFSize; // state: target default fund size for all perpetuals in pool
        // -----
        int128 fBrokerCollateralLotSize; // param:how much collateral do brokers deposit when providing "1 lot" (not trading lot)
        uint128 prevTokenAmount; // state
        // -----
        uint128 nextTokenAmount; // state
        uint128 totalSupplyShareToken; // state
        // -----
        int128 fBrokerFundCashCC; // state: amount of cash in broker fund
    }

    address internal treasuryAddress; // address for the protocol treasury

    //pool id => perpetual id list
    mapping(uint8 => uint24[]) internal perpetualIds;

    //pool id => perpetual id => data
    mapping(uint8 => mapping(uint24 => PerpetualData)) internal perpetuals;

    /// @dev flag whether MarginTradeOrder was already executed or cancelled
    mapping(bytes32 => uint8) internal executedOrCancelledOrders;

    //proxy
    mapping(bytes32 => EnumerableBytes4Set.Bytes4Set) internal moduleActiveFuncSignatureList;
    mapping(bytes32 => address) internal moduleNameToAddress;
    mapping(address => bytes32) internal moduleAddressToModuleName;

    // fee structure
    struct VolumeEMA {
        int128 fTradingVolumeEMAusd; //trading volume EMA in usd
        uint64 timestamp; // timestamp of last trade
    }

    uint256[] public traderVolumeTiers; // dec18, regardless of token
    uint256[] public brokerVolumeTiers; // dec18, regardless of token
    uint16[] public traderVolumeFeesTbps;
    uint16[] public brokerVolumeFeesTbps;
    mapping(uint24 => address) public perpBaseToUSDOracle;
    mapping(uint24 => int128) public perpToLastBaseToUSD;
    mapping(uint8 => mapping(address => VolumeEMA)) public traderVolumeEMA;
    mapping(uint8 => mapping(address => VolumeEMA)) public brokerVolumeEMA;
    uint64 public lastBaseToUSDUpdateTs;

    // liquidity withdrawals
    struct WithdrawRequest {
        address lp;
        uint256 shareTokens;
        uint64 withdrawTimestamp;
    }

    mapping(address => mapping(uint8 => WithdrawRequest)) internal lpWithdrawMap;

    // users who initiated withdrawals are registered here
    mapping(uint8 => EnumerableSetUpgradeable.AddressSet) internal activeWithdrawals; //poolId => lpAddressSet

    mapping(uint8 => bool) public liquidityProvisionIsPaused;
}
/* solhint-enable max-states-count */

File 22 of 45 : AMMPerpLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../../libraries/ABDKMath64x64.sol";
import "../../libraries/ConverterDec18.sol";
import "../../perpetual/interfaces/IAMMPerpLogic.sol";

contract AMMPerpLogic is IAMMPerpLogic {
    using ABDKMath64x64 for int128;
    /* solhint-disable const-name-snakecase */
    int128 internal constant ONE_64x64 = 0x10000000000000000; // 2^64
    int128 internal constant TWO_64x64 = 0x20000000000000000; // 2*2^64
    int128 internal constant FOUR_64x64 = 0x40000000000000000; //4*2^64
    int128 internal constant HALF_64x64 = 0x8000000000000000; //0.5*2^64
    int128 internal constant TWENTY_64x64 = 0x140000000000000000; //20*2^64
    int128 private constant CDF_CONST_0 = 0x023a6ce358298c;
    int128 private constant CDF_CONST_1 = -0x216c61522a6f3f;
    int128 private constant CDF_CONST_2 = 0xc9320d9945b6c3;
    int128 private constant CDF_CONST_3 = -0x01bcfd4bf0995aaf;
    int128 private constant CDF_CONST_4 = -0x086de76427c7c501;
    int128 private constant CDF_CONST_5 = 0x749741d084e83004;
    int128 private constant CDF_CONST_6 = 0xcc42299ea1b28805;
    int128 private constant CDF_CONST_7 = 0x0281b263fec4e0a007;
    int128 private constant EXPM1_Q0 = 0x0a26c00000000000000000;
    int128 private constant EXPM1_Q1 = 0x0127500000000000000000;
    int128 private constant EXPM1_P0 = 0x0513600000000000000000;
    int128 private constant EXPM1_P1 = 0x27600000000000000000;
    int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    /* solhint-enable const-name-snakecase */

    enum CollateralCurrency {
        QUOTE,
        BASE,
        QUANTO
    }

    struct AMMVariables {
        // all variables are
        // signed 64.64-bit fixed point number
        int128 fLockedValue1; // L1 in quote currency
        int128 fPoolM1; // M1 in quote currency
        int128 fPoolM2; // M2 in base currency
        int128 fPoolM3; // M3 in quanto currency
        int128 fAMM_K2; // AMM exposure (positive if trader long)
        int128 fCurrentTraderExposureEMA; // current average unsigned trader exposure
    }

    struct MarketVariables {
        int128 fIndexPriceS2; // base index
        int128 fIndexPriceS3; // quanto index
        int128 fSigma2; // standard dev of base currency
        int128 fSigma3; // standard dev of quanto currency
        int128 fRho23; // correlation base/quanto currency
    }

    /**
     *  Calculate the normal CDF value of _fX, i.e.,
     *  k=P(X<=_fX), for X~normal(0,1)
     *  The approximation is of the form
     *  Phi(x) = 1 - phi(x) / (x + exp(p(x))),
     *  where p(x) is a polynomial of degree 6
     *  @param _fX signed 64.64-bit fixed point number
     *  @return fY approximated normal-cdf evaluated at X
     */
    function _normalCDF(int128 _fX) internal pure returns (int128 fY) {
        bool isNegative = _fX < 0;
        if (isNegative) {
            _fX = _fX.neg();
        }
        if (_fX > FOUR_64x64) {
            fY = int128(0);
        } else {
            fY = _fX.mul(CDF_CONST_0).add(CDF_CONST_1);
            fY = _fX.mul(fY).add(CDF_CONST_2);
            fY = _fX.mul(fY).add(CDF_CONST_3);
            fY = _fX.mul(fY).add(CDF_CONST_4);
            fY = _fX.mul(fY).add(CDF_CONST_5).mul(_fX).neg().exp();
            fY = fY.mul(CDF_CONST_6).add(_fX);
            fY = _fX.mul(_fX).mul(HALF_64x64).neg().exp().div(CDF_CONST_7).div(fY);
        }
        if (!isNegative) {
            fY = ONE_64x64.sub(fY);
        }
        return fY;
    }

    /**
     *  Calculate the target size for the default fund
     *
     *  @param _fK2AMM       signed 64.64-bit fixed point number, Conservative negative[0]/positive[1] AMM exposure
     *  @param _fk2Trader    signed 64.64-bit fixed point number, Conservative (absolute) trader exposure
     *  @param _fCoverN      signed 64.64-bit fixed point number, cover-n rule for default fund parameter
     *  @param fStressRet2   signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for base/quote pair
     *  @param fStressRet3   signed 64.64-bit fixed point number, negative[0]/positive[1] stress returns for quanto/quote currency
     *  @param fIndexPrices  signed 64.64-bit fixed point number, spot price for base/quote[0] and quanto/quote[1] pairs
     *  @param _eCCY         enum that specifies in which currency the collateral is held: QUOTE, BASE, QUANTO
     *  @return approximated normal-cdf evaluated at X
     */
    function calculateDefaultFundSize(
        int128[2] memory _fK2AMM,
        int128 _fk2Trader,
        int128 _fCoverN,
        int128[2] memory fStressRet2,
        int128[2] memory fStressRet3,
        int128[2] memory fIndexPrices,
        AMMPerpLogic.CollateralCurrency _eCCY
    ) external pure override returns (int128) {
        require(_fK2AMM[0] < 0, "_fK2AMM[0] must be negative");
        require(_fK2AMM[1] > 0, "_fK2AMM[1] must be positive");
        require(_fk2Trader > 0, "_fk2Trader must be positive");

        int128[2] memory fEll;
        // downward stress scenario
        fEll[0] = (_fK2AMM[0].abs().add(_fk2Trader.mul(_fCoverN))).mul(
            ONE_64x64.sub((fStressRet2[0].exp()))
        );
        // upward stress scenario
        fEll[1] = (_fK2AMM[1].abs().add(_fk2Trader.mul(_fCoverN))).mul(
            (fStressRet2[1].exp().sub(ONE_64x64))
        );
        int128 fIstar;
        if (_eCCY == AMMPerpLogic.CollateralCurrency.BASE) {
            fIstar = fEll[0].div(fStressRet2[0].exp());
            int128 fI2 = fEll[1].div(fStressRet2[1].exp());
            if (fI2 > fIstar) {
                fIstar = fI2;
            }
        } else if (_eCCY == AMMPerpLogic.CollateralCurrency.QUANTO) {
            fIstar = fEll[0].div(fStressRet3[0].exp());
            int128 fI2 = fEll[1].div(fStressRet3[1].exp());
            if (fI2 > fIstar) {
                fIstar = fI2;
            }
            fIstar = fIstar.mul(fIndexPrices[0].div(fIndexPrices[1]));
        } else {
            assert(_eCCY == AMMPerpLogic.CollateralCurrency.QUOTE);
            if (fEll[0] > fEll[1]) {
                fIstar = fEll[0].mul(fIndexPrices[0]);
            } else {
                fIstar = fEll[1].mul(fIndexPrices[0]);
            }
        }
        return fIstar;
    }

    /**
     *  Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when
     *  there is no quanto currency collateral.
     *  We assume r=0 everywhere.
     *  The underlying distribution is log-normal, hence the log below.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param fSigma2 current Market variables (price&params)
     *  @param _fSign signed 64.64-bit fixed point number, sign of denominator of distance to default
     *  @return _fThresh signed 64.64-bit fixed point number, number for which the log is the unnormalized distance to default
     */
    function _calculateRiskNeutralDDNoQuanto(
        int128 fSigma2,
        int128 _fSign,
        int128 _fThresh
    ) internal pure returns (int128) {
        require(_fThresh > 0, "argument to log must be >0");
        int128 _fLogTresh = _fThresh.ln();
        int128 fSigma2_2 = fSigma2.mul(fSigma2);
        int128 fMean = fSigma2_2.div(TWO_64x64).neg();
        int128 fDistanceToDefault = ABDKMath64x64.sub(_fLogTresh, fMean).div(fSigma2);
        // because 1-Phi(x) = Phi(-x) we change the sign if _fSign<0
        // now we would like to get the normal cdf of that beast
        if (_fSign < 0) {
            fDistanceToDefault = fDistanceToDefault.neg();
        }
        return fDistanceToDefault;
    }

    /**
     *  Calculate the standard deviation for the random variable
     *  evolving when quanto currencies are involved.
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _mktVars current Market variables (price&params)
     *  @param _fC3 signed 64.64-bit fixed point number current AMM/Market variables
     *  @param _fC3_2 signed 64.64-bit fixed point number, squared fC3
     *  @return fSigmaZ standard deviation, 64.64-bit fixed point number
     */
    function _calculateStandardDeviationQuanto(
        MarketVariables memory _mktVars,
        int128 _fC3,
        int128 _fC3_2
    ) internal pure returns (int128 fSigmaZ) {
        // fVarA = (exp(sigma2^2) - 1)
        int128 fVarA = _mktVars.fSigma2.mul(_mktVars.fSigma2);

        // fVarB = 2*(exp(sigma2*sigma3*rho) - 1)
        int128 fVarB = _mktVars.fSigma2.mul(_mktVars.fSigma3).mul(_mktVars.fRho23).mul(TWO_64x64);

        // fVarC = exp(sigma3^2) - 1
        int128 fVarC = _mktVars.fSigma3.mul(_mktVars.fSigma3);

        // sigmaZ = fVarA*C^2 + fVarB*C + fVarC
        fSigmaZ = fVarA.mul(_fC3_2).add(fVarB.mul(_fC3)).add(fVarC).sqrt();
    }

    /**
     *  Calculate the risk neutral Distance to Default (Phi(DD)=default probability) when
     *  presence of quanto currency collateral.
     *
     *  We approximate the distribution with a normal distribution
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number
     *  @param _ammVars current AMM/Market variables
     *  @param _mktVars current Market variables (price&params)
     *  @param _fSign 64.64-bit fixed point number, current AMM/Market variables
     *  @return fDistanceToDefault signed 64.64-bit fixed point number
     */
    function _calculateRiskNeutralDDWithQuanto(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fSign,
        int128 _fThresh
    ) internal pure returns (int128 fDistanceToDefault) {
        require(_fSign > 0, "no sign in quanto case");
        // 1) Calculate C3
        int128 fC3 = _mktVars.fIndexPriceS2.mul(_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).div(
            _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3)
        );
        int128 fC3_2 = fC3.mul(fC3);

        // 2) Calculate Variance
        int128 fSigmaZ = _calculateStandardDeviationQuanto(_mktVars, fC3, fC3_2);

        // 3) Calculate mean
        int128 fMean = fC3.add(ONE_64x64);
        // 4) Distance to default
        fDistanceToDefault = _fThresh.sub(fMean).div(fSigmaZ);
    }

    function calculateRiskNeutralPD(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) external view virtual override returns (int128, int128) {
        return _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, _withCDF);
    }

    /**
     *  Calculate the risk neutral default probability (>=0).
     *  Function decides whether pricing with or without quanto CCY is chosen.
     *  We assume r=0 everywhere.
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars         current AMM variables.
     *  @param _mktVars         current Market variables (price&params)
     *  @param _fTradeAmount    Trade amount (can be 0), hence amounts k2 are not already factored in
     *                          that is, function will set K2:=K2+k2, L1:=L1+k2*s2 (k2=_fTradeAmount)
     *  @param _withCDF         bool. If false, the normal-cdf is not evaluated (in case the caller is only
     *                          interested in the distance-to-default, this saves calculations)
     *  @return (default probabilit, distance to default) ; 64.64-bit fixed point numbers
     */
    function _calculateRiskNeutralPD(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) internal pure returns (int128, int128) {
        int128 dL = _fTradeAmount.mul(_mktVars.fIndexPriceS2);
        int128 dK = _fTradeAmount;
        _ammVars.fLockedValue1 = _ammVars.fLockedValue1.add(dL);
        _ammVars.fAMM_K2 = _ammVars.fAMM_K2.add(dK);
        // -L1 - k*s2 - M1
        int128 fNumerator = (_ammVars.fLockedValue1.neg()).sub(_ammVars.fPoolM1);
        // s2*(M2-k2-K2) if no quanto, else M3 * s3
        int128 fDenominator = _ammVars.fPoolM3 == 0
            ? (_ammVars.fPoolM2.sub(_ammVars.fAMM_K2)).mul(_mktVars.fIndexPriceS2)
            : _ammVars.fPoolM3.mul(_mktVars.fIndexPriceS3);
        // handle edge sign cases first
        int128 fThresh;
        if (_ammVars.fPoolM3 == 0) {
            if (fNumerator < 0) {
                if (fDenominator >= 0) {
                    // P( den * exp(x) < 0) = 0
                    return (int128(0), TWENTY_64x64.neg());
                } else {
                    // num < 0 and den < 0, and P(exp(x) > infty) = 0
                    int256 result = (int256(fNumerator) << 64) / fDenominator;
                    if (result > MAX_64x64) {
                        return (int128(0), TWENTY_64x64.neg());
                    }
                    fThresh = int128(result);
                }
            } else if (fNumerator > 0) {
                if (fDenominator <= 0) {
                    // P( exp(x) >= 0) = 1
                    return (int128(ONE_64x64), TWENTY_64x64);
                } else {
                    // num > 0 and den > 0, and P(exp(x) < infty) = 1
                    int256 result = (int256(fNumerator) << 64) / fDenominator;
                    if (result > MAX_64x64) {
                        return (int128(ONE_64x64), TWENTY_64x64);
                    }
                    fThresh = int128(result);
                }
            } else {
                return
                    fDenominator >= 0
                        ? (int128(0), TWENTY_64x64.neg())
                        : (int128(ONE_64x64), TWENTY_64x64);
            }
        } else {
            // denom is O(M3 * S3), div should not overflow
            fThresh = fNumerator.div(fDenominator);
        }
        // if we're here fDenominator !=0 and fThresh did not overflow
        // sign tells us whether we consider norm.cdf(f(threshold)) or 1-norm.cdf(f(threshold))
        // we recycle fDenominator to store the sign since it's no longer used
        fDenominator = fDenominator < 0 ? ONE_64x64.neg() : ONE_64x64;
        int128 dd = _ammVars.fPoolM3 == 0
            ? _calculateRiskNeutralDDNoQuanto(_mktVars.fSigma2, fDenominator, fThresh)
            : _calculateRiskNeutralDDWithQuanto(_ammVars, _mktVars, fDenominator, fThresh);

        int128 q;
        if (_withCDF) {
            q = _normalCDF(dd);
        }
        return (q, dd);
    }

    /**
     *  Calculate additional/non-risk based slippage.
     *  Ensures slippage is bounded away from zero for small trades,
     *  and plateaus for larger-than-average trades, so that price becomes risk based.
     *
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars current AMM variables - we need the current average exposure per trader
     *  @param _fTradeAmount 64.64-bit fixed point number, signed size of trade
     *  @return 64.64-bit fixed point number, a number between minus one and one
     */
    function _calculateBoundedSlippage(
        AMMVariables memory _ammVars,
        int128 _fTradeAmount
    ) internal pure returns (int128) {
        int128 fTradeSizeEMA = _ammVars.fCurrentTraderExposureEMA;
        int128 fSlippageSize = ONE_64x64;
        if (_fTradeAmount.abs() < fTradeSizeEMA) {
            fSlippageSize = fSlippageSize.sub(_fTradeAmount.abs().div(fTradeSizeEMA));
            fSlippageSize = ONE_64x64.sub(fSlippageSize.mul(fSlippageSize));
        }
        return _fTradeAmount > 0 ? fSlippageSize : fSlippageSize.neg();
    }

    /**
     *  Calculate AMM price.
     *
     *  All variables are 64.64-bit fixed point number (or struct thereof)
     *  @param _ammVars current AMM variables.
     *  @param _mktVars current Market variables (price&params)
     *                 Trader amounts k2 must already be factored in
     *                 that is, K2:=K2+k2, L1:=L1+k2*s2
     *  @param _fTradeAmount 64.64-bit fixed point number, signed size of trade
     *  @param _fHBidAskSpread half bid-ask spread, 64.64-bit fixed point number
     *  @return 64.64-bit fixed point number, AMM price
     */
    function calculatePerpetualPrice(
        AMMVariables memory _ammVars,
        MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        int128 _fHBidAskSpread,
        int128 _fIncentiveSpread
    ) external view virtual override returns (int128) {
        // add minimal spread in quote currency
        _fHBidAskSpread = _fTradeAmount > 0 ? _fHBidAskSpread : _fHBidAskSpread.neg();
        if (_fTradeAmount == 0) {
            _fHBidAskSpread = 0;
        }
        // get risk-neutral default probability (always >0)
        {
            int128 fQ;
            int128 dd;
            int128 fkStar = _ammVars.fPoolM2.sub(_ammVars.fAMM_K2);
            (fQ, dd) = _calculateRiskNeutralPD(_ammVars, _mktVars, _fTradeAmount, true);
            if (_ammVars.fPoolM3 != 0) {
                // amend K* (see whitepaper)
                int128 nominator = _mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3));
                int128 denom = _mktVars.fSigma2.mul(_mktVars.fSigma2);
                int128 h = nominator.div(denom).mul(_ammVars.fPoolM3);
                h = h.mul(_mktVars.fIndexPriceS3).div(_mktVars.fIndexPriceS2);
                fkStar = fkStar.add(h);
            }
            // decide on sign of premium
            if (_fTradeAmount < fkStar) {
                fQ = fQ.neg();
            }
            // no rebate if exposure increases
            if (_fTradeAmount > 0 && _ammVars.fAMM_K2 > 0) {
                fQ = fQ > 0 ? fQ : int128(0);
            } else if (_fTradeAmount < 0 && _ammVars.fAMM_K2 < 0) {
                fQ = fQ < 0 ? fQ : int128(0);
            }
            // handle discontinuity at zero
            if (
                _fTradeAmount == 0 &&
                ((fQ < 0 && _ammVars.fAMM_K2 > 0) || (fQ > 0 && _ammVars.fAMM_K2 < 0))
            ) {
                fQ = fQ.div(TWO_64x64);
            }
            _fHBidAskSpread = _fHBidAskSpread.add(fQ);
        }
        // get additional slippage
        if (_fTradeAmount != 0) {
            _fIncentiveSpread = _fIncentiveSpread.mul(
                _calculateBoundedSlippage(_ammVars, _fTradeAmount)
            );
            _fHBidAskSpread = _fHBidAskSpread.add(_fIncentiveSpread);
        }
        // s2*(1 + sign(qp-q)*q + sign(k)*minSpread)
        return _mktVars.fIndexPriceS2.mul(ONE_64x64.add(_fHBidAskSpread));
    }

    /**
     *  Calculate target collateral M1 (Quote Currency), when no M2, M3 is present
     *  The targeted default probability is expressed using the inverse
     *  _fTargetDD = Phi^(-1)(targetPD)
     *  _fK2 in absolute terms must be 'reasonably large'
     *  sigma3, rho23, IndexpriceS3 not relevant.
     *  @param _fK2 signed 64.64-bit fixed point number, !=0, EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number, >0, EWMA of actual L.
     *  @param  _mktVars contains 64.64 values for fIndexPriceS2*, fIndexPriceS3, fSigma2*, fSigma3, fRho23
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M1Star signed 64.64-bit fixed point number, >0
     */
    function getTargetCollateralM1(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure virtual override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 == 0);
        assert(_mktVars.fIndexPriceS3 == 0);
        assert(_mktVars.fRho23 == 0);
        int128 fMu2 = HALF_64x64.neg().mul(_mktVars.fSigma2).mul(_mktVars.fSigma2);
        int128 ddScaled = _fK2 < 0
            ? _mktVars.fSigma2.mul(_fTargetDD)
            : _mktVars.fSigma2.mul(_fTargetDD).neg();
        int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled));
        return _fK2.mul(_mktVars.fIndexPriceS2).mul(A1).sub(_fL1);
    }

    /**
     *  Calculate target collateral *M2* (Base Currency), when no M1, M3 is present
     *  The targeted default probability is expressed using the inverse
     *  _fTargetDD = Phi^(-1)(targetPD)
     *  _fK2 in absolute terms must be 'reasonably large'
     *  sigma3, rho23, IndexpriceS3 not relevant.
     *  @param _fK2 signed 64.64-bit fixed point number, EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number, EWMA of actual L.
     *  @param _mktVars contains 64.64 values for fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M2Star signed 64.64-bit fixed point number
     */
    function getTargetCollateralM2(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure virtual override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 == 0);
        assert(_mktVars.fIndexPriceS3 == 0);
        assert(_mktVars.fRho23 == 0);
        int128 fMu2 = HALF_64x64.mul(_mktVars.fSigma2).mul(_mktVars.fSigma2).neg();
        int128 ddScaled = _fL1 < 0
            ? _mktVars.fSigma2.mul(_fTargetDD)
            : _mktVars.fSigma2.mul(_fTargetDD).neg();
        int128 A1 = ABDKMath64x64.exp(fMu2.add(ddScaled)).mul(_mktVars.fIndexPriceS2);
        return _fK2.sub(_fL1.div(A1));
    }

    /**
     *  Calculate target collateral M3 (Quanto Currency), when no M1, M2 not present
     *  @param _fK2 signed 64.64-bit fixed point number. EWMA of actual K.
     *  @param _fL1 signed 64.64-bit fixed point number.  EWMA of actual L.
     *  @param  _mktVars contains 64.64 values for
     *           fIndexPriceS2, fIndexPriceS3, fSigma2, fSigma3, fRho23 - all required
     *  @param _fTargetDD signed 64.64-bit fixed point number
     *  @return M2Star signed 64.64-bit fixed point number
     */
    function getTargetCollateralM3(
        int128 _fK2,
        int128 _fL1,
        MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure override returns (int128) {
        assert(_fK2 != 0);
        assert(_mktVars.fSigma3 != 0);
        assert(_mktVars.fIndexPriceS3 != 0);
        // we solve the quadratic equation A x^2 + Bx + C = 0
        // B = 2 * [X + Y * target_dd^2 * (exp(rho*sigma2*sigma3) - 1) ]
        // C = X^2  - Y^2 * target_dd^2 * (exp(sigma2^2) - 1)
        // where:
        // X = L1 / S3 - Y and Y = K2 * S2 / S3
        // we re-use L1 for X and K2 for Y to save memory since they don't enter the equations otherwise
        _fK2 = _fK2.mul(_mktVars.fIndexPriceS2).div(_mktVars.fIndexPriceS3); // Y
        _fL1 = _fL1.div(_mktVars.fIndexPriceS3).sub(_fK2); // X
        // we only need the square of the target DD
        _fTargetDD = _fTargetDD.mul(_fTargetDD);
        // and we only need B/2
        int128 fHalfB = _fL1.add(
            _fK2.mul(_fTargetDD.mul(_mktVars.fRho23.mul(_mktVars.fSigma2.mul(_mktVars.fSigma3))))
        );
        int128 fC = _fL1.mul(_fL1).sub(
            _fK2.mul(_fK2).mul(_fTargetDD).mul(_mktVars.fSigma2.mul(_mktVars.fSigma2))
        );
        // A = 1 - (exp(sigma3^2) - 1) * target_dd^2
        int128 fA = ONE_64x64.sub(_mktVars.fSigma3.mul(_mktVars.fSigma3).mul(_fTargetDD));
        // we re-use C to store the discriminant: D = (B/2)^2 - A * C
        fC = fHalfB.mul(fHalfB).sub(fA.mul(fC));
        if (fC < 0) {
            // no solutions -> AMM is in profit, probability is smaller than target regardless of capital
            return int128(0);
        }
        // we want the larger of (-B/2 + sqrt((B/2)^2-A*C)) / A and (-B/2 - sqrt((B/2)^2-A*C)) / A
        // so it depends on the sign of A, or, equivalently, the sign of sqrt(...)/A
        fC = ABDKMath64x64.sqrt(fC).div(fA);
        fHalfB = fHalfB.div(fA);
        return fC > 0 ? fC.sub(fHalfB) : fC.neg().sub(fHalfB);
    }

    /**
     *  Calculate the required deposit for a new position
     *  of size _fPosition+_fTradeAmount and leverage _fTargetLeverage,
     *  having an existing position with balance fBalance0 and size _fPosition.
     *  This is the amount to be added to the margin collateral and can be negative (hence remove).
     *  Fees not factored-in.
     *  @param _fPosition0   signed 64.64-bit fixed point number. Position in base currency
     *  @param _fBalance0   signed 64.64-bit fixed point number. Current balance.
     *  @param _fTradeAmount signed 64.64-bit fixed point number. Trade amt in base currency
     *  @param _fTargetLeverage signed 64.64-bit fixed point number. Desired leverage
     *  @param _fPrice signed 64.64-bit fixed point number. Price for the trade of size _fTradeAmount
     *  @param _fS2Mark signed 64.64-bit fixed point number. Mark-price
     *  @param _fS3 signed 64.64-bit fixed point number. Collateral 2 quote conversion
     *  @return signed 64.64-bit fixed point number. Required cash_cc
     */
    function getDepositAmountForLvgPosition(
        int128 _fPosition0,
        int128 _fBalance0,
        int128 _fTradeAmount,
        int128 _fTargetLeverage,
        int128 _fPrice,
        int128 _fS2Mark,
        int128 _fS3,
        int128 _fS2
    ) external pure override returns (int128) {
        // calculation has to be aligned with _getAvailableMargin and _executeTrade
        // calculation
        // otherwise the calculated deposit might not be enough to declare
        // the margin to be enough
        // aligned with get available margin balance
        int128 fPremiumCash = _fTradeAmount.mul(_fPrice.sub(_fS2));
        int128 fDeltaLockedValue = _fTradeAmount.mul(_fS2);
        int128 fPnL = _fTradeAmount.mul(_fS2Mark);
        // we replace _fTradeAmount * price/S3 by
        // fDeltaLockedValue + fPremiumCash to be in line with
        // _executeTrade
        fPnL = fPnL.sub(fDeltaLockedValue).sub(fPremiumCash);
        int128 fLvgFrac = _fPosition0.add(_fTradeAmount).abs();
        fLvgFrac = fLvgFrac.mul(_fS2Mark).div(_fTargetLeverage);
        fPnL = fPnL.sub(fLvgFrac).div(_fS3);
        _fBalance0 = _fBalance0.add(fPnL);
        return _fBalance0.neg();
    }

    function entropy(int128 _p) external pure override returns (int128) {
        return _entropy(_p);
    }

    function _entropy(int128 _p) internal pure returns (int128) {
        //- p * log2(p) - (1-p) * log2(1-p)
        if (_p <= 0) {
            return 0;
        }
        if (_p >= ONE_64x64) {
            return 0;
        }
        if (_p < 184467440737) {
            // approximate to avoid numerical troubles
            // 184467440737=1e-8
            return _p;
        }
        if (_p > 18446743889242110879) {
            // approximate to avoid numerical troubles
            // 18446743889242110879 = 1-1e-8
            return ONE_64x64.sub(_p);
        }
        int128 h = ONE_64x64.sub(_p).mul(ONE_64x64.sub(_p).log_2());
        h = _p.mul(_p.log_2()).add(h);
        return h.neg();
    }

    function first_nonzeronum(uint16 numDec) internal pure returns (uint256) {
        uint256 pos = 0;
        while (numDec > 0) {
            numDec = numDec / 10;
            pos = pos + 1;
        }
        return pos;
    }

    /**
     * Decode uint16-float and convert into ABDK int128 fixed point number
     * Uint16-float is custom.
     * @param num number that encodes a float
     */
    function decodeUint16Float(uint16 num) external pure returns (int128) {
        return _decodeUint16Float(num);
    }

    function _decodeUint16Float(uint16 num) internal pure returns (int128) {
        uint16 sgnNum = num >> 15;
        uint16 sgnE = (num >> 14) & 1;
        uint16 val = (num >> 4) & ((2 ** 10) - 1);
        uint16 exponent = num & ((2 ** 4) - 1);

        //convert val abcde to normalized form a.bcde
        int128 v = int128(uint128(val)) * ONE_64x64;
        uint256 exponent1 = (first_nonzeronum(val) - 1);
        v = v.div(ABDKMath64x64.pow(10 * ONE_64x64, exponent1));

        if (sgnE == 1) {
            v = v.div(ABDKMath64x64.pow(10 * ONE_64x64, uint256(exponent)));
        } else {
            v = v.mul(ABDKMath64x64.pow(10 * ONE_64x64, uint256(exponent)));
        }
        if (sgnNum == 1) {
            v = v.neg();
        }
        return v;
    }

    /**
     * Calculate the price impact for betting market trades
     * @param _amount trade amount (signed base currency)
     * @param _params float encoded parameters for bid side (left 32 bit) and ask side (right 32 bit)
     */
    function priceImpact(int128 _amount, uint64 _params) external pure returns (int128) {
        uint32 params;
        if (_amount > 0) {
            params = uint32(_params & ((2 ** 32) - 1));
        } else {
            params = uint32(_params >> 32);
        }
        int128 a = _decodeUint16Float(uint16(params >> 16));
        int128 m = _decodeUint16Float(uint16(params & ((2 ** 16) - 1)));
        int128 l = a.add(_amount.abs().mul(m));
        if (l<0x200000000000000000) {
            // here if impact is not close to overflow
            return _amount < 0 ? -l.exp() : l.exp();
        }
        // return a very big number
        return _amount < 0 ? -int128(0x40000000000000000000000000000000) : int128(0x40000000000000000000000000000000);
    }

    function _expectedLossImpact(
        int128 _fp, //probability (long)
        int128 _m, //max maint margin rate
        int128 _tradeAmt, //amount being traded
        int128 _mgnRate //margin rate for trade. If zero, set to maintenance margin rate
    ) internal pure returns (int128) {
        //maintMgnRate = (0.4-m)*entropy(p) + m
        int128 maintMgnRate = _entropy(_fp);
        maintMgnRate = ABDKMath64x64.sub(7378697629483820646, _m).mul(maintMgnRate).add(_m);
        if (_mgnRate == 0) {
            _mgnRate = maintMgnRate;
        }
        int128 a;
        int128 b;
        {
            int128 dlm;
            int128 dsm;
            int128 dl;
            int128 ds;
            if (_tradeAmt > 0) {
                dlm = _fp.mul(_tradeAmt);
                dlm = dlm.mul(_mgnRate);
                dl = _tradeAmt;
            } else if (_tradeAmt < 0) {
                dsm = ONE_64x64.sub(_fp);
                dsm = dsm.mul(_tradeAmt).neg().mul(_mgnRate);
                ds = _tradeAmt.neg();
            }
            a = dl.sub(dsm);
            b = ds.sub(dlm);
        }
        int128 el;
        el = a.add(b);
        if (el < 0) {
            return 0;
        }
        _fp = _fp.mul(ONE_64x64.sub(_fp));
        return el.mul(_fp);
    }

    /**
     * Returns $fee/tradeamt
     * @param _fPx price (1+p)
     * @param _fm max maint margin rate
     * @param _fTradeAmt amount being traded
     * @param _fMgnRate margin rate for trade. If zero maintenance margin will be used
     */
    function prdMktsLvgFee(
        int128 _fPx, //price (1+p)
        int128 _fm, //max maint margin rate
        int128 _fTradeAmt, //amount being traded
        int128 _fMgnRate //margin rate for trade
    ) external pure returns (int128) {
        /*
        fee application: tradeAmtCC = pos * s2/s3
                         fee = tradeAmtCC * feeRate
        we need to calculate 'feeRate'. The fee here
        is however the dollar fee, so $fee/s3 is the fee
        charged in USD-collateral currency
        -> $fee/S3 = tradeamt*feeRate/s3 -> we calculate feeRate
        -> feeRate = $fee/tradeamt.
        */
        _fPx = _fPx.sub(ONE_64x64); // convert to probability
        int128 dEl = _expectedLossImpact(_fPx, _fm, _fTradeAmt, _fMgnRate);
        // diff (elAfter.sub(elBefore)) is the fee in dollar terms,
        // now we calculate r*s2
        dEl = dEl.div(_fTradeAmt.abs());
        if (dEl < 18446744073709552) {
            // if smaller than 0.1 cent per contract, set to 0.1 cent
            dEl = 18446744073709552;
        }
        return dEl;
    }
}

File 23 of 45 : PerpetualHashFunctions.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "../interfaces/IPerpetualOrder.sol";

library PerpetualHashFunctions {
    string private constant NAME = "Perpetual Trade Manager";

    //The EIP-712 typehash for the contract's domain.
    bytes32 private constant DOMAIN_TYPEHASH =
        0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866;
    // keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")

    //The EIP-712 typehash for the Order struct used by the contract.
    bytes32 private constant TRADE_ORDER_TYPEHASH =
        0xe5599e89712387846e6878c8cac8abdf5d6051ccbc4a6cfa5efe389720300ec8;
    // keccak256(
    //     "Order(uint24 iPerpetualId,uint16 brokerFeeTbps,address traderAddr,address brokerAddr,int128 fAmount,int128 fLimitPrice,int128 fTriggerPrice,uint32 iDeadline,uint32 flags,uint16 leverageTDR,uint32 executionTimestamp)"
    // )

    bytes32 private constant TRADE_BROKER_TYPEHASH =
        0x1aae56290d242a9c761ca2ef80072ffe2a6171793ad9f88e04426b2acc5e730d;

    // keccak256(
    //     "Order(uint24 iPerpetualId,uint16 brokerFeeTbps,address traderAddr,uint32 iDeadline)"
    // )

    /**
     * @notice Creates the hash for an order
     * @param _order the address of perpetual proxy manager.
     * @param _contract The id of perpetual.
     * @param _createOrder true if order is to be executed, false for cancel-order digest
     * @return hash of order and _createOrder-flag
     * */
    function _getDigest(
        IPerpetualOrder.Order memory _order,
        address _contract,
        bool _createOrder
    ) internal view returns (bytes32) {
        /*
         * The DOMAIN_SEPARATOR is a hash that uniquely identifies a
         * smart contract. It is built from a string denoting it as an
         * EIP712 Domain, the name of the token contract, the version,
         * the chainId in case it changes, and the address that the
         * contract is deployed at.
         */
        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), _getChainId(), _contract)
        );

        // ORDER_TYPEHASH
        bytes32 structHash = _getStructHash(_order);

        bytes32 digest = keccak256(abi.encode(domainSeparator, structHash, _createOrder));

        digest = ECDSA.toEthSignedMessageHash(digest);
        return digest;
    }

    /**
     * @dev Get digest a broker would sign, given an order and perpetual
     * @param _order Order struct
     * @param _contract Address of the perpetual manager
     */
    function _getBrokerDigest(IPerpetualOrder.Order memory _order, address _contract)
        internal
        view
        returns (bytes32)
    {
        bytes32 domainSeparator = keccak256(
            abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), _getChainId(), _contract)
        );
        // ORDER_TYPEHASH
        bytes32 structHash = _getStructBrokerHash(_order);
        bytes32 digest = keccak256(abi.encode(domainSeparator, structHash));
        digest = ECDSA.toEthSignedMessageHash(digest);
        return digest;
    }

    /**
     * @dev Chain Id
     */
    function _getChainId() internal view returns (uint256) {
        uint256 chainId;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            chainId := chainid()
        }
        return chainId;
    }

    /**
     * @notice Creates the hash of the order-struct
     * @dev order.executorAddr is not hashed,
     * because it is to be set by the executor
     * @param _order : order struct
     * @return bytes32 hash of order
     * */
    function _getStructHash(IPerpetualOrder.Order memory _order) internal pure returns (bytes32) {
        bytes32 structHash = keccak256(
            abi.encode(
                TRADE_ORDER_TYPEHASH,
                _order.iPerpetualId,
                _order.brokerFeeTbps, // trader needs to sign for the broker fee
                _order.traderAddr,
                _order.brokerAddr, // trader needs to sign for broker
                _order.fAmount,
                _order.fLimitPrice,
                _order.fTriggerPrice,
                _order.iDeadline,
                _order.flags,
                _order.leverageTDR,
                _order.executionTimestamp
            )
        );
        return structHash;
    }

    /**
     * @dev Hash an order struct, used when creating the digest for a broker to sign.
     * @param _order Order struct
     */
    function _getStructBrokerHash(IPerpetualOrder.Order memory _order)
        internal
        pure
        returns (bytes32)
    {
        bytes32 structHash = keccak256(
            abi.encode(
                TRADE_BROKER_TYPEHASH,
                _order.iPerpetualId,
                _order.brokerFeeTbps,
                _order.traderAddr,
                _order.iDeadline
            )
        );
        return structHash;
    }
}

File 24 of 45 : IAMMPerpLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../functions/AMMPerpLogic.sol";

interface IAMMPerpLogic {
    function calculateDefaultFundSize(
        int128[2] memory _fK2AMM,
        int128 _fk2Trader,
        int128 _fCoverN,
        int128[2] memory fStressRet2,
        int128[2] memory fStressRet3,
        int128[2] memory fIndexPrices,
        AMMPerpLogic.CollateralCurrency _eCCY
    ) external pure returns (int128);

    function calculateRiskNeutralPD(
        AMMPerpLogic.AMMVariables memory _ammVars,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        bool _withCDF
    ) external view returns (int128, int128);

    function calculatePerpetualPrice(
        AMMPerpLogic.AMMVariables memory _ammVars,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTradeAmount,
        int128 _fBidAskSpread,
        int128 _fIncentiveSpread
    ) external view returns (int128);

    function getTargetCollateralM1(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getTargetCollateralM2(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getTargetCollateralM3(
        int128 _fK2,
        int128 _fL1,
        AMMPerpLogic.MarketVariables memory _mktVars,
        int128 _fTargetDD
    ) external pure returns (int128);

    function getDepositAmountForLvgPosition(
        int128 _fPosition0,
        int128 _fBalance0,
        int128 _fTradeAmount,
        int128 _fTargetLeverage,
        int128 _fPrice,
        int128 _fS2Mark,
        int128 _fS3,
        int128 _fS2
    ) external pure returns (int128);

    function entropy(int128 _p) external pure returns (int128);
    function decodeUint16Float(uint16 num) external pure returns (int128);
    function priceImpact(int128 _amount, uint64 _params) external pure returns (int128);
    function prdMktsLvgFee(
        int128 _fPx,
        int128 _fm,
        int128 _fTradeAmt,
        int128 _fMgnRate
    ) external pure returns (int128);
}

File 25 of 45 : IClientOrder.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/**
 * @title Trader/Broker facing order struct
 * @notice this order struct is sent to the limit order book and converted into an IPerpetualOrder
 */
interface IClientOrder {
    struct ClientOrder {
        uint24 iPerpetualId; // unique id of the perpetual
        int128 fLimitPrice; // order will not execute if realized price is above (buy) or below (sell) this price
        uint16 leverageTDR; // leverage, set to 0 if deposit margin and trade separate; format: two-digit integer (e.g., 12.34 -> 1234)
        uint32 executionTimestamp; // the order will not be executed before this timestamp, allows TWAP orders
        uint32 flags; // Order-flags are specified in OrderFlags.sol
        uint32 iDeadline; // order will not be executed after this deadline
        address brokerAddr; // can be empty, address of the broker
        int128 fTriggerPrice; // trigger price for stop-orders|0. Order can be executed if the mark price is below this price (sell order) or above (buy)
        int128 fAmount; // signed amount of base-currency. Will be rounded to lot size
        bytes32 parentChildDigest1; // see notice in LimitOrderBook.sol
        address traderAddr; // address of the trader
        bytes32 parentChildDigest2; // see notice in LimitOrderBook.sol
        uint16 brokerFeeTbps; // broker fee in tenth of a basis point
        bytes brokerSignature; // signature, can be empty if no brokerAddr provided
        address callbackTarget; // address of contract implementing callback function
        //address executorAddr; <- will be set by LimitOrderBook
        //uint64 submittedBlock <- will be set by LimitOrderBook
    }
}

interface ID8XExecutionCallbackReceiver {
    function d8xExecutionCallback(bytes32 orderDigest, bool isExecuted) external;
}

File 26 of 45 : ILibraryEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "./IPerpetualOrder.sol";

/**
 * @notice  The libraryEvents defines events that will be raised from modules (contract/modules).
 * @dev     DO REMEMBER to add new events in modules here.
 */
interface ILibraryEvents {
    // PerpetualModule
    event Clear(uint24 indexed perpetualId, address indexed trader);
    event Settle(uint24 indexed perpetualId, address indexed trader, int256 amount);
    event SettlementComplete(uint24 indexed perpetualId);
    event SetNormalState(uint24 indexed perpetualId);
    event SetEmergencyState(
        uint24 indexed perpetualId,
        int128 fSettlementMarkPremiumRate,
        int128 fSettlementS2Price,
        int128 fSettlementS3Price
    );
    event SettleState(uint24 indexed perpetualId);
    event SetClearedState(uint24 indexed perpetualId);

    // Participation pool
    event LiquidityAdded(
        uint8 indexed poolId,
        address indexed user,
        uint256 tokenAmount,
        uint256 shareAmount
    );
    event LiquidityProvisionPaused(bool pauseOn, uint8 poolId);
    event LiquidityRemoved(
        uint8 indexed poolId,
        address indexed user,
        uint256 tokenAmount,
        uint256 shareAmount
    );
    event LiquidityWithdrawalInitiated(
        uint8 indexed poolId,
        address indexed user,
        uint256 shareAmount
    );

    // setters
    // oracles
    event SetOracles(uint24 indexed perpetualId, bytes4[2] baseQuoteS2, bytes4[2] baseQuoteS3);
    // perp parameters
    event SetPerpetualBaseParameters(uint24 indexed perpetualId, int128[7] baseParams);
    event SetPerpetualRiskParameters(
        uint24 indexed perpetualId,
        int128[5] underlyingRiskParams,
        int128[12] defaultFundRiskParams
    );
    event SetParameter(uint24 indexed perpetualId, string name, int128 value);
    event SetParameterPair(uint24 indexed perpetualId, string name, int128 value1, int128 value2);
    // pool parameters
    event SetPoolParameter(uint8 indexed poolId, string name, int128 value);

    event TransferAddressTo(string name, address oldOBFactory, address newOBFactory); // only governance
    event SetBlockDelay(uint8 delay);

    // fee structure parameters
    event SetBrokerDesignations(uint32[] designations, uint16[] fees);
    event SetBrokerTiers(uint256[] tiers, uint16[] feesTbps);
    event SetTraderTiers(uint256[] tiers, uint16[] feesTbps);
    event SetTraderVolumeTiers(uint256[] tiers, uint16[] feesTbps);
    event SetBrokerVolumeTiers(uint256[] tiers, uint16[] feesTbps);
    event SetUtilityToken(address tokenAddr);

    event BrokerLotsTransferred(
        uint8 indexed poolId,
        address oldOwner,
        address newOwner,
        uint32 numLots
    );
    event BrokerVolumeTransferred(
        uint8 indexed poolId,
        address oldOwner,
        address newOwner,
        int128 fVolume
    );

    // brokers
    event UpdateBrokerAddedCash(uint8 indexed poolId, uint32 iLots, uint32 iNewBrokerLots);

    // TradeModule

    event Trade(
        uint24 indexed perpetualId,
        address indexed trader,
        IPerpetualOrder.Order order,
        bytes32 orderDigest,
        int128 newPositionSizeBC,
        int128 price,
        int128 fFeeCC,
        int128 fPnlCC,
        int128 fB2C
    );

    event UpdateMarginAccount(
        uint24 indexed perpetualId,
        address indexed trader,
        int128 fFundingPaymentCC
    );

    event Liquidate(
        uint24 perpetualId,
        address indexed liquidator,
        address indexed trader,
        int128 amountLiquidatedBC,
        int128 liquidationPrice,
        int128 newPositionSizeBC,
        int128 fFeeCC,
        int128 fPnlCC
    );

    event PerpetualLimitOrderCancelled(uint24 indexed perpetualId, bytes32 indexed orderHash);
    event DistributeFees(
        uint8 indexed poolId,
        uint24 indexed perpetualId,
        address indexed trader,
        int128 protocolFeeCC,
        int128 participationFundFeeCC
    );

    // PerpetualManager/factory
    event RunLiquidityPool(uint8 _liqPoolID);
    event LiquidityPoolCreated(
        uint8 id,
        address marginTokenAddress,
        address shareTokenAddress,
        uint16 iTargetPoolSizeUpdateTime,
        int128 fBrokerCollateralLotSize
    );
    event PerpetualCreated(
        uint8 poolId,
        uint24 id,
        int128[7] baseParams,
        int128[5] underlyingRiskParams,
        int128[12] defaultFundRiskParams,
        uint256 eCollateralCurrency
    );

    // emit tokenAddr==0x0 if the token paid is the aggregated token, otherwise the address of the token
    event TokensDeposited(uint24 indexed perpetualId, address indexed trader, int128 amount);
    event TokensWithdrawn(uint24 indexed perpetualId, address indexed trader, int128 amount);

    event UpdateMarkPrice(
        uint24 indexed perpetualId,
        int128 fMidPricePremium,
        int128 fMarkPricePremium,
        int128 fMarkIndexPrice //either spot or "ema" for prd mkts
    );

    event UpdateFundingRate(uint24 indexed perpetualId, int128 fFundingRate);

    event SetDelegate(address indexed trader, address indexed delegate, uint256 index);
}

File 27 of 45 : IPerpetualBrokerFeeLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "../interfaces/IPerpetualOrder.sol";
import "../../interface/ISpotOracle.sol";

interface IPerpetualBrokerFeeLogic {
    function determineExchangeFee(IPerpetualOrder.Order memory _order)
        external
        view
        returns (uint16);

    function updateVolumeEMAOnNewTrade(
        uint24 _iPerpetualId,
        address _traderAddr,
        address _brokerAddr,
        int128 _tradeAmountBC
    ) external;

    function queryExchangeFee(
        uint8 _poolId,
        address _traderAddr,
        address _brokerAddr
    ) external view returns (uint16);

    function splitProtocolFee(uint16 fee) external pure returns (int128, int128);

    function setFeesForDesignation(uint32[] calldata _designations, uint16[] calldata _fees)
        external;

    function getLastPerpetualBaseToUSDConversion(uint24 _iPerpetualId)
        external
        view
        returns (int128);

    function getFeeForTraderVolume(uint8 _poolId, address _traderAddr)
        external
        view
        returns (uint16);

    function getFeeForBrokerVolume(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint16);

    function setOracleFactoryForPerpetual(uint24 _iPerpetualId, address _oracleAddr) external;

    function setBrokerTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setTraderTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setTraderVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setBrokerVolumeTiers(uint256[] calldata _tiers, uint16[] calldata _feesTbps) external;

    function setUtilityTokenAddr(address tokenAddr) external;

    function getBrokerInducedFee(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint16);

    function getBrokerDesignation(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (uint32);

    function getFeeForBrokerDesignation(uint32 _brokerDesignation) external view returns (uint16);

    function getFeeForBrokerStake(address brokerAddr) external view returns (uint16);

    function getFeeForTraderStake(address traderAddr) external view returns (uint16);

    function getCurrentTraderVolume(uint8 _poolId, address _traderAddr)
        external
        view
        returns (int128);

    function getCurrentBrokerVolume(uint8 _poolId, address _brokerAddr)
        external
        view
        returns (int128);

    function transferBrokerLots(
        uint8 _poolId,
        address _transferToAddr,
        uint32 _lots
    ) external;

    function transferBrokerOwnership(uint8 _poolId, address _transferToAddr) external;

    function setInitialVolumeForFee(
        uint8 _poolId,
        address _brokerAddr,
        uint16 _feeTbps
    ) external;
}

File 28 of 45 : IPerpetualDepositManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualDepositManager {
    function deposit(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fAmount,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable;

    function depositToDefaultFund(uint8 _poolId, int128 _fAmount) external;

    function depositBrokerLots(uint8 _poolId, uint32 _iLots) external;

    function withdrawFromDefaultFund(uint8 _poolId, int128 _fAmount) external;

    function transferEarningsToTreasury(uint8 _poolId, int128 _fAmount) external;
}

File 29 of 45 : IPerpetualFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualFactory {
    function createPerpetual(
        uint8 _iPoolId,
        bytes4[2] calldata _baseQuoteS2,
        bytes4[2] calldata _baseQuoteS3,
        int128[7] calldata _baseParams,
        int128[5] calldata _underlyingRiskParams,
        int128[12] calldata _defaultFundRiskParams,
        uint256 _eCollateralCurrency
    ) external;

    function activatePerpetual(uint24 _perpetualId) external;
}

File 30 of 45 : IPerpetualGetter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../core/PerpStorage.sol";
import "../../interface/IShareTokenFactory.sol";

interface IPerpetualGetter {
    function getAMMPerpLogic() external view returns (address);

    function getShareTokenFactory() external view returns (IShareTokenFactory);

    function getOracleFactory() external view returns (address);

    function getTreasuryAddress() external view returns (address);

    function getOrderBookFactoryAddress() external view returns (address);

    function getOrderBookAddress(uint24 _perpetualId) external view returns (address);

    function isPerpMarketClosed(uint24 _perpetualId) external view returns (bool isClosed);

    function getOracleUpdateTime(uint24 _perpetualId) external view returns (uint256);

    function isDelegate(address _trader, address _delegate) external view returns (bool);
}

File 31 of 45 : IPerpetualInfo.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../core/PerpStorage.sol";

interface IPerpetualInfo {
    struct PerpetualStaticInfo {
        uint24 id;
        address limitOrderBookAddr;
        int32 fInitialMarginRate;
        int32 fMaintenanceMarginRate;
        uint8 perpetualState;
        AMMPerpLogic.CollateralCurrency collCurrencyType;
        bytes4 S2BaseCCY; //base currency of S2
        bytes4 S2QuoteCCY; //quote currency of S2
        bytes4 S3BaseCCY; //base currency of S3
        bytes4 S3QuoteCCY; //quote currency of S3
        int128 fLotSizeBC;
        int128 fReferralRebateCC;
        bytes32[] priceIds;
        bool[] isPyth;
        int128 perpFlags;
    }

    function getPoolCount() external view returns (uint8);

    function getPerpetualId(uint8 _poolId, uint8 _perpetualIndex) external view returns (uint24);

    function getLiquidityPool(
        uint8 _poolId
    ) external view returns (PerpStorage.LiquidityPoolData memory);

    function getLiquidityPools(
        uint8 _poolIdFrom,
        uint8 _poolIdTo
    ) external view returns (PerpStorage.LiquidityPoolData[] memory);

    function getPoolIdByPerpetualId(uint24 _perpetualId) external view returns (uint8);

    function getPerpetual(
        uint24 _perpetualId
    ) external view returns (PerpStorage.PerpetualData memory);

    function getPerpetuals(
        uint24[] calldata perpetualIds
    ) external view returns (PerpStorage.PerpetualData[] memory);

    function queryMidPrices(
        uint24[] calldata _perpetualIds,
        int128[] calldata _idxPriceDataPairs
    ) external view returns (int128[] memory);

    function getMarginAccount(
        uint24 _perpetualId,
        address _traderAddress
    ) external view returns (PerpStorage.MarginAccount memory);

    function getMarginAccounts(
        uint24[] calldata _perpetualIds,
        address _traderAddress
    ) external view returns (PerpStorage.MarginAccount[] memory);

    function isActiveAccount(
        uint24 _perpetualId,
        address _traderAddress
    ) external view returns (bool);

    function getActivePerpAccounts(uint24 _perpetualId) external view returns (address[] memory);

    function getPerpetualCountInPool(uint8 _poolId) external view returns (uint8);

    function getExchangeFeePrdMkts(
        uint24 _perpetualId,
        int128 _fAmount,
        uint16 _leverageTDR,
        address _traderAddr
    ) external view returns (uint16);

    function getAMMState(
        uint24 _perpetualId,
        int128[2] calldata _fIndexPrice
    ) external view returns (int128[15] memory);

    function getTraderState(
        uint24 _perpetualId,
        address _traderAddress,
        int128[2] calldata _fIndexPrice
    ) external view returns (int128[11] memory);

    function getActivePerpAccountsByChunks(
        uint24 _perpetualId,
        uint256 _from,
        uint256 _to
    ) external view returns (address[] memory);

    function countActivePerpAccounts(uint24 _perpetualId) external view returns (uint256);

    function getOraclePrice(bytes4[2] memory _baseQuote) external view returns (int128, uint64);

    function isMarketClosed(
        bytes4 _baseCurrency,
        bytes4 _quoteCurrency
    ) external view returns (bool);

    function getPriceInfo(
        uint24 _perpetualId
    ) external view returns (bytes32[] memory, bool[] memory);

    function getPoolStaticInfo(
        uint8 _poolFromIdx,
        uint8 _poolToIdx
    )
        external
        view
        returns (
            uint24[][] memory,
            address[] memory,
            address[] memory,
            address _oracleFactoryAddress
        );

    function getPerpetualStaticInfo(
        uint24[] calldata perpetualIds
    ) external view returns (PerpetualStaticInfo[] memory);

    function getLiquidatableAccounts(
        uint24 _perpetualId,
        int128[2] calldata _fIndexPrice
    ) external view returns (address[] memory unsafeAccounts);

    function getNextLiquidatableTrader(
        uint24 _perpetualId,
        int128[2] calldata _fIndexPrice
    ) external view returns (address traderAddr);
}

File 32 of 45 : IPerpetualLimitTradeManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./IPerpetualOrder.sol";

interface IPerpetualLimitTradeManager is IPerpetualOrder {
    function tradeViaOrderBook(Order memory _order, address _executor) external returns (bool);

    function executeCancelOrder(uint24 _perpetualId, bytes32 _digest) external;

    function isOrderExecuted(bytes32 digest) external view returns (bool);

    function isOrderCanceled(bytes32 digest) external view returns (bool);
}

File 33 of 45 : IPerpetualLiquidator.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualLiquidator {
    function liquidateByAMM(
        uint24 _perpetualIndex,
        address _liquidatorAddr,
        address _traderAddr,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable returns (int128 liquidatedAmount);
}

File 34 of 45 : IPerpetualManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./IPerpetualFactory.sol";
import "./IPerpetualPoolFactory.sol";
import "./IPerpetualDepositManager.sol";
import "./IPerpetualWithdrawManager.sol";
import "./IPerpetualWithdrawAllManager.sol";
import "./IPerpetualLiquidator.sol";
import "./IPerpetualTreasury.sol";
import "./IPerpetualSettlement.sol";
import "./IPerpetualGetter.sol";
import "./IPerpetualSetter.sol";
import "./IPerpetualInfo.sol";
import "./ILibraryEvents.sol";
import "./IPerpetualTradeLogic.sol";
import "./IAMMPerpLogic.sol";
import "./IPerpetualUpdateLogic.sol";
import "./IPerpetualRebalanceLogic.sol";
import "./IPerpetualMarginLogic.sol";
import "./IPerpetualLimitTradeManager.sol";
import "./IPerpetualBrokerFeeLogic.sol";

interface IPerpetualManager is
    IPerpetualFactory,
    IPerpetualSetter,
    IPerpetualPoolFactory,
    IPerpetualDepositManager,
    IPerpetualWithdrawManager,
    IPerpetualWithdrawAllManager,
    IPerpetualLiquidator,
    IPerpetualTreasury,
    IPerpetualTradeLogic,
    IPerpetualBrokerFeeLogic,
    IPerpetualLimitTradeManager,
    IPerpetualSettlement,
    IPerpetualGetter,
    IAMMPerpLogic,
    ILibraryEvents,
    IPerpetualUpdateLogic,
    IPerpetualRebalanceLogic,
    IPerpetualMarginLogic,
    IPerpetualInfo
{}

File 35 of 45 : IPerpetualMarginLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "./IPerpetualOrder.sol";

interface IPerpetualMarginLogic is IPerpetualOrder {
    function depositMarginForOpeningTrade(
        uint24 _iPerpetualId,
        int128 _fDepositRequired,
        Order memory _order
    ) external returns (bool);

    function withdrawDepositFromMarginAccount(uint24 _iPerpetualId, address _traderAddr) external;

    function reduceMarginCollateral(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fAmountToWithdraw
    ) external;
}

File 36 of 45 : IPerpetualOrder.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualOrder {
    struct Order {
        uint16 leverageTDR; // 12.43x leverage is represented by 1243 (two-digit integer representation); 0 if deposit and trade separate
        uint16 brokerFeeTbps; // broker can set their own fee
        uint24 iPerpetualId; // global id for perpetual
        address traderAddr; // address of trader
        uint32 executionTimestamp; // normally set to current timestamp; order will not be executed prior to this timestamp.
        address brokerAddr; // address of the broker or zero
        uint32 submittedTimestamp;
        uint32 flags; // order flags
        uint32 iDeadline; //deadline for price (seconds timestamp)
        address executorAddr; // address of the executor set by contract
        int128 fAmount; // amount in base currency to be traded
        int128 fLimitPrice; // limit price
        int128 fTriggerPrice; //trigger price. Non-zero for stop orders.
        bytes brokerSignature; //signature of broker (or 0)
    }
}

File 37 of 45 : IPerpetualPoolFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualPoolFactory {
    function setPerpetualPoolFactory(address _shareTokenFactory) external;

    function createLiquidityPool(
        address _marginTokenAddress,
        uint16 _iTargetPoolSizeUpdateTime,
        int128 _fBrokerCollateralLotSize,
        int128 _fCeilPnLShare
    ) external returns (uint8);

    function runLiquidityPool(uint8 _liqPoolID) external;

    function setAMMPerpLogic(address _AMMPerpLogic) external;

    function setPoolParam(
        uint8 _poolId,
        string memory _name,
        int128 _value
    ) external;
}

File 38 of 45 : IPerpetualRebalanceLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualRebalanceLogic {
    function rebalance(uint24 _iPerpetualId) external;

    function decreasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external;

    function increasePoolCash(uint8 _iPoolIdx, int128 _fAmount) external;
}

File 39 of 45 : IPerpetualSetter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "./../interfaces/IPerpetualOrder.sol";

interface IPerpetualSetter {
    function setPerpetualOracles(
        uint24 _iPerpetualId,
        bytes4[2] calldata _baseQuoteS2,
        bytes4[2] calldata _baseQuoteS3
    ) external;

    function setPerpetualBaseParams(uint24 _iPerpetualId, int128[7] calldata _baseParams) external;

    function setPerpetualRiskParams(
        uint24 _iPerpetualId,
        int128[5] calldata _underlyingRiskParams,
        int128[12] calldata _defaultFundRiskParams
    ) external;

    function setPerpetualParam(
        uint24 _iPerpetualId,
        string memory _varName,
        int128 _value
    ) external;
    function resetMarkPremium(uint24 _iPerpetualId) external;
    function testTradeEvent(IPerpetualOrder.Order memory _order) external;
    function setPerpetualParamPair(
        uint24 _iPerpetualId,
        string memory _name,
        int128 _value1,
        int128 _value2
    ) external;
}

File 40 of 45 : IPerpetualSettlement.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualSettlement {
    function adjustSettlementPrice(
        uint24 _perpetualId,
        int128 _fSettlementS2,
        int128 _fSettlementS3
    ) external;

    function togglePerpEmergencyState(uint24 _perpetualId) external;

    function settleNextTraderInPool(uint8 _id) external returns (bool);

    function clearTradersInPool(uint8 _id, uint256 _bulkSize) external returns (bool);

    function settle(uint24 _perpetualID, address _traderAddr) external;

    function settleTraders(
        uint24 _perpetualID,
        uint256 _bulkSize
    ) external returns (uint256 _settled);

    function getSettleableAccounts(
        uint24 _perpetualId,
        uint256 start,
        uint256 count
    ) external view returns (address[] memory);

    function transferValueToTreasury() external returns (bool success);

    function deactivatePerp(uint24 _perpetualId) external;
}

File 41 of 45 : IPerpetualTradeLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import "../interfaces/IPerpetualOrder.sol";

interface IPerpetualTradeLogic {
    function executeTrade(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fTraderPos,
        int128 _fTradeAmount,
        int128 _fPrice,
        bool _isClose
    ) external returns (int128);

    function preTrade(IPerpetualOrder.Order memory _order) external returns (int128, int128);

    function distributeFeesLiquidation(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fDeltaPositionBC,
        uint16 _protocolFeeTbps
    ) external returns (int128);

    function distributeFees(
        IPerpetualOrder.Order memory _order,
        uint16 _brkrFeeTbps,
        uint16 _protocolFeeTbps,
        bool _hasOpened
    ) external returns (int128);

    function validateStopPrice(
        bool _isLong,
        int128 _fMarkPrice,
        int128 _fTriggerPrice
    ) external pure;

    function getMaxSignedOpenTradeSizeForPos(
        uint24 _perpetualId,
        int128 _fCurrentTraderPos,
        bool _isBuy
    ) external view returns (int128);

    function queryPerpetualPrice(
        uint24 _iPerpetualId,
        int128 _fTradeAmountBC,
        int128[2] calldata _fIndexPrice,
        uint16 _confTbps,
        uint64 _params
    ) external view returns (int128);
}

File 42 of 45 : IPerpetualTreasury.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "../core/PerpStorage.sol";

interface IPerpetualTreasury {
    function addLiquidity(uint8 _iPoolIndex, uint256 _tokenAmount) external;

    function pauseLiquidityProvision(uint8 _poolId, bool _pauseOn) external;

    function withdrawLiquidity(uint8 _iPoolIndex, uint256 _shareAmount) external;

    function executeLiquidityWithdrawal(uint8 _poolId, address _lpAddr) external;

    function getCollateralTokenAmountForPricing(uint8 _poolId) external view returns (int128);

    function getShareTokenPriceD18(uint8 _poolId) external view returns (uint256 price);

    function getTokenAmountToReturn(
        uint8 _poolId,
        uint256 _shareAmount
    ) external view returns (uint256);

    function getWithdrawRequests(
        uint8 poolId,
        uint256 _fromIdx,
        uint256 numRequests
    ) external view returns (PerpStorage.WithdrawRequest[] memory);
}

File 43 of 45 : IPerpetualUpdateLogic.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualUpdateLogic {
    function updateAMMTargetFundSize(uint24 _iPerpetualId) external;

    function updateDefaultFundTargetSize(uint24 _iPerpetualId) external;

    function updateFundingAndPricesBefore(uint24 _iPerpetualId, bool _revertIfClosed) external;

    function updateFundingAndPricesAfter(uint24 _iPerpetualId) external;

    function setNormalState(uint24 _iPerpetualId) external;

    /**
     * Set emergency state
     * @param _iPerpetualId Perpetual id
     */
    function setEmergencyState(uint24 _iPerpetualId) external;

    /**
     * @notice Set external treasury (DAO)
     * @param _treasury treasury address
     */
    function setTreasury(address _treasury) external;

    /**
     * @notice Set order book factory (DAO)
     * @param _orderBookFactory order book factory address
     */
    function setOrderBookFactory(address _orderBookFactory) external;

    /**
     * @notice Set oracle factory (DAO)
     * @param _oracleFactory oracle factory address
     */
    function setOracleFactory(address _oracleFactory) external;

    /**
     * @notice Set delay for trades to be executed
     * @param _delay    delay in number of blocks
     */
    function setBlockDelay(uint8 _delay) external;

    /**
     * @notice Submits price updates to the feeds used by a given perpetual.
     * @dev Reverts if the submission does not match the perpetual or
     * if the feed rejects it for a reason other than being unnecessary.
     * If this function returns false, sender is not charged msg.value.
     * @param _perpetualId Perpetual Id
     * @param _updateData Data to send to price feeds
     * @param _publishTimes Publish timestamps
     * @param _maxAcceptableFeedAge Maximum age of update in seconds
     */
    function updatePriceFeeds(
        uint24 _perpetualId,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes,
        uint256 _maxAcceptableFeedAge
    ) external payable;

    /**
     * @notice Links the message sender to a delegate to manage orders on their behalf.
     * @param delegate Address of delegate
     * @param index Index to emit with event. A value of zero removes the current delegate.
     */
    function setDelegate(address delegate, uint256 index) external;
}

File 44 of 45 : IPerpetualWithdrawAllManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualWithdrawAllManager {
    function withdrawAll(
        uint24 _iPerpetualId,
        address _traderAddr,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable;
}

File 45 of 45 : IPerpetualWithdrawManager.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IPerpetualWithdrawManager {
    function withdraw(
        uint24 _iPerpetualId,
        address _traderAddr,
        int128 _fAmount,
        bytes[] calldata _updateData,
        uint64[] calldata _publishTimes
    ) external payable;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100000000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"callbackTarget","type":"address"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"Callback","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"bytes32","name":"digest","type":"bytes32"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"ExecutionFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint24","name":"perpetualId","type":"uint24"},{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"brokerAddr","type":"address"},{"components":[{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"uint32","name":"submittedTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"executorAddr","type":"address"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"}],"indexed":false,"internalType":"struct IPerpetualOrder.Order","name":"order","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"digest","type":"bytes32"}],"name":"PerpetualLimitOrderCreated","type":"event"},{"inputs":[],"name":"CALLBACK_GAS_LIMIT","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"actvDigestPos","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"callbackFunctions","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bytes[]","name":"_updateData","type":"bytes[]"},{"internalType":"uint64[]","name":"_publishTimes","type":"uint64[]"}],"name":"cancelOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"digestsOfTrader","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_digests","type":"bytes32[]"},{"internalType":"address","name":"_executorAddr","type":"address"},{"internalType":"bytes[]","name":"_updateData","type":"bytes[]"},{"internalType":"uint64[]","name":"_publishTimes","type":"uint64[]"}],"name":"executeOrders","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_digest","type":"bytes32"}],"name":"getOrderStatus","outputs":[{"internalType":"enum LimitOrderBook.OrderStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getOrders","outputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"orders","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"digest","type":"bytes32"}],"name":"getTrader","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_perpetualManagerAddr","type":"address"},{"internalType":"uint24","name":"_perpetualId","type":"uint24"},{"internalType":"uint8","name":"_iCancelDelaySec","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastOrderHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"limitDigestsOfTrader","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketCloseSwitchTimestamp","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"}],"name":"numberOfDigestsOfTrader","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orderDependency","outputs":[{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"orderOfDigest","outputs":[{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"uint32","name":"submittedTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"executorAddr","type":"address"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perpManager","outputs":[{"internalType":"contract IPerpetualManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"perpetualId","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_numElements","type":"uint256"}],"name":"pollRange","outputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"orders","type":"tuple[]"},{"internalType":"bytes32[]","name":"orderHashes","type":"bytes32[]"},{"internalType":"uint32[]","name":"submittedTs","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint24","name":"iPerpetualId","type":"uint24"},{"internalType":"int128","name":"fLimitPrice","type":"int128"},{"internalType":"uint16","name":"leverageTDR","type":"uint16"},{"internalType":"uint32","name":"executionTimestamp","type":"uint32"},{"internalType":"uint32","name":"flags","type":"uint32"},{"internalType":"uint32","name":"iDeadline","type":"uint32"},{"internalType":"address","name":"brokerAddr","type":"address"},{"internalType":"int128","name":"fTriggerPrice","type":"int128"},{"internalType":"int128","name":"fAmount","type":"int128"},{"internalType":"bytes32","name":"parentChildDigest1","type":"bytes32"},{"internalType":"address","name":"traderAddr","type":"address"},{"internalType":"bytes32","name":"parentChildDigest2","type":"bytes32"},{"internalType":"uint16","name":"brokerFeeTbps","type":"uint16"},{"internalType":"bytes","name":"brokerSignature","type":"bytes"},{"internalType":"address","name":"callbackTarget","type":"address"}],"internalType":"struct IClientOrder.ClientOrder[]","name":"_orders","type":"tuple[]"},{"internalType":"bytes[]","name":"_signatures","type":"bytes[]"}],"name":"postOrders","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b506200001c62000022565b620000e3565b600054610100900460ff16156200008f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff90811614620000e1576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b615e9a80620000f36000396000f3fe60806040526004361061016a5760003560e01c8063749fb279116100cb578063995785341161007f578063cccc2ede11610059578063cccc2ede146104e4578063da5c2078146104fa578063e905f3d11461054857600080fd5b80639957853414610484578063a9a5e49c146104b1578063b67f7613146104d157600080fd5b806382227d87116100b057806382227d87146103f05780639023dc5b1461041d57806395fafddb1461045757600080fd5b8063749fb279146103ae57806379b8783c146103d057600080fd5b806327b62ad91161012257806346423aa71161010757806346423aa7146102ea57806361fe71c61461031757806367be6a641461034657600080fd5b806327b62ad91461029157806333d608f1146102be57600080fd5b80631e8dd78a116101535780631e8dd78a146101fc57806320ef8157146102455780632453ffa81461027c57600080fd5b80630ee27e341461016f5780631a604cba146101c5575b600080fd5b34801561017b57600080fd5b506101b261018a366004614e05565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b6040519081526020015b60405180910390f35b3480156101d157600080fd5b506000546101e9906601000000000000900460070b81565b60405160079190910b81526020016101bc565b34801561020857600080fd5b50610230610217366004614e20565b6003602052600090815260409020805460019091015482565b604080519283526020830191909152016101bc565b34801561025157600080fd5b50600054610268906301000000900462ffffff1681565b60405162ffffff90911681526020016101bc565b34801561028857600080fd5b506004546101b2565b34801561029d57600080fd5b506102b16102ac366004614e39565b61055b565b6040516101bc919061504e565b3480156102ca57600080fd5b506102d56207a12081565b60405163ffffffff90911681526020016101bc565b3480156102f657600080fd5b5061030a610305366004614e20565b6106f4565b6040516101bc9190615090565b34801561032357600080fd5b506103376103323660046150d1565b610879565b6040516101bc9392919061512e565b34801561035257600080fd5b50610389610361366004614e20565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101bc565b3480156103ba57600080fd5b506103ce6103c93660046151b0565b610b3d565b005b3480156103dc57600080fd5b506101b26103eb3660046151fd565b610e91565b3480156103fc57600080fd5b506101b261040b366004614e20565b60056020526000908152604090205481565b34801561042957600080fd5b5061043d610438366004614e20565b610ec2565b6040516101bc9e9d9c9b9a99989796959493929190615227565b34801561046357600080fd5b50610477610472366004614e39565b611070565b6040516101bc9190615325565b34801561049057600080fd5b506008546103899073ffffffffffffffffffffffffffffffffffffffff1681565b3480156104bd57600080fd5b506103ce6104cc36600461537d565b61111c565b6103ce6104df3660046153e9565b611238565b3480156104f057600080fd5b506101b260075481565b34801561050657600080fd5b50610389610515366004614e20565b600090815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b6103ce6105563660046154bb565b61194d565b60608167ffffffffffffffff81111561057657610576615524565b60405190808252806020026020018201604052801561063a57816020015b604080516101e0810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a08301526101c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816105945790505b5073ffffffffffffffffffffffffffffffffffffffff851660009081526001602052604081209192505b838110156106eb5781546106788683615582565b10156106e35760008261068b8784615582565b8154811061069b5761069b615595565b906000526020600020015490506106c46002600083815260200190815260200160002082611d78565b8483815181106106d6576106d6615595565b6020026020010181905250505b600101610664565b50509392505050565b6008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810183905260009173ffffffffffffffffffffffffffffffffffffffff169063b12dff7890602401602060405180830381865afa158015610764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078891906155c4565b1561079557506000919050565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa158015610804573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082891906155c4565b1561083557506001919050565b600082815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1661087157506003919050565b506002919050565b6060808060006108898587615582565b60045490915081111561089b57506004545b8467ffffffffffffffff8111156108b4576108b4615524565b60405190808252806020026020018201604052801561097857816020015b604080516101e0810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a08301526101c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816108d25790505b5093508467ffffffffffffffff81111561099457610994615524565b6040519080825280602002602001820160405280156109bd578160200160208202803683370190505b5092508467ffffffffffffffff8111156109d9576109d9615524565b604051908082528060200260200182016040528015610a02578160200160208202803683370190505b509150855b81811015610b34576000610a1b88836155e6565b905060048281548110610a3057610a30615595565b9060005260206000200154858281518110610a4d57610a4d615595565b602002602001018181525050610aa760026000878481518110610a7257610a72615595565b60200260200101518152602001908152602001600020868381518110610a9a57610a9a615595565b6020026020010151611d78565b868281518110610ab957610ab9615595565b602002602001018190525060026000868381518110610ada57610ada615595565b6020026020010151815260200190815260200160002060010160149054906101000a900463ffffffff16848281518110610b1657610b16615595565b63ffffffff9092166020928302919091019091015250600101610a07565b50509250925092565b600054610100900460ff1615808015610b5d5750600054600160ff909116105b80610b775750303b158015610b77575060005460ff166001145b610c08576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610c6657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff8416610ce3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f70657270657475616c206d616e6167657220696e76616c6964000000000000006044820152606401610bff565b62ffffff8316610d4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f70657270657475616c496420696e76616c6964000000000000000000000000006044820152606401610bff565b60008054600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790557fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff16630100000062ffffff8616027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff16176201000060ff851602177fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff1666010000000000004267ffffffffffffffff16021790558015610e8b57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b50505050565b60016020528160005260406000208181548110610ead57600080fd5b90600052602060002001600091509150505481565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501805461ffff808716986201000088049091169762ffffff6401000000008904169773ffffffffffffffffffffffffffffffffffffffff670100000000000000820481169863ffffffff7b0100000000000000000000000000000000000000000000000000000090930483169885831698740100000000000000000000000000000000000000008704851698780100000000000000000000000000000000000000000000000088048616987c0100000000000000000000000000000000000000000000000000000000909804909516969290931694600f82810b95700100000000000000000000000000000000909304810b94900b92909190610fed906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054611019906155f9565b80156110665780601f1061103b57610100808354040283529160200191611066565b820191906000526020600020905b81548152906001019060200180831161104957829003601f168201915b505050505090508e565b60606111148383600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561110057602002820191906000526020600020905b8154815260200190600101908083116110ec575b5050505050611fb19092919063ffffffff16565b949350505050565b821580159061112a57508281145b611190576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f617272617973206d69736d6174636800000000000000000000000000000000006044820152606401610bff565b611198612090565b60005b83811015611231576112288585838181106111b8576111b8615595565b90506020028101906111ca919061564c565b8484848181106111dc576111dc615595565b90506020028101906111ee919061568a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061220892505050565b5060010161119b565b5050505050565b600087815260026020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff6401000000008504169281019290925273ffffffffffffffffffffffffffffffffffffffff67010000000000000084048116606084015263ffffffff7b0100000000000000000000000000000000000000000000000000000090940484166080840152600182015480821660a0850152740100000000000000000000000000000000000000008104851660c085015278010000000000000000000000000000000000000000000000008104851660e08501527c01000000000000000000000000000000000000000000000000000000009004909316610100830152928301549091166101208201526003820154600f81810b610140840152700100000000000000000000000000000000909104810b6101608301526004830154900b6101808201526005820180549192916101a0840191906113a9906155f9565b80601f01602080910402602001604051908101604052809291908181526020018280546113d5906155f9565b80156114225780601f106113f757610100808354040283529160200191611422565b820191906000526020600020905b81548152906001019060200180831161140557829003601f168201915b50505091909252505050604081015160005491925062ffffff908116630100000090920416146114ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f72646572206e6f7420666f756e6400000000000000000000000000000000006044820152606401610bff565b600854600080546040517f3bb510c900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90931692633bb510c992349261151f92630100000090910462ffffff16918b918b918b918b919060040161582b565b6000604051808303818588803b15801561153857600080fd5b505af115801561154c573d6000803e3d6000fd5b5050505050611559612090565b600080546601000000000000900460070b81131561158f5760005461158a906601000000000000900460070b615871565b6115a0565b6000546601000000000000900460070b5b90508160c0015163ffffffff168167ffffffffffffffff1610156115cb575060c081015163ffffffff165b600080546601000000000000900460070b128061160657506000546115f99062010000900460ff16826158af565b67ffffffffffffffff1642105b156116b057816060015173ffffffffffffffffffffffffffffffffffffffff16600060039054906101000a900462ffffff1662ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a08b6040516116a39181526040602082018190526015908201527f63616e63656c2064656c61792072657175697265640000000000000000000000606082015260800190565b60405180910390a3611942565b816060015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415801561178b575060085460608301516040517f5fec5d0b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152336024820152911690635fec5d0b90604401602060405180830381865afa158015611765573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178991906155c4565b155b15611895576008546000906117f8906117bc90859073ffffffffffffffffffffffffffffffffffffffff168461344d565b8a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061359392505050565b9050826060015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f747261646572206d757374207369676e000000000000000000000000000000006044820152606401610bff565b505b6008546000546040517f7200c7c5000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff166004820152602481018b905273ffffffffffffffffffffffffffffffffffffffff90911690637200c7c590604401600060405180830381600087803b15801561191657600080fd5b505af115801561192a573d6000803e3d6000fd5b50505050611937896135af565b611942896000613807565b505050505050505050565b85806119b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f6e6f206f726465727300000000000000000000000000000000000000000000006044820152606401610bff565b6000600260008a8a60008181106119ce576119ce615595565b90506020020135815260200190815260200160002060010160149054906101000a900463ffffffff1663ffffffff1690506000600260008b8b6000818110611a1857611a18615595565b602090810292909201358352508101919091526040016000908120547b01000000000000000000000000000000000000000000000000000000900463ffffffff169150818311611a685781611a6a565b825b90506000428210611a7c576000611a86565b611a8682426155e6565b905060015b85811015611b7857600260008e8e84818110611aa957611aa9615595565b90506020020135815260200190815260200160002060010160149054906101000a900463ffffffff1663ffffffff169450600260008e8e84818110611af057611af0615595565b60209081029290920135835250810191909152604001600020547b01000000000000000000000000000000000000000000000000000000900463ffffffff169350838511611b3e5783611b40565b845b92506000428410611b52576000611b5c565b611b5c84426155e6565b9050808310611b6b5780611b6d565b825b925050600101611a8b565b508715611c22576008546000546040517f3bb510c900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691633bb510c9913491611bef916301000000900462ffffff16908e908e908e908e908a9060040161582b565b6000604051808303818588803b158015611c0857600080fd5b505af1158015611c1c573d6000803e3d6000fd5b50505050505b6008546000546040517ff404f2ee000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff16600482015273ffffffffffffffffffffffffffffffffffffffff9091169063f404f2ee90602401602060405180830381865afa158015611c9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc391906158d7565b821115611d2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6f75746461746564206f7261636c6573000000000000000000000000000000006044820152606401610bff565b611d34612090565b60005b85811015611d6957611d618d8d83818110611d5457611d54615595565b905060200201358c61394a565b600101611d37565b50505050505050505050505050565b604080516101e081018252600060208201819052918101829052606080820183905260a0820183905260e082018390526101008201839052610120820183905261016082018390526101a08201526101c081019190915260018301547801000000000000000000000000000000000000000000000000810463ffffffff1660808301528354640100000000810462ffffff16835262010000810461ffff16610180840152670100000000000000900473ffffffffffffffffffffffffffffffffffffffff9081166101408401521660c0820152600583018054611e5a906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054611e86906155f9565b8015611ed35780601f10611ea857610100808354040283529160200191611ed3565b820191906000526020600020905b815481529060010190602001808311611eb657829003601f168201915b50505050506101a0820152600383810154600f81810b610100850152700100000000000000000000000000000000909104810b602080850191909152600486015490910b60e0840152845461ffff81166040808601919091526001808801547c0100000000000000000000000000000000000000000000000000000000900463ffffffff90811660a08801527b01000000000000000000000000000000000000000000000000000000909304909216606086015260008681528484529081208054610120870152908690529290915201546101608201525b92915050565b60608167ffffffffffffffff811115611fcc57611fcc615524565b604051908082528060200260200182016040528015611ff5578160200160208202803683370190505b50905060005b828110156120885784518161201085876158f0565b61201a9190615582565b101561207157848161202c85876158f0565b6120369190615582565b8151811061204657612046615595565b602002602001015182828151811061206057612060615595565b602002602001018181525050612076565b612088565b8061208081615907565b915050611ffb565b509392505050565b600854600080546040517f7cb8ff18000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff166004820152909173ffffffffffffffffffffffffffffffffffffffff1690637cb8ff1890602401602060405180830381865afa15801561210e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213291906155c4565b60008054919250660100000000000090910460070b1380156121515750805b156121a65761215f42615871565b6000805467ffffffffffffffff929092166601000000000000027fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff90921691909117905550565b600080546601000000000000900460070b1280156121c2575080155b1561220557600080547fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff1666010000000000004267ffffffffffffffff16021790555b50565b6000612217602084018461593f565b6000546301000000900462ffffff908116911614612291576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e67206f7264657220626f6f6b000000000000000000000000000000006044820152606401610bff565b60006122a561016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff1603612322576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c69642d7472616465720000000000000000000000000000000000006044820152606401610bff565b6123346101208401610100850161595a565b600f0b6000036123a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c696420616d6f756e740000000000000000000000000000000000006044820152606401610bff565b426123b160c0850160a0860161597d565b63ffffffff161161241e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642d646561646c696e65000000000000000000000000000000006044820152606401610bff565b61242e60c0840160a0850161597d565b63ffffffff16612444608085016060860161597d565b63ffffffff16108015612477575061245f4262093a80615582565b61246f608085016060860161597d565b63ffffffff16105b6124dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c6964206578656320747300000000000000000000000000000000006044820152606401610bff565b6124fb6124f060a085016080860161597d565b632000000016151590565b1561257d576000612513610100850160e0860161595a565b600f0b1361257d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964207472696767657220707269636500000000000000000000006044820152606401610bff565b604080516101c08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820183905261010082018390526101208201839052610140820183905261016082018390526101808201929092526101a081019190915261260060a085016080860161597d565b63ffffffff1660e0820152612618602085018561593f565b62ffffff1660408201526126346101a0850161018086016159a3565b61ffff16602082015261264f61016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff16606082015261267a60e0850160c08601614e05565b73ffffffffffffffffffffffffffffffffffffffff1660a08201526126a36101a085018561568a565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050506101a08201526126ef6101208501610100860161595a565b600f0b610140820152612708604085016020860161595a565b600f0b610160820152612722610100850160e0860161595a565b600f0b61018082015261273b60608501604086016159a3565b61ffff16815261275160c0850160a0860161597d565b63ffffffff1661010082015261276d608085016060860161597d565b63ffffffff9081166080830152421660c082015260006127916101a086018661568a565b905011156129315760085460009061280c906127c490849073ffffffffffffffffffffffffffffffffffffffff166142fc565b6127d26101a088018861568a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061359392505050565b905073ffffffffffffffffffffffffffffffffffffffff811661288b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c69642062726f6b65722073696700000000000000000000000000006044820152606401610bff565b61289b60e0860160c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461292f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c69642062726f6b65722073696700000000000000000000000000006044820152606401610bff565b505b60085461295790829073ffffffffffffffffffffffffffffffffffffffff16600161344d565b9150610120840135151580612970575061016084013515155b15612a2f57818461012001351415801561298f57508184610160013514155b6129f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6f726465722073656c662d6c696e6b65640000000000000000000000000000006044820152606401610bff565b6040805180820182526101208601358152610160860135602080830191825260008681526003909152929092209051815590516001909101555b6000612a436101e086016101c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff1614158015612a935750612a93612a776101e086016101c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b15612afc57612aaa6101e085016101c08601614e05565b600083815260066020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555b612b0e61016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015612c09575060085473ffffffffffffffffffffffffffffffffffffffff16635fec5d0b612b7461016087016101408801614e05565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152336024820152604401602060405180830381865afa158015612be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0791906155c4565b155b15612d34576000612c1a8385613593565b905073ffffffffffffffffffffffffffffffffffffffff8116612c99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610bff565b816060015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614612d32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610bff565b505b600082815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1615612dcb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6f726465722d65786973747300000000000000000000000000000000000000006044820152606401610bff565b606081015173ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902054603211612e5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f746f6f206d616e79206f726465727300000000000000000000000000000000006044820152606401610bff565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa158015612ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef191906155c4565b15612f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6f726465722065786563757465640000000000000000000000000000000000006044820152606401610bff565b6008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa158015612fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612feb91906155c4565b15613052576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6f726465722063616e63656c65640000000000000000000000000000000000006044820152606401610bff565b6000828152600260208181526040928390208451815492860151948601516060870151608088015161ffff9384167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090961695909517620100009390971692909202959095177fffffffffff0000000000000000000000000000000000000000000000ffffffff1664010000000062ffffff909616959095027fffffffffff0000000000000000000000000000000000000000ffffffffffffff169490941767010000000000000073ffffffffffffffffffffffffffffffffffffffff95861602177fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000063ffffffff9384160217815560a085015160018201805460c088015160e08901516101008a01519489167fffffffffffffffff0000000000000000000000000000000000000000000000009093169290921774010000000000000000000000000000000000000000918716919091021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000918616919091027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177c010000000000000000000000000000000000000000000000000000000092909416919091029290921790915561012084015191810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001692909316919091179091556101408201516101608301516fffffffffffffffffffffffffffffffff918216700100000000000000000000000000000000918316919091021760038301556101808301516004830180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000016919092161790556101a0820151829190600582019061332e9082615a0d565b50600191506000905061334961016087016101408801614e05565b73ffffffffffffffffffffffffffffffffffffffff16815260208082019290925260409081016000908120805460018181018355918352848320018690556004805491820181557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910186905554858252600590935220556133d461016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff166133f7602086018661593f565b62ffffff167fb8ace0e652054ba9160467efc4bd026f89b793c887035f7d88d7b7b0615c4c5f61342d60e0880160c08901614e05565b848660405161343e93929190615c83565b60405180910390a35092915050565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb8184015246606082015273ffffffffffffffffffffffffffffffffffffffff85166080808301919091528351808303909101815260a09091019092528151910120600090816135178661443f565b60408051602081018590529081018290528515156060820152909150600090608001604051602081830303815290604052805190602001209050613588817f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b979650505050505050565b60008060006135a2858561457d565b91509150612088816145c2565b806135b75750565b600081815260026020908152604080832054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff168352600190915281208054909103613600575050565b60005b81548110156136ad578282828154811061361f5761361f615595565b9060005260206000200154036136a5578154829061363f906001906155e6565b8154811061364f5761364f615595565b906000526020600020015482828154811061366c5761366c615595565b90600052602060002001819055508180548061368a5761368a615cc2565b60019003818190600052602060002001600090559055603290505b600101613603565b6000838152600260208190526040822080547fff000000000000000000000000000000000000000000000000000000000000001681556001810183905590810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600381018290556004810180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000169055906137526005830182614d8e565b505060008381526005602052604081205460048054919291613776906001906155e6565b8154811061378657613786615595565b60009182526020808320909101548083526005909152604080832085905587835282209190915590508060046137bd6001856155e6565b815481106137cd576137cd615595565b60009182526020909120015560048054806137ea576137ea615cc2565b600190038181906000526020600020016000905590555050505050565b600082815260066020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811690915573ffffffffffffffffffffffffffffffffffffffff168061385d57505050565b6040517f22ad65db00000000000000000000000000000000000000000000000000000000815260048101849052821515602482015260009073ffffffffffffffffffffffffffffffffffffffff8316906322ad65db906207a12090604401600060405180830381600088803b1580156138d557600080fd5b5087f1935050505080156138e7575060015b156138f0575060015b6040805173ffffffffffffffffffffffffffffffffffffffff8416815282151560208201526207a120918101919091527f1d4b3142757d5200ad5437c0348f6047f6f312b4ccadda70ecf72cd93ea1e26090606001610e82565b600082815260026020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff6401000000008504169281019290925273ffffffffffffffffffffffffffffffffffffffff67010000000000000084048116606084015263ffffffff7b0100000000000000000000000000000000000000000000000000000090940484166080840152600182015480821660a0850152740100000000000000000000000000000000000000008104851660c085015278010000000000000000000000000000000000000000000000008104851660e08501527c01000000000000000000000000000000000000000000000000000000009004909316610100830152928301549091166101208201526003820154600f81810b610140840152700100000000000000000000000000000000909104810b6101608301526004830154900b6101808201526005820180549192916101a084019190613abb906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054613ae7906155f9565b8015613b345780601f10613b0957610100808354040283529160200191613b34565b820191906000526020600020905b815481529060010190602001808311613b1757829003601f168201915b50505091909252505050606081015190915073ffffffffffffffffffffffffffffffffffffffff8116613bc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f72646572206e6f7420666f756e6400000000000000000000000000000000006044820152606401610bff565b73ffffffffffffffffffffffffffffffffffffffff8316610120830152613be984614775565b15613c50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f64706379206e6f742066756c66696c6c656400000000000000000000000000006044820152606401610bff565b600080836080015163ffffffff16421015613cc7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6578656320746f6f206561726c790000000000000000000000000000000000006044820152606401610bff565b6008546040517fa83679b200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063a83679b290613d1f9087903390600401615cf1565b6020604051808303816000875af1925050508015613d78575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252613d75918101906155c4565b60015b6140f257613d84615d29565b806308c379a0036140e65750613d98615d90565b80613da357506140e8565b613de2816040518060400160405280602081526020017f547261646520616d743e6d617820616d7420666f72207472616465722f414d4d81525061491d565b15613df0576001925061407e565b613e2f816040518060400160405280600e81526020017f64656c617920726571756972656400000000000000000000000000000000000081525061491d565b80613fae575060e0850151631000000016158015613fae5750613e87816040518060400160405280601081526020017f6d61726b657420697320636c6f7365640000000000000000000000000000000081525061491d565b80613ecc5750613ecc816040518060400160405280601481526020017f7472696767657220636f6e64206e6f74206d657400000000000000000000000081525061491d565b80613fae575060e0850151634000000016158015613fae5750613f24816040518060400160405280601381526020017f747261646520697320636c6f7365206f6e6c790000000000000000000000000081525061491d565b80613f695750613f69816040518060400160405280601381526020017f70726963652065786365656473206c696d69740000000000000000000000000081525061491d565b80613fae5750613fae816040518060400160405280601081526020017f6f75746461746564206f7261636c65730000000000000000000000000000000081525061491d565b15613fe757806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bff9190615e38565b614026816040518060400160405280600f81526020017f6f726465722063616e63656c6c6564000000000000000000000000000000000081525061491d565b8061406b575061406b816040518060400160405280600e81526020017f6f7264657220657865637574656400000000000000000000000000000000000081525061491d565b15614079576000925061407e565b600192505b60005460405173ffffffffffffffffffffffffffffffffffffffff8616916301000000900462ffffff16907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a0906140d8908b908690615e4b565b60405180910390a3506142e0565b505b3d6000803e3d6000fd5b80614248574285610100015163ffffffff16116141a65760005460405173ffffffffffffffffffffffffffffffffffffffff86169162ffffff630100000090910416907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a090614199908b81526040602082018190526008908201527f646561646c696e65000000000000000000000000000000000000000000000000606082015260800190565b60405180910390a361423f565b60005460405173ffffffffffffffffffffffffffffffffffffffff86169162ffffff630100000090910416907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a090614236908b81526040602082018190526013908201527f70726963652065786365656473206c696d697400000000000000000000000000606082015260800190565b60405180910390a35b600192506142de565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810189905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156142b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142db91906155c4565b91505b505b6142ea8683614944565b6142f48682613807565b505050505050565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb8184015246606082015273ffffffffffffffffffffffffffffffffffffffff84166080808301919091528351808303909101815260a09091019092528151910120600090816143c685614b43565b9050600082826040516020016143e6929190918252602082015260400190565b604051602081830303815290604052805190602001209050614435817f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b9695505050505050565b6000807fe5599e89712387846e6878c8cac8abdf5d6051ccbc4a6cfa5efe389720300ec860001b8360400151846020015185606001518660a001518761014001518861016001518961018001518a61010001518b60e001518c600001518d608001516040516020016145409c9b9a999897969594939291909b8c5262ffffff9a909a1660208c015261ffff98891660408c015273ffffffffffffffffffffffffffffffffffffffff97881660608c01529590961660808a0152600f93840b60a08a015291830b60c089015290910b60e087015263ffffffff908116610100870152918216610120860152909116610140840152166101608201526101800190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b60008082516041036145b35760208301516040840151606085015160001a6145a787828585614bd6565b945094505050506145bb565b506000905060025b9250929050565b60008160048111156145d6576145d6615061565b036145de5750565b60018160048111156145f2576145f2615061565b03614659576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610bff565b600281600481111561466d5761466d615061565b036146d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610bff565b60038160048111156146e8576146e8615061565b03612205576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610bff565b60008181526003602052604081208054156147935750600092915050565b600181015480158061483357506008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa15801561480f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061483391906155c4565b806148cc57506008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156148a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148cc91906155c4565b806149045750600081815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff16155b15614913575060009392505050565b5060019392505050565b60008151835114801561493d575081805190602001208380519060200120145b9392505050565b61494d826135af565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156149bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149e091906155c4565b158015614a7d57506008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa158015614a57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a7b91906155c4565b155b15614b1c576008546000546040517f7200c7c5000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff1660048201526024810184905273ffffffffffffffffffffffffffffffffffffffff90911690637200c7c590604401600060405180830381600087803b158015614b0357600080fd5b505af1158015614b17573d6000803e3d6000fd5b505050505b8015614b2b57614b2b82614cc5565b50600090815260036020526040812081815560010155565b6000807f1aae56290d242a9c761ca2ef80072ffe2a6171793ad9f88e04426b2acc5e730d60001b83604001518460200151856060015186610100015160405160200161454095949392919094855262ffffff93909316602085015261ffff91909116604084015273ffffffffffffffffffffffffffffffffffffffff16606083015263ffffffff16608082015260a00190565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614c0d5750600090506003614cbc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614c61573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116614cb557600060019250925050614cbc565b9150600090505b94509492505050565b600080614cd183614cfd565b90925090508115614ce757614ce7826000614944565b8015614cf857614cf8816000614944565b505050565b60008181526003602052604081208190614d178154151590565b614d275750600093849350915050565b80546000818152600360205260409020600101548514614d45575060005b6001820154614d5957946000945092505050565b6001808301546000818152600360205260409020909101548614614d835750946000945092505050565b909590945092505050565b508054614d9a906155f9565b6000825580601f10614daa575050565b601f01602090049060005260206000209081019061220591905b80821115614dd85760008155600101614dc4565b5090565b803573ffffffffffffffffffffffffffffffffffffffff81168114614e0057600080fd5b919050565b600060208284031215614e1757600080fd5b61493d82614ddc565b600060208284031215614e3257600080fd5b5035919050565b600080600060608486031215614e4e57600080fd5b614e5784614ddc565b95602085013595506040909401359392505050565b6000815180845260005b81811015614e9257602081850181015186830182015201614e76565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600081518084526020808501808196508360051b8101915082860160005b858110156150415782840389528151805162ffffff1685526101e086820151614f1b88880182600f0b9052565b5060408281015161ffff169087015260608083015163ffffffff9081169188019190915260808084015182169088015260a0808401519091169087015260c08083015173ffffffffffffffffffffffffffffffffffffffff169087015260e080830151600f81900b82890152505061010080830151614f9e82890182600f0b9052565b505061012082810151908701526101408083015173ffffffffffffffffffffffffffffffffffffffff169087015261016080830151908701526101808083015161ffff16908701526101a080830151818801839052614fff83890182614e6c565b925050506101c080830151925061502d8188018473ffffffffffffffffffffffffffffffffffffffff169052565b509986019994505090840190600101614eee565b5091979650505050505050565b60208152600061493d6020830184614ed0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208101600483106150cb577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b600080604083850312156150e457600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561512357815187529582019590820190600101615107565b509495945050505050565b6060815260006151416060830186614ed0565b60208382038185015261515482876150f3565b8481036040860152855180825282870193509082019060005b8181101561518f57845163ffffffff168352938301939183019160010161516d565b509098975050505050505050565b803562ffffff81168114614e0057600080fd5b6000806000606084860312156151c557600080fd5b6151ce84614ddc565b92506151dc6020850161519d565b9150604084013560ff811681146151f257600080fd5b809150509250925092565b6000806040838503121561521057600080fd5b61521983614ddc565b946020939093013593505050565b61ffff8f811682528e16602082015262ffffff8d16604082015273ffffffffffffffffffffffffffffffffffffffff8c16606082015263ffffffff8b16608082015273ffffffffffffffffffffffffffffffffffffffff8a1660a082015263ffffffff891660c082015263ffffffff881660e082015263ffffffff871661010082015273ffffffffffffffffffffffffffffffffffffffff86166101208201526152d7610140820186600f0b9052565b6152e7610160820185600f0b9052565b6152f7610180820184600f0b9052565b6101c06101a082015260006153106101c0830184614e6c565b90509f9e505050505050505050505050505050565b60208152600061493d60208301846150f3565b60008083601f84011261534a57600080fd5b50813567ffffffffffffffff81111561536257600080fd5b6020830191508360208260051b85010111156145bb57600080fd5b6000806000806040858703121561539357600080fd5b843567ffffffffffffffff808211156153ab57600080fd5b6153b788838901615338565b909650945060208701359150808211156153d057600080fd5b506153dd87828801615338565b95989497509550505050565b60008060008060008060006080888a03121561540457600080fd5b87359650602088013567ffffffffffffffff8082111561542357600080fd5b818a0191508a601f83011261543757600080fd5b81358181111561544657600080fd5b8b602082850101111561545857600080fd5b6020830198508097505060408a013591508082111561547657600080fd5b6154828b838c01615338565b909650945060608a013591508082111561549b57600080fd5b506154a88a828b01615338565b989b979a50959850939692959293505050565b60008060008060008060006080888a0312156154d657600080fd5b873567ffffffffffffffff808211156154ee57600080fd5b6154fa8b838c01615338565b909950975087915061550e60208b01614ddc565b965060408a013591508082111561547657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611fab57611fab615553565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156155d657600080fd5b8151801515811461493d57600080fd5b81810381811115611fab57611fab615553565b600181811c9082168061560d57607f821691505b602082108103615646577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe2183360301811261568057600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156bf57600080fd5b83018035915067ffffffffffffffff8211156156da57600080fd5b6020019150368190038213156145bb57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561504157828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe188360301811261579157600080fd5b8701858101903567ffffffffffffffff8111156157ad57600080fd5b8036038213156157bc57600080fd5b6157c78682846156ef565b9a87019a9550505090840190600101615752565b818352600060208085019450826000805b8681101561581f57823567ffffffffffffffff811680821461580c578384fd5b89525096830196918301916001016157ec565b50959695505050505050565b62ffffff8716815260806020820152600061584a608083018789615738565b828103604084015261585d8186886157db565b915050826060830152979650505050505050565b60008160070b7fffffffffffffffffffffffffffffffffffffffffffffffff800000000000000081036158a6576158a6615553565b60000392915050565b67ffffffffffffffff8181168382160190808211156158d0576158d0615553565b5092915050565b6000602082840312156158e957600080fd5b5051919050565b8082028115828204841417611fab57611fab615553565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361593857615938615553565b5060010190565b60006020828403121561595157600080fd5b61493d8261519d565b60006020828403121561596c57600080fd5b813580600f0b811461493d57600080fd5b60006020828403121561598f57600080fd5b813563ffffffff8116811461493d57600080fd5b6000602082840312156159b557600080fd5b813561ffff8116811461493d57600080fd5b601f821115614cf857600081815260208120601f850160051c810160208610156159ee5750805b601f850160051c820191505b818110156142f4578281556001016159fa565b815167ffffffffffffffff811115615a2757615a27615524565b615a3b81615a3584546155f9565b846159c7565b602080601f831160018114615a8e5760008415615a585750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556142f4565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015615adb57888601518255948401946001909101908401615abc565b5085821015615b1757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b805161ffff16825260006101c06020830151615b49602086018261ffff169052565b506040830151615b60604086018262ffffff169052565b506060830151615b88606086018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615ba0608086018263ffffffff169052565b5060a0830151615bc860a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c0830151615be060c086018263ffffffff169052565b5060e0830151615bf860e086018263ffffffff169052565b506101008381015163ffffffff16908501526101208084015173ffffffffffffffffffffffffffffffffffffffff169085015261014080840151600f81900b82870152505061016080840151615c5282870182600f0b9052565b505061018080840151615c6982870182600f0b9052565b50506101a080840151828287015261443583870182614e6c565b73ffffffffffffffffffffffffffffffffffffffff84168152606060208201526000615cb26060830185615b27565b9050826040830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b604081526000615d046040830185615b27565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b600060033d1115615d425760046000803e5060005160e01c5b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116810181811067ffffffffffffffff82111715615d8957615d89615524565b6040525050565b600060443d1015615d9e5790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff8160248401118184111715615dec57505050505090565b8285019150815181811115615e045750505050505090565b843d8701016020828501011115615e1e5750505050505090565b615e2d60208286010187615d45565b509095945050505050565b60208152600061493d6020830184614e6c565b8281526040602082015260006111146040830184614e6c56fea2646970667358221220d4e6ece2bdb158e526398e762820c9306f8cac22f3547ca4f87fb10385c8b18864736f6c63430008150033

Deployed Bytecode

0x60806040526004361061016a5760003560e01c8063749fb279116100cb578063995785341161007f578063cccc2ede11610059578063cccc2ede146104e4578063da5c2078146104fa578063e905f3d11461054857600080fd5b80639957853414610484578063a9a5e49c146104b1578063b67f7613146104d157600080fd5b806382227d87116100b057806382227d87146103f05780639023dc5b1461041d57806395fafddb1461045757600080fd5b8063749fb279146103ae57806379b8783c146103d057600080fd5b806327b62ad91161012257806346423aa71161010757806346423aa7146102ea57806361fe71c61461031757806367be6a641461034657600080fd5b806327b62ad91461029157806333d608f1146102be57600080fd5b80631e8dd78a116101535780631e8dd78a146101fc57806320ef8157146102455780632453ffa81461027c57600080fd5b80630ee27e341461016f5780631a604cba146101c5575b600080fd5b34801561017b57600080fd5b506101b261018a366004614e05565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b6040519081526020015b60405180910390f35b3480156101d157600080fd5b506000546101e9906601000000000000900460070b81565b60405160079190910b81526020016101bc565b34801561020857600080fd5b50610230610217366004614e20565b6003602052600090815260409020805460019091015482565b604080519283526020830191909152016101bc565b34801561025157600080fd5b50600054610268906301000000900462ffffff1681565b60405162ffffff90911681526020016101bc565b34801561028857600080fd5b506004546101b2565b34801561029d57600080fd5b506102b16102ac366004614e39565b61055b565b6040516101bc919061504e565b3480156102ca57600080fd5b506102d56207a12081565b60405163ffffffff90911681526020016101bc565b3480156102f657600080fd5b5061030a610305366004614e20565b6106f4565b6040516101bc9190615090565b34801561032357600080fd5b506103376103323660046150d1565b610879565b6040516101bc9392919061512e565b34801561035257600080fd5b50610389610361366004614e20565b60066020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101bc565b3480156103ba57600080fd5b506103ce6103c93660046151b0565b610b3d565b005b3480156103dc57600080fd5b506101b26103eb3660046151fd565b610e91565b3480156103fc57600080fd5b506101b261040b366004614e20565b60056020526000908152604090205481565b34801561042957600080fd5b5061043d610438366004614e20565b610ec2565b6040516101bc9e9d9c9b9a99989796959493929190615227565b34801561046357600080fd5b50610477610472366004614e39565b611070565b6040516101bc9190615325565b34801561049057600080fd5b506008546103899073ffffffffffffffffffffffffffffffffffffffff1681565b3480156104bd57600080fd5b506103ce6104cc36600461537d565b61111c565b6103ce6104df3660046153e9565b611238565b3480156104f057600080fd5b506101b260075481565b34801561050657600080fd5b50610389610515366004614e20565b600090815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b6103ce6105563660046154bb565b61194d565b60608167ffffffffffffffff81111561057657610576615524565b60405190808252806020026020018201604052801561063a57816020015b604080516101e0810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a08301526101c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816105945790505b5073ffffffffffffffffffffffffffffffffffffffff851660009081526001602052604081209192505b838110156106eb5781546106788683615582565b10156106e35760008261068b8784615582565b8154811061069b5761069b615595565b906000526020600020015490506106c46002600083815260200190815260200160002082611d78565b8483815181106106d6576106d6615595565b6020026020010181905250505b600101610664565b50509392505050565b6008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810183905260009173ffffffffffffffffffffffffffffffffffffffff169063b12dff7890602401602060405180830381865afa158015610764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078891906155c4565b1561079557506000919050565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa158015610804573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082891906155c4565b1561083557506001919050565b600082815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1661087157506003919050565b506002919050565b6060808060006108898587615582565b60045490915081111561089b57506004545b8467ffffffffffffffff8111156108b4576108b4615524565b60405190808252806020026020018201604052801561097857816020015b604080516101e0810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e08301829052610100830182905261012083018290526101408301829052610160830182905261018083018290526101a08301526101c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816108d25790505b5093508467ffffffffffffffff81111561099457610994615524565b6040519080825280602002602001820160405280156109bd578160200160208202803683370190505b5092508467ffffffffffffffff8111156109d9576109d9615524565b604051908082528060200260200182016040528015610a02578160200160208202803683370190505b509150855b81811015610b34576000610a1b88836155e6565b905060048281548110610a3057610a30615595565b9060005260206000200154858281518110610a4d57610a4d615595565b602002602001018181525050610aa760026000878481518110610a7257610a72615595565b60200260200101518152602001908152602001600020868381518110610a9a57610a9a615595565b6020026020010151611d78565b868281518110610ab957610ab9615595565b602002602001018190525060026000868381518110610ada57610ada615595565b6020026020010151815260200190815260200160002060010160149054906101000a900463ffffffff16848281518110610b1657610b16615595565b63ffffffff9092166020928302919091019091015250600101610a07565b50509250925092565b600054610100900460ff1615808015610b5d5750600054600160ff909116105b80610b775750303b158015610b77575060005460ff166001145b610c08576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610c6657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff8416610ce3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f70657270657475616c206d616e6167657220696e76616c6964000000000000006044820152606401610bff565b62ffffff8316610d4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f70657270657475616c496420696e76616c6964000000000000000000000000006044820152606401610bff565b60008054600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88161790557fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff16630100000062ffffff8616027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff16176201000060ff851602177fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff1666010000000000004267ffffffffffffffff16021790558015610e8b57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15b50505050565b60016020528160005260406000208181548110610ead57600080fd5b90600052602060002001600091509150505481565b600260208190526000918252604090912080546001820154928201546003830154600484015460058501805461ffff808716986201000088049091169762ffffff6401000000008904169773ffffffffffffffffffffffffffffffffffffffff670100000000000000820481169863ffffffff7b0100000000000000000000000000000000000000000000000000000090930483169885831698740100000000000000000000000000000000000000008704851698780100000000000000000000000000000000000000000000000088048616987c0100000000000000000000000000000000000000000000000000000000909804909516969290931694600f82810b95700100000000000000000000000000000000909304810b94900b92909190610fed906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054611019906155f9565b80156110665780601f1061103b57610100808354040283529160200191611066565b820191906000526020600020905b81548152906001019060200180831161104957829003601f168201915b505050505090508e565b60606111148383600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561110057602002820191906000526020600020905b8154815260200190600101908083116110ec575b5050505050611fb19092919063ffffffff16565b949350505050565b821580159061112a57508281145b611190576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f617272617973206d69736d6174636800000000000000000000000000000000006044820152606401610bff565b611198612090565b60005b83811015611231576112288585838181106111b8576111b8615595565b90506020028101906111ca919061564c565b8484848181106111dc576111dc615595565b90506020028101906111ee919061568a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061220892505050565b5060010161119b565b5050505050565b600087815260026020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff6401000000008504169281019290925273ffffffffffffffffffffffffffffffffffffffff67010000000000000084048116606084015263ffffffff7b0100000000000000000000000000000000000000000000000000000090940484166080840152600182015480821660a0850152740100000000000000000000000000000000000000008104851660c085015278010000000000000000000000000000000000000000000000008104851660e08501527c01000000000000000000000000000000000000000000000000000000009004909316610100830152928301549091166101208201526003820154600f81810b610140840152700100000000000000000000000000000000909104810b6101608301526004830154900b6101808201526005820180549192916101a0840191906113a9906155f9565b80601f01602080910402602001604051908101604052809291908181526020018280546113d5906155f9565b80156114225780601f106113f757610100808354040283529160200191611422565b820191906000526020600020905b81548152906001019060200180831161140557829003601f168201915b50505091909252505050604081015160005491925062ffffff908116630100000090920416146114ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f72646572206e6f7420666f756e6400000000000000000000000000000000006044820152606401610bff565b600854600080546040517f3bb510c900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90931692633bb510c992349261151f92630100000090910462ffffff16918b918b918b918b919060040161582b565b6000604051808303818588803b15801561153857600080fd5b505af115801561154c573d6000803e3d6000fd5b5050505050611559612090565b600080546601000000000000900460070b81131561158f5760005461158a906601000000000000900460070b615871565b6115a0565b6000546601000000000000900460070b5b90508160c0015163ffffffff168167ffffffffffffffff1610156115cb575060c081015163ffffffff165b600080546601000000000000900460070b128061160657506000546115f99062010000900460ff16826158af565b67ffffffffffffffff1642105b156116b057816060015173ffffffffffffffffffffffffffffffffffffffff16600060039054906101000a900462ffffff1662ffffff167faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a08b6040516116a39181526040602082018190526015908201527f63616e63656c2064656c61792072657175697265640000000000000000000000606082015260800190565b60405180910390a3611942565b816060015173ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415801561178b575060085460608301516040517f5fec5d0b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152336024820152911690635fec5d0b90604401602060405180830381865afa158015611765573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178991906155c4565b155b15611895576008546000906117f8906117bc90859073ffffffffffffffffffffffffffffffffffffffff168461344d565b8a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061359392505050565b9050826060015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f747261646572206d757374207369676e000000000000000000000000000000006044820152606401610bff565b505b6008546000546040517f7200c7c5000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff166004820152602481018b905273ffffffffffffffffffffffffffffffffffffffff90911690637200c7c590604401600060405180830381600087803b15801561191657600080fd5b505af115801561192a573d6000803e3d6000fd5b50505050611937896135af565b611942896000613807565b505050505050505050565b85806119b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f6e6f206f726465727300000000000000000000000000000000000000000000006044820152606401610bff565b6000600260008a8a60008181106119ce576119ce615595565b90506020020135815260200190815260200160002060010160149054906101000a900463ffffffff1663ffffffff1690506000600260008b8b6000818110611a1857611a18615595565b602090810292909201358352508101919091526040016000908120547b01000000000000000000000000000000000000000000000000000000900463ffffffff169150818311611a685781611a6a565b825b90506000428210611a7c576000611a86565b611a8682426155e6565b905060015b85811015611b7857600260008e8e84818110611aa957611aa9615595565b90506020020135815260200190815260200160002060010160149054906101000a900463ffffffff1663ffffffff169450600260008e8e84818110611af057611af0615595565b60209081029290920135835250810191909152604001600020547b01000000000000000000000000000000000000000000000000000000900463ffffffff169350838511611b3e5783611b40565b845b92506000428410611b52576000611b5c565b611b5c84426155e6565b9050808310611b6b5780611b6d565b825b925050600101611a8b565b508715611c22576008546000546040517f3bb510c900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90921691633bb510c9913491611bef916301000000900462ffffff16908e908e908e908e908a9060040161582b565b6000604051808303818588803b158015611c0857600080fd5b505af1158015611c1c573d6000803e3d6000fd5b50505050505b6008546000546040517ff404f2ee000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff16600482015273ffffffffffffffffffffffffffffffffffffffff9091169063f404f2ee90602401602060405180830381865afa158015611c9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc391906158d7565b821115611d2c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6f75746461746564206f7261636c6573000000000000000000000000000000006044820152606401610bff565b611d34612090565b60005b85811015611d6957611d618d8d83818110611d5457611d54615595565b905060200201358c61394a565b600101611d37565b50505050505050505050505050565b604080516101e081018252600060208201819052918101829052606080820183905260a0820183905260e082018390526101008201839052610120820183905261016082018390526101a08201526101c081019190915260018301547801000000000000000000000000000000000000000000000000810463ffffffff1660808301528354640100000000810462ffffff16835262010000810461ffff16610180840152670100000000000000900473ffffffffffffffffffffffffffffffffffffffff9081166101408401521660c0820152600583018054611e5a906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054611e86906155f9565b8015611ed35780601f10611ea857610100808354040283529160200191611ed3565b820191906000526020600020905b815481529060010190602001808311611eb657829003601f168201915b50505050506101a0820152600383810154600f81810b610100850152700100000000000000000000000000000000909104810b602080850191909152600486015490910b60e0840152845461ffff81166040808601919091526001808801547c0100000000000000000000000000000000000000000000000000000000900463ffffffff90811660a08801527b01000000000000000000000000000000000000000000000000000000909304909216606086015260008681528484529081208054610120870152908690529290915201546101608201525b92915050565b60608167ffffffffffffffff811115611fcc57611fcc615524565b604051908082528060200260200182016040528015611ff5578160200160208202803683370190505b50905060005b828110156120885784518161201085876158f0565b61201a9190615582565b101561207157848161202c85876158f0565b6120369190615582565b8151811061204657612046615595565b602002602001015182828151811061206057612060615595565b602002602001018181525050612076565b612088565b8061208081615907565b915050611ffb565b509392505050565b600854600080546040517f7cb8ff18000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff166004820152909173ffffffffffffffffffffffffffffffffffffffff1690637cb8ff1890602401602060405180830381865afa15801561210e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213291906155c4565b60008054919250660100000000000090910460070b1380156121515750805b156121a65761215f42615871565b6000805467ffffffffffffffff929092166601000000000000027fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff90921691909117905550565b600080546601000000000000900460070b1280156121c2575080155b1561220557600080547fffffffffffffffffffffffffffffffffffff0000000000000000ffffffffffff1666010000000000004267ffffffffffffffff16021790555b50565b6000612217602084018461593f565b6000546301000000900462ffffff908116911614612291576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f77726f6e67206f7264657220626f6f6b000000000000000000000000000000006044820152606401610bff565b60006122a561016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff1603612322576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c69642d7472616465720000000000000000000000000000000000006044820152606401610bff565b6123346101208401610100850161595a565b600f0b6000036123a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f696e76616c696420616d6f756e740000000000000000000000000000000000006044820152606401610bff565b426123b160c0850160a0860161597d565b63ffffffff161161241e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f696e76616c69642d646561646c696e65000000000000000000000000000000006044820152606401610bff565b61242e60c0840160a0850161597d565b63ffffffff16612444608085016060860161597d565b63ffffffff16108015612477575061245f4262093a80615582565b61246f608085016060860161597d565b63ffffffff16105b6124dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f696e76616c6964206578656320747300000000000000000000000000000000006044820152606401610bff565b6124fb6124f060a085016080860161597d565b632000000016151590565b1561257d576000612513610100850160e0860161595a565b600f0b1361257d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f696e76616c6964207472696767657220707269636500000000000000000000006044820152606401610bff565b604080516101c08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820183905261010082018390526101208201839052610140820183905261016082018390526101808201929092526101a081019190915261260060a085016080860161597d565b63ffffffff1660e0820152612618602085018561593f565b62ffffff1660408201526126346101a0850161018086016159a3565b61ffff16602082015261264f61016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff16606082015261267a60e0850160c08601614e05565b73ffffffffffffffffffffffffffffffffffffffff1660a08201526126a36101a085018561568a565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050506101a08201526126ef6101208501610100860161595a565b600f0b610140820152612708604085016020860161595a565b600f0b610160820152612722610100850160e0860161595a565b600f0b61018082015261273b60608501604086016159a3565b61ffff16815261275160c0850160a0860161597d565b63ffffffff1661010082015261276d608085016060860161597d565b63ffffffff9081166080830152421660c082015260006127916101a086018661568a565b905011156129315760085460009061280c906127c490849073ffffffffffffffffffffffffffffffffffffffff166142fc565b6127d26101a088018861568a565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061359392505050565b905073ffffffffffffffffffffffffffffffffffffffff811661288b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c69642062726f6b65722073696700000000000000000000000000006044820152606401610bff565b61289b60e0860160c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161461292f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c69642062726f6b65722073696700000000000000000000000000006044820152606401610bff565b505b60085461295790829073ffffffffffffffffffffffffffffffffffffffff16600161344d565b9150610120840135151580612970575061016084013515155b15612a2f57818461012001351415801561298f57508184610160013514155b6129f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6f726465722073656c662d6c696e6b65640000000000000000000000000000006044820152606401610bff565b6040805180820182526101208601358152610160860135602080830191825260008681526003909152929092209051815590516001909101555b6000612a436101e086016101c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff1614158015612a935750612a93612a776101e086016101c08701614e05565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b15612afc57612aaa6101e085016101c08601614e05565b600083815260066020526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555b612b0e61016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015612c09575060085473ffffffffffffffffffffffffffffffffffffffff16635fec5d0b612b7461016087016101408801614e05565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152336024820152604401602060405180830381865afa158015612be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0791906155c4565b155b15612d34576000612c1a8385613593565b905073ffffffffffffffffffffffffffffffffffffffff8116612c99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610bff565b816060015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614612d32576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c6964207369676e61747572650000000000000000000000000000006044820152606401610bff565b505b600082815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff1615612dcb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6f726465722d65786973747300000000000000000000000000000000000000006044820152606401610bff565b606081015173ffffffffffffffffffffffffffffffffffffffff16600090815260016020526040902054603211612e5e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f746f6f206d616e79206f726465727300000000000000000000000000000000006044820152606401610bff565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa158015612ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef191906155c4565b15612f58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6f726465722065786563757465640000000000000000000000000000000000006044820152606401610bff565b6008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa158015612fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612feb91906155c4565b15613052576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6f726465722063616e63656c65640000000000000000000000000000000000006044820152606401610bff565b6000828152600260208181526040928390208451815492860151948601516060870151608088015161ffff9384167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090961695909517620100009390971692909202959095177fffffffffff0000000000000000000000000000000000000000000000ffffffff1664010000000062ffffff909616959095027fffffffffff0000000000000000000000000000000000000000ffffffffffffff169490941767010000000000000073ffffffffffffffffffffffffffffffffffffffff95861602177fff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff167b0100000000000000000000000000000000000000000000000000000063ffffffff9384160217815560a085015160018201805460c088015160e08901516101008a01519489167fffffffffffffffff0000000000000000000000000000000000000000000000009093169290921774010000000000000000000000000000000000000000918716919091021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000918616919091027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16177c010000000000000000000000000000000000000000000000000000000092909416919091029290921790915561012084015191810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001692909316919091179091556101408201516101608301516fffffffffffffffffffffffffffffffff918216700100000000000000000000000000000000918316919091021760038301556101808301516004830180547fffffffffffffffffffffffffffffffff0000000000000000000000000000000016919092161790556101a0820151829190600582019061332e9082615a0d565b50600191506000905061334961016087016101408801614e05565b73ffffffffffffffffffffffffffffffffffffffff16815260208082019290925260409081016000908120805460018181018355918352848320018690556004805491820181557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910186905554858252600590935220556133d461016085016101408601614e05565b73ffffffffffffffffffffffffffffffffffffffff166133f7602086018661593f565b62ffffff167fb8ace0e652054ba9160467efc4bd026f89b793c887035f7d88d7b7b0615c4c5f61342d60e0880160c08901614e05565b848660405161343e93929190615c83565b60405180910390a35092915050565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb8184015246606082015273ffffffffffffffffffffffffffffffffffffffff85166080808301919091528351808303909101815260a09091019092528151910120600090816135178661443f565b60408051602081018590529081018290528515156060820152909150600090608001604051602081830303815290604052805190602001209050613588817f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b979650505050505050565b60008060006135a2858561457d565b91509150612088816145c2565b806135b75750565b600081815260026020908152604080832054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff168352600190915281208054909103613600575050565b60005b81548110156136ad578282828154811061361f5761361f615595565b9060005260206000200154036136a5578154829061363f906001906155e6565b8154811061364f5761364f615595565b906000526020600020015482828154811061366c5761366c615595565b90600052602060002001819055508180548061368a5761368a615cc2565b60019003818190600052602060002001600090559055603290505b600101613603565b6000838152600260208190526040822080547fff000000000000000000000000000000000000000000000000000000000000001681556001810183905590810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600381018290556004810180547fffffffffffffffffffffffffffffffff00000000000000000000000000000000169055906137526005830182614d8e565b505060008381526005602052604081205460048054919291613776906001906155e6565b8154811061378657613786615595565b60009182526020808320909101548083526005909152604080832085905587835282209190915590508060046137bd6001856155e6565b815481106137cd576137cd615595565b60009182526020909120015560048054806137ea576137ea615cc2565b600190038181906000526020600020016000905590555050505050565b600082815260066020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811690915573ffffffffffffffffffffffffffffffffffffffff168061385d57505050565b6040517f22ad65db00000000000000000000000000000000000000000000000000000000815260048101849052821515602482015260009073ffffffffffffffffffffffffffffffffffffffff8316906322ad65db906207a12090604401600060405180830381600088803b1580156138d557600080fd5b5087f1935050505080156138e7575060015b156138f0575060015b6040805173ffffffffffffffffffffffffffffffffffffffff8416815282151560208201526207a120918101919091527f1d4b3142757d5200ad5437c0348f6047f6f312b4ccadda70ecf72cd93ea1e26090606001610e82565b600082815260026020818152604080842081516101c081018352815461ffff8082168352620100008204169482019490945262ffffff6401000000008504169281019290925273ffffffffffffffffffffffffffffffffffffffff67010000000000000084048116606084015263ffffffff7b0100000000000000000000000000000000000000000000000000000090940484166080840152600182015480821660a0850152740100000000000000000000000000000000000000008104851660c085015278010000000000000000000000000000000000000000000000008104851660e08501527c01000000000000000000000000000000000000000000000000000000009004909316610100830152928301549091166101208201526003820154600f81810b610140840152700100000000000000000000000000000000909104810b6101608301526004830154900b6101808201526005820180549192916101a084019190613abb906155f9565b80601f0160208091040260200160405190810160405280929190818152602001828054613ae7906155f9565b8015613b345780601f10613b0957610100808354040283529160200191613b34565b820191906000526020600020905b815481529060010190602001808311613b1757829003601f168201915b50505091909252505050606081015190915073ffffffffffffffffffffffffffffffffffffffff8116613bc3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6f72646572206e6f7420666f756e6400000000000000000000000000000000006044820152606401610bff565b73ffffffffffffffffffffffffffffffffffffffff8316610120830152613be984614775565b15613c50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f64706379206e6f742066756c66696c6c656400000000000000000000000000006044820152606401610bff565b600080836080015163ffffffff16421015613cc7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6578656320746f6f206561726c790000000000000000000000000000000000006044820152606401610bff565b6008546040517fa83679b200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063a83679b290613d1f9087903390600401615cf1565b6020604051808303816000875af1925050508015613d78575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252613d75918101906155c4565b60015b6140f257613d84615d29565b806308c379a0036140e65750613d98615d90565b80613da357506140e8565b613de2816040518060400160405280602081526020017f547261646520616d743e6d617820616d7420666f72207472616465722f414d4d81525061491d565b15613df0576001925061407e565b613e2f816040518060400160405280600e81526020017f64656c617920726571756972656400000000000000000000000000000000000081525061491d565b80613fae575060e0850151631000000016158015613fae5750613e87816040518060400160405280601081526020017f6d61726b657420697320636c6f7365640000000000000000000000000000000081525061491d565b80613ecc5750613ecc816040518060400160405280601481526020017f7472696767657220636f6e64206e6f74206d657400000000000000000000000081525061491d565b80613fae575060e0850151634000000016158015613fae5750613f24816040518060400160405280601381526020017f747261646520697320636c6f7365206f6e6c790000000000000000000000000081525061491d565b80613f695750613f69816040518060400160405280601381526020017f70726963652065786365656473206c696d69740000000000000000000000000081525061491d565b80613fae5750613fae816040518060400160405280601081526020017f6f75746461746564206f7261636c65730000000000000000000000000000000081525061491d565b15613fe757806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bff9190615e38565b614026816040518060400160405280600f81526020017f6f726465722063616e63656c6c6564000000000000000000000000000000000081525061491d565b8061406b575061406b816040518060400160405280600e81526020017f6f7264657220657865637574656400000000000000000000000000000000000081525061491d565b15614079576000925061407e565b600192505b60005460405173ffffffffffffffffffffffffffffffffffffffff8616916301000000900462ffffff16907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a0906140d8908b908690615e4b565b60405180910390a3506142e0565b505b3d6000803e3d6000fd5b80614248574285610100015163ffffffff16116141a65760005460405173ffffffffffffffffffffffffffffffffffffffff86169162ffffff630100000090910416907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a090614199908b81526040602082018190526008908201527f646561646c696e65000000000000000000000000000000000000000000000000606082015260800190565b60405180910390a361423f565b60005460405173ffffffffffffffffffffffffffffffffffffffff86169162ffffff630100000090910416907faabfe27de8303a4c68aa0a50788ef1207117ec13d2fba01e5b1c0750705988a090614236908b81526040602082018190526013908201527f70726963652065786365656473206c696d697400000000000000000000000000606082015260800190565b60405180910390a35b600192506142de565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810189905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156142b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142db91906155c4565b91505b505b6142ea8683614944565b6142f48682613807565b505050505050565b604080518082018252601781527f50657270657475616c205472616465204d616e6167657200000000000000000060209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f17c95922bd75dac3f3356997c3388ddf83efb4b0267c590453d691334c1c5bbb8184015246606082015273ffffffffffffffffffffffffffffffffffffffff84166080808301919091528351808303909101815260a09091019092528151910120600090816143c685614b43565b9050600082826040516020016143e6929190918252602082015260400190565b604051602081830303815290604052805190602001209050614435817f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b9695505050505050565b6000807fe5599e89712387846e6878c8cac8abdf5d6051ccbc4a6cfa5efe389720300ec860001b8360400151846020015185606001518660a001518761014001518861016001518961018001518a61010001518b60e001518c600001518d608001516040516020016145409c9b9a999897969594939291909b8c5262ffffff9a909a1660208c015261ffff98891660408c015273ffffffffffffffffffffffffffffffffffffffff97881660608c01529590961660808a0152600f93840b60a08a015291830b60c089015290910b60e087015263ffffffff908116610100870152918216610120860152909116610140840152166101608201526101800190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b60008082516041036145b35760208301516040840151606085015160001a6145a787828585614bd6565b945094505050506145bb565b506000905060025b9250929050565b60008160048111156145d6576145d6615061565b036145de5750565b60018160048111156145f2576145f2615061565b03614659576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610bff565b600281600481111561466d5761466d615061565b036146d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610bff565b60038160048111156146e8576146e8615061565b03612205576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610bff565b60008181526003602052604081208054156147935750600092915050565b600181015480158061483357506008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa15801561480f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061483391906155c4565b806148cc57506008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156148a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148cc91906155c4565b806149045750600081815260026020526040902054670100000000000000900473ffffffffffffffffffffffffffffffffffffffff16155b15614913575060009392505050565b5060019392505050565b60008151835114801561493d575081805190602001208380519060200120145b9392505050565b61494d826135af565b6008546040517f4bbec6670000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff90911690634bbec66790602401602060405180830381865afa1580156149bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149e091906155c4565b158015614a7d57506008546040517fb12dff780000000000000000000000000000000000000000000000000000000081526004810184905273ffffffffffffffffffffffffffffffffffffffff9091169063b12dff7890602401602060405180830381865afa158015614a57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a7b91906155c4565b155b15614b1c576008546000546040517f7200c7c5000000000000000000000000000000000000000000000000000000008152630100000090910462ffffff1660048201526024810184905273ffffffffffffffffffffffffffffffffffffffff90911690637200c7c590604401600060405180830381600087803b158015614b0357600080fd5b505af1158015614b17573d6000803e3d6000fd5b505050505b8015614b2b57614b2b82614cc5565b50600090815260036020526040812081815560010155565b6000807f1aae56290d242a9c761ca2ef80072ffe2a6171793ad9f88e04426b2acc5e730d60001b83604001518460200151856060015186610100015160405160200161454095949392919094855262ffffff93909316602085015261ffff91909116604084015273ffffffffffffffffffffffffffffffffffffffff16606083015263ffffffff16608082015260a00190565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115614c0d5750600090506003614cbc565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015614c61573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116614cb557600060019250925050614cbc565b9150600090505b94509492505050565b600080614cd183614cfd565b90925090508115614ce757614ce7826000614944565b8015614cf857614cf8816000614944565b505050565b60008181526003602052604081208190614d178154151590565b614d275750600093849350915050565b80546000818152600360205260409020600101548514614d45575060005b6001820154614d5957946000945092505050565b6001808301546000818152600360205260409020909101548614614d835750946000945092505050565b909590945092505050565b508054614d9a906155f9565b6000825580601f10614daa575050565b601f01602090049060005260206000209081019061220591905b80821115614dd85760008155600101614dc4565b5090565b803573ffffffffffffffffffffffffffffffffffffffff81168114614e0057600080fd5b919050565b600060208284031215614e1757600080fd5b61493d82614ddc565b600060208284031215614e3257600080fd5b5035919050565b600080600060608486031215614e4e57600080fd5b614e5784614ddc565b95602085013595506040909401359392505050565b6000815180845260005b81811015614e9257602081850181015186830182015201614e76565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b600081518084526020808501808196508360051b8101915082860160005b858110156150415782840389528151805162ffffff1685526101e086820151614f1b88880182600f0b9052565b5060408281015161ffff169087015260608083015163ffffffff9081169188019190915260808084015182169088015260a0808401519091169087015260c08083015173ffffffffffffffffffffffffffffffffffffffff169087015260e080830151600f81900b82890152505061010080830151614f9e82890182600f0b9052565b505061012082810151908701526101408083015173ffffffffffffffffffffffffffffffffffffffff169087015261016080830151908701526101808083015161ffff16908701526101a080830151818801839052614fff83890182614e6c565b925050506101c080830151925061502d8188018473ffffffffffffffffffffffffffffffffffffffff169052565b509986019994505090840190600101614eee565b5091979650505050505050565b60208152600061493d6020830184614ed0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208101600483106150cb577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b600080604083850312156150e457600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561512357815187529582019590820190600101615107565b509495945050505050565b6060815260006151416060830186614ed0565b60208382038185015261515482876150f3565b8481036040860152855180825282870193509082019060005b8181101561518f57845163ffffffff168352938301939183019160010161516d565b509098975050505050505050565b803562ffffff81168114614e0057600080fd5b6000806000606084860312156151c557600080fd5b6151ce84614ddc565b92506151dc6020850161519d565b9150604084013560ff811681146151f257600080fd5b809150509250925092565b6000806040838503121561521057600080fd5b61521983614ddc565b946020939093013593505050565b61ffff8f811682528e16602082015262ffffff8d16604082015273ffffffffffffffffffffffffffffffffffffffff8c16606082015263ffffffff8b16608082015273ffffffffffffffffffffffffffffffffffffffff8a1660a082015263ffffffff891660c082015263ffffffff881660e082015263ffffffff871661010082015273ffffffffffffffffffffffffffffffffffffffff86166101208201526152d7610140820186600f0b9052565b6152e7610160820185600f0b9052565b6152f7610180820184600f0b9052565b6101c06101a082015260006153106101c0830184614e6c565b90509f9e505050505050505050505050505050565b60208152600061493d60208301846150f3565b60008083601f84011261534a57600080fd5b50813567ffffffffffffffff81111561536257600080fd5b6020830191508360208260051b85010111156145bb57600080fd5b6000806000806040858703121561539357600080fd5b843567ffffffffffffffff808211156153ab57600080fd5b6153b788838901615338565b909650945060208701359150808211156153d057600080fd5b506153dd87828801615338565b95989497509550505050565b60008060008060008060006080888a03121561540457600080fd5b87359650602088013567ffffffffffffffff8082111561542357600080fd5b818a0191508a601f83011261543757600080fd5b81358181111561544657600080fd5b8b602082850101111561545857600080fd5b6020830198508097505060408a013591508082111561547657600080fd5b6154828b838c01615338565b909650945060608a013591508082111561549b57600080fd5b506154a88a828b01615338565b989b979a50959850939692959293505050565b60008060008060008060006080888a0312156154d657600080fd5b873567ffffffffffffffff808211156154ee57600080fd5b6154fa8b838c01615338565b909950975087915061550e60208b01614ddc565b965060408a013591508082111561547657600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115611fab57611fab615553565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156155d657600080fd5b8151801515811461493d57600080fd5b81810381811115611fab57611fab615553565b600181811c9082168061560d57607f821691505b602082108103615646577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe2183360301811261568057600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126156bf57600080fd5b83018035915067ffffffffffffffff8211156156da57600080fd5b6020019150368190038213156145bb57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561504157828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe188360301811261579157600080fd5b8701858101903567ffffffffffffffff8111156157ad57600080fd5b8036038213156157bc57600080fd5b6157c78682846156ef565b9a87019a9550505090840190600101615752565b818352600060208085019450826000805b8681101561581f57823567ffffffffffffffff811680821461580c578384fd5b89525096830196918301916001016157ec565b50959695505050505050565b62ffffff8716815260806020820152600061584a608083018789615738565b828103604084015261585d8186886157db565b915050826060830152979650505050505050565b60008160070b7fffffffffffffffffffffffffffffffffffffffffffffffff800000000000000081036158a6576158a6615553565b60000392915050565b67ffffffffffffffff8181168382160190808211156158d0576158d0615553565b5092915050565b6000602082840312156158e957600080fd5b5051919050565b8082028115828204841417611fab57611fab615553565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361593857615938615553565b5060010190565b60006020828403121561595157600080fd5b61493d8261519d565b60006020828403121561596c57600080fd5b813580600f0b811461493d57600080fd5b60006020828403121561598f57600080fd5b813563ffffffff8116811461493d57600080fd5b6000602082840312156159b557600080fd5b813561ffff8116811461493d57600080fd5b601f821115614cf857600081815260208120601f850160051c810160208610156159ee5750805b601f850160051c820191505b818110156142f4578281556001016159fa565b815167ffffffffffffffff811115615a2757615a27615524565b615a3b81615a3584546155f9565b846159c7565b602080601f831160018114615a8e5760008415615a585750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556142f4565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015615adb57888601518255948401946001909101908401615abc565b5085821015615b1757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b805161ffff16825260006101c06020830151615b49602086018261ffff169052565b506040830151615b60604086018262ffffff169052565b506060830151615b88606086018273ffffffffffffffffffffffffffffffffffffffff169052565b506080830151615ba0608086018263ffffffff169052565b5060a0830151615bc860a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c0830151615be060c086018263ffffffff169052565b5060e0830151615bf860e086018263ffffffff169052565b506101008381015163ffffffff16908501526101208084015173ffffffffffffffffffffffffffffffffffffffff169085015261014080840151600f81900b82870152505061016080840151615c5282870182600f0b9052565b505061018080840151615c6982870182600f0b9052565b50506101a080840151828287015261443583870182614e6c565b73ffffffffffffffffffffffffffffffffffffffff84168152606060208201526000615cb26060830185615b27565b9050826040830152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b604081526000615d046040830185615b27565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b600060033d1115615d425760046000803e5060005160e01c5b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116810181811067ffffffffffffffff82111715615d8957615d89615524565b6040525050565b600060443d1015615d9e5790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff8160248401118184111715615dec57505050505090565b8285019150815181811115615e045750505050505090565b843d8701016020828501011115615e1e5750505050505090565b615e2d60208286010187615d45565b509095945050505050565b60208152600061493d6020830184614e6c565b8281526040602082015260006111146040830184614e6c56fea2646970667358221220d4e6ece2bdb158e526398e762820c9306f8cac22f3547ca4f87fb10385c8b18864736f6c63430008150033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.