MON Price: $0.019027 (+1.23%)

Contract

0x89B9274e775c386f9860Af792BB1684328f4bf50

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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

Contract Source Code Verified (Exact Match)

Contract Name:
TradingOpenFacet

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../../utils/TransferHelper.sol";
import "../security/OnlySelf.sol";
import "../interfaces/IFeeManager.sol";
import "../interfaces/ITradingOpen.sol";
import "../interfaces/ITradingChecker.sol";
import "../interfaces/IHookManager.sol";
import {ITradingConfig} from "../interfaces/ITradingConfig.sol";
import "../libraries/LibTrading.sol";

contract TradingOpenFacet is ITradingOpen, OnlySelf {
    using TransferHelper for address;

    function limitOrderDeal(LimitOrderDeal memory order, uint256 marketPrice) external override onlySelf {
        LibTrading.TradingStorage storage ts = LibTrading.tradingStorage();

        LibTrading.increaseOpenTradeAmount(ts, order.tokenIn, order.margin);
        // update fundingFee
        int256 longAccFundingFeePerShare = ITradingCore(address(this))
            .updatePairPositionInfo(order.pairBase, order.entryPrice, marketPrice, order.qty, order.isLong, true);

        bytes32[] storage tradeHashes = ts.userOpenTradeHashes[order.user];
        uint24 broker = IFeeManager(address(this)).chargeOpenFee(order.tokenIn, order.openFee, order.broker);
        OpenTrade memory ot = OpenTrade(
            order.user,
            uint32(tradeHashes.length),
            order.entryPrice,
            order.pairBase,
            order.tokenIn,
            order.margin,
            order.stopLoss,
            order.takeProfit,
            broker,
            order.isLong,
            order.openFee,
            longAccFundingFeePerShare,
            order.executionFee,
            uint40(block.timestamp),
            order.qty,
            IPairsManager(address(this)).getPairHoldingFeeRate(order.pairBase, order.isLong),
            Constants.currentBlockNumber()
        );
        ts.openTrades[order.orderHash] = ot;
        tradeHashes.push(order.orderHash);
        emit OpenMarketTrade(ot.user, order.orderHash, ot);
        IHookManager(address(this)).afterMarketExecuted(order.orderHash, ot);
    }

    function marketTradeCallback(bytes32 tradeHash, uint256 upperPrice, uint256 lowerPrice) external override onlySelf {
        LibTrading.TradingStorage storage ts = LibTrading.tradingStorage();
        ITrading.PendingTrade memory pt = ts.pendingTrades[tradeHash];
        uint256 marketPrice = pt.isLong ? upperPrice : lowerPrice;
        (bool successful, ITradingChecker.Refund refund, bytes memory data) =
            ITradingChecker(address(this)).marketTradeCallbackCheck(pt, marketPrice);
        if (!successful) {
            (address tokenIn, address user, uint256 amountIn) = (pt.tokenIn, pt.user, pt.amountIn);
            // clear pending data
            ts.pendingTradeAmountIns[tokenIn] -= amountIn;
            delete ts.pendingTrades[tradeHash];

            tokenIn.transfer(user, amountIn);
            emit PendingTradeRefund(user, tradeHash, refund);
            IHookManager(address(this)).afterMarketRefund(tradeHash, user, refund);
        } else {
            (uint96 openFee, uint96 executionFee, uint64 entryPrice, uint64 takeProfit) =
                abi.decode(data, (uint96, uint96, uint64, uint64));
            pt.takeProfit = takeProfit;
            _marketTradeDeal(ts, pt, tradeHash, openFee, executionFee, marketPrice, entryPrice);
            // clear pending data
            ts.pendingTradeAmountIns[pt.tokenIn] -= pt.amountIn;
            address tokenIn = pt.tokenIn;
            delete ts.pendingTrades[tradeHash];

            tokenIn.transfer(ITradingConfig(address(this)).executionFeeReceiver(), executionFee);
        }
    }

    function _marketTradeDeal(
        LibTrading.TradingStorage storage ts,
        ITrading.PendingTrade memory pt,
        bytes32 tradeHash,
        uint96 openFee,
        uint96 executionFee,
        uint256 marketPrice,
        uint64 entryPrice
    ) private {
        uint96 margin = uint96(pt.amountIn) - openFee - executionFee;
        LibTrading.increaseOpenTradeAmount(ts, pt.tokenIn, margin);
        // update fundingFee
        int256 longAccFundingFeePerShare = ITradingCore(address(this))
            .updatePairPositionInfo(pt.pairBase, entryPrice, marketPrice, pt.qty, pt.isLong, true);

        uint24 broker = IFeeManager(address(this)).chargeOpenFee(pt.tokenIn, openFee, pt.broker);
        bytes32[] storage tradeHashes = ts.userOpenTradeHashes[pt.user];
        OpenTrade memory ot = OpenTrade(
            pt.user,
            uint32(tradeHashes.length),
            entryPrice,
            pt.pairBase,
            pt.tokenIn,
            margin,
            pt.stopLoss,
            pt.takeProfit,
            broker,
            pt.isLong,
            openFee,
            longAccFundingFeePerShare,
            executionFee,
            uint40(block.timestamp),
            pt.qty,
            IPairsManager(address(this)).getPairHoldingFeeRate(pt.pairBase, pt.isLong),
            Constants.currentBlockNumber()
        );
        ts.openTrades[tradeHash] = ot;
        tradeHashes.push(tradeHash);
        emit OpenMarketTrade(pt.user, tradeHash, ot);
        IHookManager(address(this)).afterMarketExecuted(tradeHash, ot);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../dependencies/IWBNB.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library TransferHelper {
    using Address for address payable;
    using SafeERC20 for IERC20;

    uint256 public constant MONAD = 143;
    address public constant MONAD_WRAPPED = 0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A;

    function transfer(address token, address to, uint256 amount) internal {
        (address wrapped, bool hasWrapped) = _nativeWrappedIfAvailable();
        if (!hasWrapped || token != wrapped || _isContract(to)) {
            IERC20(token).safeTransfer(to, amount);
        } else {
            IWBNB(token).withdraw(amount);
            payable(to).sendValue(amount);
        }
    }

    function transferFrom(address token, address from, uint256 amount) internal {
        (address wrapped, bool hasWrapped) = _nativeWrappedIfAvailable();
        if (!hasWrapped || token != wrapped) {
            IERC20(token).safeTransferFrom(from, address(this), amount);
        } else {
            require(msg.value >= amount, "insufficient transfers");
            IWBNB(token).deposit{value: amount}();
        }
    }

    function nativeWrapped() internal view returns (address) {
        (address wrapped, bool hasWrapped) = _nativeWrappedIfAvailable();
        if (!hasWrapped) {
            revert("unsupported chain id");
        }
        return wrapped;
    }

    function _nativeWrappedIfAvailable() private view returns (address wrapped, bool hasWrapped) {
        uint256 chainId = block.chainid;
        if (chainId == MONAD) {
            return (MONAD_WRAPPED, true);
        }
        return (address(0), false);
    }

    function _isContract(address account) private view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }
}

File 3 of 33 : OnlySelf.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

abstract contract OnlySelf {
    // Functions that add the onlySelf modifier can eliminate many basic parameter checks, such as address != address(0), etc.
    modifier onlySelf() {
        require(msg.sender == address(this), "only self call");
        _;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../libraries/LibFeeManager.sol";
import "./IPairsManager.sol";
import {CommissionInfo} from "./IBrokerManager.sol";

interface IFeeManager {
    event AddFeeConfig(
        uint16 indexed index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP, string name
    );
    event RemoveFeeConfig(uint16 indexed index);
    event UpdateFeeConfig(uint16 indexed index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP);
    event SetDaoRepurchase(address indexed oldDaoRepurchase, address daoRepurchase);
    event SetRevenueAddress(address indexed oldRevenueAddress, address revenueAddress);
    event OpenFee(
        address indexed token,
        uint256 totalFee,
        uint256 daoAmount,
        uint24 brokerId,
        uint256 brokerAmount,
        uint256 alpPoolAmount
    );
    event CloseFee(
        address indexed token,
        uint256 totalFee,
        uint256 daoAmount,
        uint24 brokerId,
        uint256 brokerAmount,
        uint256 alpPoolAmount
    );
    event PredictionOpenFee(
        address indexed token,
        uint256 totalFee,
        uint256 daoAmount,
        uint24 brokerId,
        uint256 brokerAmount,
        uint256 alpPoolAmount
    );
    event PredictionCloseFee(
        address indexed token,
        uint256 totalFee,
        uint256 daoAmount,
        uint24 brokerId,
        uint256 brokerAmount,
        uint256 alpPoolAmount
    );
    event WithdrawRevenue(address indexed token, address indexed operator, uint256 amount);

    struct FeeDetail {
        // total accumulated fees, include DAO/referral fee
        uint256 total;
        // accumulated DAO repurchase funds
        uint256 daoAmount;
        uint256 brokerAmount;
        uint256 alpPoolAmount;
    }

    function addFeeConfig(
        uint16 index,
        string calldata name,
        uint16 openFeeP,
        uint16 closeFeeP,
        uint24 shareP,
        uint24 minCloseFeeP
    ) external;

    function removeFeeConfig(uint16 index) external;

    function updateFeeConfig(uint16 index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP)
        external;

    function setDaoRepurchase(address daoRepurchase) external;

    function setRevenueAddress(address revenueAddress) external;

    function getFeeConfigByIndex(uint16 index)
        external
        view
        returns (LibFeeManager.FeeConfig memory, IPairsManager.PairSimple[] memory);

    function getFeeDetails(address[] calldata tokens) external view returns (FeeDetail[] memory);

    function feeAddress() external view returns (address daoRepurchase, address revenueAddress);

    function revenues(address[] calldata tokens) external view returns (CommissionInfo[] memory);

    function chargeOpenFee(address token, uint256 openFee, uint24 broker) external returns (uint24);

    function chargePredictionOpenFee(address token, uint256 openFee, uint24 broker) external returns (uint24);

    function chargeCloseFee(address token, uint256 closeFee, uint24 broker) external;

    function chargePredictionCloseFee(address token, uint256 closeFee, uint24 broker) external;

    function withdrawRevenue(address[] calldata tokens) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./ITrading.sol";
import "./ITradingChecker.sol";

struct LimitOrderDeal {
    bytes32 orderHash;
    address user;
    uint64 entryPrice;
    address pairBase;
    address tokenIn;
    uint96 margin;
    uint64 stopLoss;
    uint64 takeProfit;
    uint24 broker;
    bool isLong;
    uint96 openFee;
    uint96 executionFee;
    uint80 qty;
}

interface ITradingOpen is ITrading {
    event PendingTradeRefund(address indexed user, bytes32 indexed tradeHash, ITradingChecker.Refund refund);
    event OpenMarketTrade(address indexed user, bytes32 indexed tradeHash, OpenTrade ot);

    function limitOrderDeal(LimitOrderDeal memory, uint256 marketPrice) external;

    function marketTradeCallback(bytes32 tradeHash, uint256 upperPrice, uint256 lowerPrice) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./IBook.sol";
import {LimitOrder, ILimitOrder} from "./ILimitOrder.sol";
import "./ITrading.sol";

interface ITradingChecker {
    enum Refund {
        NO,
        SWITCH,
        PAIR_STATUS,
        AMOUNT_IN,
        USER_PRICE,
        MIN_NOTIONAL_USD,
        MAX_NOTIONAL_USD,
        MAX_LEVERAGE,
        TP,
        SL,
        PAIR_OI,
        OPEN_LOST,
        SYSTEM,
        FEED_DELAY,
        PRICE_PROTECTION
    }

    function checkSl(bool isLong, uint256 stopLoss, uint256 entryPrice) external pure returns (bool);

    function openLimitOrderCheck(IBook.OpenDataInput calldata data) external view;

    /// @return successful
    /// @return refund
    /// @return data (uint96 openFee, uint96 executionFee, uint64 takeProfit)
    function executeLimitOrderCheck(LimitOrder calldata order, uint256 marketPrice)
        external
        view
        returns (bool successful, Refund refund, bytes memory data);

    function availableTakeProfit(ITrading.OpenTrade calldata) external view returns (uint64 takeProfit);

    function openMarketTradeCheck(IBook.OpenDataInput calldata data) external view;

    /// @return successful
    /// @return refund
    /// @return data (uint96 openFee, uint96 executionFee, uint64 entryPrice, uint64 takeProfit)
    function marketTradeCallbackCheck(ITrading.PendingTrade calldata pt, uint256 marketPrice)
        external
        view
        returns (bool successful, Refund refund, bytes memory data);

    function executeLiquidateCheck(ITrading.OpenTrade calldata ot, uint256 marketPrice, uint256 closePrice)
        external
        view
        returns (bool needLiq, int256 pnl, int256 fundingFee, uint256 closeFee, uint256 holdingFee);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "./ITrading.sol";
import {CloseInfo} from "./ITradingClose.sol";
import "./ITradingChecker.sol";

struct PartnerInfo {
    string name;
    string url;
    address protocolAddress;
    address callbackReceiver;
    uint256 gasCost;
}

interface IHookManager {
    event UpdatePartnerName(address indexed protocolAddress, string name);
    event UpdatePartnerUrl(address indexed protocolAddress, string url);
    event UpdatePartnerCallbackReceiver(address indexed protocolAddress, address callbackReceiver);

    function addPartner(string calldata name, string calldata url, address protocolAddress, address callbackReceiver)
        external;

    function updatePartnerName(address protocolAddress, string calldata name) external;

    function updatePartnerUrl(address protocolAddress, string calldata url) external;

    function updatePartnerCallbackReceiver(address protocolAddress, address callbackReceiver) external;

    function getPartnerByAddress(address protocolAddress)
        external
        view
        returns (uint256 blockNumber, PartnerInfo memory);

    function partners(uint256 start, uint8 length) external view returns (uint256 blockNumber, PartnerInfo[] memory);

    function afterMarketExecuted(bytes32 tradeHash, ITrading.OpenTrade memory ot) external;

    function afterMarketRefund(bytes32 tradeHash, address user, ITradingChecker.Refund refund) external;

    function afterClose(bytes32 tradeHash, address user, CloseInfo memory info) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

struct MaxTpRatioForLeverage {
    uint16 leverage;
    uint24 maxTakeProfitP;
}

interface ITradingConfig {
    event SetTradeSwitches(uint16 indexed oldTradeSwitches, uint16 indexed tradeSwitches);
    event SetExecutionFeeUsd(uint256 oldExecutionFeeUsd, uint256 executionFeeUsd);
    event SetMinNotionalUsd(uint256 oldMinNotionalUsd, uint256 minNotionalUsd);
    event SetMaxTakeProfitP(uint24 oldMaxTakeProfitP, uint24 maxTakeProfitP);
    event SetMaxTpRatioForLeverage(address indexed pairBase, MaxTpRatioForLeverage[] maxTpRatios);
    event SetExecutionFeeReceiver(address receiver);

    /*
    |-----------> 8 bit <-----------|
    |---|---|---|---|---|---|---|---|
    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
    |---|---|---|---|---|---|---|---|
    */
    enum TradingSwitch {
        LIMIT_ORDER,
        EXECUTE_LIMIT_ORDER,
        MARKET_TRADING,
        USER_CLOSE_TRADING,
        TP_SL_CLOSE_TRADING,
        LIQUIDATE_TRADING,
        PREDICTION_BET,
        PREDICTION_SETTLE
    }

    struct TradingConfig {
        uint256 executionFeeUsd;
        uint256 minNotionalUsd;
        uint24 maxTakeProfitP;
        bool limitOrder;
        bool executeLimitOrder;
        bool marketTrading;
        bool userCloseTrading;
        bool tpSlCloseTrading;
        bool liquidateTrading;
    }

    struct PriceConfig {
        address pairBase;
        uint64 upperPrice; // 1e8
        uint64 lowerPrice; // 1e8
    }

    struct PredictionConfig {
        uint256 minBetUsd;
        bool predictionBet;
        bool predictionSettle;
    }

    function getTradingConfig() external view returns (TradingConfig memory);

    function getPredictionConfig() external view returns (PredictionConfig memory);

    function setTradingSwitches(
        bool limitOrder,
        bool executeLimitOrder,
        bool marketTrade,
        bool userCloseTrade,
        bool tpSlCloseTrade,
        bool liquidateTrade,
        bool predictBet,
        bool predictSettle
    ) external;

    function setExecutionFeeUsd(uint256 executionFeeUsd) external;

    function setMinNotionalUsd(uint256 minNotionalUsd) external;

    function setMinBetUsd(uint256 minBetUsd) external;

    function setMaxTakeProfitP(uint24 maxTakeProfitP) external;

    function setMaxTpRatioForLeverage(address pairBase, MaxTpRatioForLeverage[] calldata maxTpRatios) external;

    function setExecutionFeeReceiver(address receiver) external;

    function executionFeeReceiver() external view returns (address);

    function getPairMaxTpRatios(address pairBase) external view returns (MaxTpRatioForLeverage[] memory);

    function getPairMaxTpRatio(address pairBase, uint256 leverage_10000) external view returns (uint24);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../../utils/Constants.sol";
import "../interfaces/IVault.sol";
import "../interfaces/ITrading.sol";
import "../interfaces/ITradingCore.sol";
import "../interfaces/IPairsManager.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import {ZERO, ONE, UC, uc, into} from "unchecked-counter/src/UC.sol";

library LibTrading {
    using Math for uint256;

    bytes32 constant TRADING_POSITION = keccak256("apollox.trading.storage");

    struct TradingStorage {
        uint256 salt;
        //--------------- pending ---------------
        // tradeHash =>
        mapping(bytes32 => ITrading.PendingTrade) pendingTrades;
        // margin.tokenIn => total amount of all pending trades
        mapping(address => uint256) pendingTradeAmountIns;
        //--------------- open ---------------
        // tradeHash =>
        mapping(bytes32 => ITrading.OpenTrade) openTrades;
        // user => tradeHash[]
        mapping(address => bytes32[]) userOpenTradeHashes;
        // tokenIn =>
        mapping(address => uint256) openTradeAmountIns;
        // tokenIn[]
        address[] openTradeTokenIns;
    }

    function tradingStorage() internal pure returns (TradingStorage storage ts) {
        bytes32 position = TRADING_POSITION;
        assembly {
            ts.slot := position
        }
    }

    function calcFundingFee(ITrading.OpenTrade memory ot, IVault.MarginToken memory mt, uint256 marketPrice)
        internal
        view
        returns (int256 fundingFee)
    {
        int256 longAccFundingFeePerShare = ITradingCore(address(this)).lastLongAccFundingFeePerShare(ot.pairBase);
        return calcFundingFee(ot, mt, marketPrice, longAccFundingFeePerShare);
    }

    function calcFundingFee(
        ITrading.OpenTrade memory ot,
        IVault.MarginToken memory mt,
        uint256 marketPrice,
        int256 longAccFundingFeePerShare
    ) internal pure returns (int256 fundingFee) {
        int256 fundingFeeUsd;
        if (ot.isLong) {
            fundingFeeUsd =
                int256(ot.qty * marketPrice) * (longAccFundingFeePerShare - ot.longAccFundingFeePerShare) / 1e18;
        } else {
            fundingFeeUsd =
                int256(ot.qty * marketPrice) * (longAccFundingFeePerShare - ot.longAccFundingFeePerShare) * (-1) / 1e18;
        }
        fundingFee = fundingFeeUsd * int256(10 ** mt.decimals) / int256(1e10 * mt.price);
        return fundingFee;
    }

    function increaseOpenTradeAmount(TradingStorage storage ts, address token, uint256 amount) internal {
        address[] storage tokenIns = ts.openTradeTokenIns;
        bool exists;
        for (UC i = ZERO; i < uc(tokenIns.length); i = i + ONE) {
            if (tokenIns[i.into()] == token) {
                exists = true;
                break;
            }
        }
        if (!exists) {
            tokenIns.push(token);
        }
        ts.openTradeAmountIns[token] += amount;
    }

    function calcHoldingFee(ITrading.OpenTrade storage ot, IVault.MarginToken memory mt)
        internal
        view
        returns (uint256)
    {
        uint256 holdingFee;
        if (ot.holdingFeeRate > 0 && ot.openBlock > 0) {
            // holdingFeeRate 1e12
            holdingFee = uint256(ot.entryPrice) * ot.qty * (Constants.currentBlockNumber() - ot.openBlock)
                * ot.holdingFeeRate * (10 ** mt.decimals) / uint256(1e22 * mt.price);
        }
        return holdingFee;
    }

    function calcCloseFee(
        IPairsManager.FeeConfig memory feeConfig,
        IVault.MarginToken memory mt,
        uint256 closeNotionalUsd,
        int256 pnl
    ) internal pure returns (uint256) {
        if (feeConfig.shareP > 0 && feeConfig.minCloseFeeP > 0) {
            // closeFeeUsd = max(pnlUsd * shareP, minCloseFeeP * notionalUsd)
            uint256 minCloseFeeUsd = closeNotionalUsd * feeConfig.minCloseFeeP;
            if (pnl <= 0) {
                return minCloseFeeUsd * (10 ** mt.decimals) / (1e5 * 1e10 * mt.price);
            } else {
                uint256 closeFeeUsd = uint256(pnl) * mt.price * feeConfig.shareP * 1e10 / (10 ** mt.decimals);
                return closeFeeUsd.max(minCloseFeeUsd) * (10 ** mt.decimals) / (1e5 * 1e10 * mt.price);
            }
        } else {
            return closeNotionalUsd * feeConfig.closeFeeP * (10 ** mt.decimals) / (1e4 * 1e10 * mt.price);
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IWBNB {
    function deposit() external payable;

    function withdraw(uint256) external;
}

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../../utils/Constants.sol";
import "../interfaces/IVault.sol";
import "../interfaces/IFeeManager.sol";
import "./LibBrokerManager.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library LibFeeManager {
    using SafeERC20 for IERC20;

    bytes32 constant FEE_MANAGER_STORAGE_POSITION = keccak256("apollox.fee.manager.storage");

    struct FeeConfig {
        string name;
        uint16 index;
        uint16 openFeeP; // 1e4
        uint16 closeFeeP; // 1e4
        bool enable;
        uint24 shareP; // 1e5
        uint24 minCloseFeeP; // 1e5
    }

    struct FeeManagerStorage {
        // 0/1/2/3/.../ => FeeConfig
        mapping(uint16 => FeeConfig) feeConfigs;
        // feeConfig index => pair.base[]
        mapping(uint16 => address[]) feeConfigPairs;
        // USDT/BUSD/.../ => FeeDetail
        mapping(address => IFeeManager.FeeDetail) feeDetails;
        address daoRepurchase;
        address revenueAddress;
        // USDT/BUSD/.../ => Commission
        mapping(address token => LibBrokerManager.Commission) revenues;
    }

    function feeManagerStorage() internal pure returns (FeeManagerStorage storage fms) {
        bytes32 position = FEE_MANAGER_STORAGE_POSITION;
        assembly {
            fms.slot := position
        }
    }

    event AddFeeConfig(
        uint16 indexed index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP, string name
    );
    event RemoveFeeConfig(uint16 indexed index);
    event UpdateFeeConfig(uint16 indexed index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP);
    event SetDaoRepurchase(address indexed oldDaoRepurchase, address daoRepurchase);
    event SetRevenueAddress(address indexed oldRevenueAddress, address revenueAddress);

    function initialize(address daoRepurchase, address revenueAddress) internal {
        FeeManagerStorage storage fms = feeManagerStorage();
        require(fms.daoRepurchase == address(0), "LibFeeManager: Already initialized");
        setDaoRepurchase(daoRepurchase);
        setRevenueAddress(revenueAddress);
        // default fee config
        fms.feeConfigs[0] = FeeConfig("Default Fee Rate", 0, 8, 8, true, 0, 0);
        emit AddFeeConfig(0, 8, 8, 0, 0, "Default Fee Rate");
    }

    function addFeeConfig(
        uint16 index,
        string calldata name,
        uint16 openFeeP,
        uint16 closeFeeP,
        uint24 shareP,
        uint24 minCloseFeeP
    ) internal {
        FeeManagerStorage storage fms = feeManagerStorage();
        FeeConfig storage config = fms.feeConfigs[index];
        require(!config.enable, "LibFeeManager: Configuration already exists");
        config.index = index;
        config.name = name;
        config.openFeeP = openFeeP;
        config.closeFeeP = closeFeeP;
        config.enable = true;
        config.shareP = shareP;
        config.minCloseFeeP = minCloseFeeP;
        emit AddFeeConfig(index, openFeeP, closeFeeP, shareP, minCloseFeeP, name);
    }

    function removeFeeConfig(uint16 index) internal {
        FeeManagerStorage storage fms = feeManagerStorage();
        FeeConfig storage config = fms.feeConfigs[index];
        require(config.enable, "LibFeeManager: Configuration not enabled");
        require(
            fms.feeConfigPairs[index].length == 0, "LibFeeManager: Cannot remove a configuration that is still in use"
        );
        delete fms.feeConfigs[index];
        emit RemoveFeeConfig(index);
    }

    function updateFeeConfig(uint16 index, uint16 openFeeP, uint16 closeFeeP, uint24 shareP, uint24 minCloseFeeP)
        internal
    {
        FeeManagerStorage storage fms = feeManagerStorage();
        FeeConfig storage config = fms.feeConfigs[index];
        require(config.enable, "LibFeeManager: Configuration not enabled");
        config.openFeeP = openFeeP;
        config.closeFeeP = closeFeeP;
        config.shareP = shareP;
        config.minCloseFeeP = minCloseFeeP;
        emit UpdateFeeConfig(index, openFeeP, closeFeeP, shareP, minCloseFeeP);
    }

    function setDaoRepurchase(address daoRepurchase) internal {
        require(daoRepurchase != address(0), "LibFeeManager: daoRepurchase cannot be 0 address");
        FeeManagerStorage storage fms = feeManagerStorage();
        address oldDaoRepurchase = fms.daoRepurchase;
        fms.daoRepurchase = daoRepurchase;
        emit SetDaoRepurchase(oldDaoRepurchase, daoRepurchase);
    }

    function setRevenueAddress(address revenueAddress) internal {
        require(revenueAddress != address(0), "LibFeeManager: revenueAddress cannot be 0 address");
        FeeManagerStorage storage fms = feeManagerStorage();
        address oldRevenueAddress = fms.revenueAddress;
        fms.revenueAddress = revenueAddress;
        emit SetRevenueAddress(oldRevenueAddress, revenueAddress);
    }

    function getFeeConfigByIndex(uint16 index) internal view returns (FeeConfig memory, address[] storage) {
        FeeManagerStorage storage fms = feeManagerStorage();
        return (fms.feeConfigs[index], fms.feeConfigPairs[index]);
    }

    function chargeFee(address token, uint256 feeAmount, uint24 broker)
        internal
        returns (uint24 brokerId, uint256 brokerAmount, uint256 daoAmount, uint256 alpPoolAmount)
    {
        FeeManagerStorage storage fms = feeManagerStorage();
        IFeeManager.FeeDetail storage detail = fms.feeDetails[token];
        detail.total += feeAmount;

        (brokerAmount, brokerId, daoAmount, alpPoolAmount) =
            LibBrokerManager.updateBrokerCommission(token, feeAmount, broker);
        detail.brokerAmount += brokerAmount;

        if (daoAmount > 0) {
            // The buyback address prefers to receive wrapped tokens since LPs are composed of wrapped tokens, for example: WBNB-APX LP.
            IERC20(token).safeTransfer(fms.daoRepurchase, daoAmount);
            detail.daoAmount += daoAmount;
        }

        if (alpPoolAmount > 0) {
            IVault(address(this)).increase(token, alpPoolAmount);
            detail.alpPoolAmount += alpPoolAmount;
        }

        uint256 revenue = feeAmount - brokerAmount - daoAmount - alpPoolAmount;
        if (revenue > 0) {
            LibBrokerManager.Commission storage c = fms.revenues[token];
            c.total += revenue;
            c.pending += revenue;
        }
        return (brokerId, brokerAmount, daoAmount, alpPoolAmount);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./IFeeManager.sol";
import {SlippageConfigView} from "./ISlippageManager.sol";
import "../libraries/LibPairsManager.sol";

struct PairMaxOiAndFundingFeeConfig {
    uint256 maxLongOiUsd;
    uint256 maxShortOiUsd;
    uint256 fundingFeePerBlockP;
    uint256 minFundingFeeR;
    uint256 maxFundingFeeR;
}

interface IPairsManager {
    enum PairType {
        CRYPTO,
        STOCKS,
        FOREX,
        INDICES,
        COMMODITIES,
        ALPHA
    }
    enum PairStatus {
        AVAILABLE,
        REDUCE_ONLY,
        CLOSE
    }

    struct PairSimple {
        // BTC/USD
        string name;
        // BTC address
        address base;
        PairType pairType;
        PairStatus status;
    }

    struct PairView {
        // BTC/USD
        string name;
        // BTC address
        address base;
        uint16 basePosition;
        PairType pairType;
        PairStatus status;
        uint256 maxLongOiUsd;
        uint256 maxShortOiUsd;
        uint256 fundingFeePerBlockP; // 1e18
        uint256 minFundingFeeR; // 1e18
        uint256 maxFundingFeeR; // 1e18

        LibPairsManager.LeverageMargin[] leverageMargins;

        uint16 slippageConfigIndex;
        uint16 slippagePosition;
        LibPairsManager.SlippageConfig slippageConfig;

        uint16 feeConfigIndex;
        uint16 feePosition;
        LibFeeManager.FeeConfig feeConfig;

        uint40 longHoldingFeeRate; // 1e12
        uint40 shortHoldingFeeRate; // 1e12
    }

    struct LeverageMargin {
        uint256 notionalUsd;
        uint16 maxLeverage;
        uint16 initialLostP; // 1e4
        uint16 liqLostP; // 1e4
    }

    struct FeeConfig {
        uint16 openFeeP; // 1e4
        uint16 closeFeeP; // 1e4
        uint24 shareP; // 1e5
        uint24 minCloseFeeP; // 1e5
    }

    struct TradingPair {
        // BTC address
        address base;
        string name;
        PairType pairType;
        PairStatus status;
        PairMaxOiAndFundingFeeConfig pairConfig;
        LeverageMargin[] leverageMargins;
        SlippageConfigView slippageConfig;
        FeeConfig feeConfig;
    }

    struct UpdatePairMaxOiParam {
        address base;
        uint256 maxLongOiUsd;
        uint256 maxShortOiUsd;
    }

    struct UpdatePairFundingFeeConfigParam {
        address base;
        uint256 fundingFeePerBlockP;
        uint256 minFundingFeeR;
        uint256 maxFundingFeeR;
    }

    struct UpdateLeverageParam {
        address base;
        LibPairsManager.LeverageMargin[] leverageMargins;
    }

    function addPair(
        address base,
        string calldata name,
        PairType pairType,
        PairStatus status,
        PairMaxOiAndFundingFeeConfig calldata pairConfig,
        uint16 slippageConfigIndex,
        uint16 feeConfigIndex,
        LibPairsManager.LeverageMargin[] calldata leverageMargins,
        uint40 longHoldingFeeRate,
        uint40 shortHoldingFeeRate
    ) external;

    function updatePairMaxOi(address base, uint256 maxLongOiUsd, uint256 maxShortOiUsd) external;

    function batchUpdatePairMaxOi(UpdatePairMaxOiParam[] calldata params) external;

    function updatePairHoldingFeeRate(address base, uint40 longHoldingFeeRate, uint40 shortHoldingFeeRate) external;

    function updatePairFundingFeeConfig(
        address base,
        uint256 fundingFeePerBlockP,
        uint256 minFundingFeeR,
        uint256 maxFundingFeeR
    ) external;

    function batchUpdatePairFundingFeeConfig(UpdatePairFundingFeeConfigParam[] calldata params) external;

    function removePair(address base) external;

    function updatePairStatus(address base, PairStatus status) external;

    function updatePairType(address base, PairType pairType) external;

    function batchUpdatePairStatus(PairType pairType, PairStatus status) external;

    function updatePairSlippage(address base, uint16 slippageConfigIndex) external;

    function updatePairFee(address base, uint16 feeConfigIndex) external;

    function updatePairLeverageMargin(address base, LibPairsManager.LeverageMargin[] calldata leverageMargins) external;

    function safeMode(bool openSafeMode, address[] calldata bases, UpdateLeverageParam[] calldata updateLeverages)
        external;

    function pairsV4() external view returns (PairView[] memory);

    function getPairByBaseV4(address base) external view returns (PairView memory);

    function getPairForTrading(address base) external view returns (TradingPair memory);

    function getPairConfig(address base) external view returns (PairMaxOiAndFundingFeeConfig memory);

    function getPairFeeConfig(address base) external view returns (FeeConfig memory);

    function getPairHoldingFeeRate(address base, bool isLong) external view returns (uint40 holdingFeeRate);

    function getPairSlippageConfig(address base) external view returns (SlippageConfigView memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

struct CommissionInfo {
    address token;
    uint256 total;
    uint256 pending;
}

interface IBrokerManager {
    struct BrokerInfo {
        string name;
        string url;
        address receiver;
        uint24 id;
        uint16 commissionP;
        uint16 daoShareP;
        uint16 alpPoolP;
        CommissionInfo[] commissions;
    }

    function addBroker(
        uint24 id,
        uint16 commissionP,
        uint16 daoShareP,
        uint16 alpPoolP,
        address receiver,
        string calldata name,
        string calldata url
    ) external;

    function removeBroker(uint24 id) external;

    function updateBrokerCommissionP(uint24 id, uint16 commissionP, uint16 daoShareP, uint16 alpPoolP) external;

    function updateBrokerReceiver(uint24 id, address receiver) external;

    function updateBrokerName(uint24 id, string calldata name) external;

    function updateBrokerUrl(uint24 id, string calldata url) external;

    function getBrokerById(uint24 id) external view returns (BrokerInfo memory);

    function brokers(uint256 start, uint8 length) external view returns (BrokerInfo[] memory);

    function withdrawCommission(uint24 id) external;
}

File 17 of 33 : ITrading.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface ITrading {
    struct PendingTrade {
        address user;
        uint24 broker;
        bool isLong;
        uint64 price; // 1e8
        address pairBase;
        uint96 amountIn; // tokenIn decimals
        address tokenIn;
        uint80 qty; // 1e10
        uint64 stopLoss; // 1e8
        uint64 takeProfit; // 1e8
        uint128 blockNumber;
    }

    struct OpenTrade {
        address user;
        uint32 userOpenTradeIndex;
        uint64 entryPrice; // 1e8
        address pairBase;
        address tokenIn;
        uint96 margin; // tokenIn decimals
        uint64 stopLoss; // 1e8
        uint64 takeProfit; // 1e8
        uint24 broker;
        bool isLong;
        uint96 openFee; // tokenIn decimals
        int256 longAccFundingFeePerShare; // 1e18
        uint96 executionFee; // tokenIn decimals
        uint40 timestamp;
        uint80 qty; // 1e10

        uint40 holdingFeeRate; // 1e12
        uint256 openBlock;
    }

    struct MarginBalance {
        address token;
        uint256 price;
        uint8 decimals;
        uint256 balanceUsd;
    }
}

File 18 of 33 : IBook.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IBook {
    struct OpenDataInput {
        // Pair.base
        address pairBase;
        bool isLong;
        // BUSD/USDT address
        address tokenIn;
        uint96 amountIn; // tokenIn decimals
        uint80 qty; // 1e10
        // Limit Order: limit price
        // Market Trade: worst price acceptable
        uint64 price; // 1e8
        uint64 stopLoss; // 1e8
        uint64 takeProfit; // 1e8
        uint24 broker;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./IBook.sol";
import "./ITradingChecker.sol";

struct LimitOrder {
    address user;
    uint32 userOpenOrderIndex;
    uint64 limitPrice; // 1e8
    // pair.base
    address pairBase;
    uint96 amountIn; // tokenIn decimals
    address tokenIn;
    bool isLong;
    uint24 broker;
    uint64 stopLoss; // 1e8
    uint80 qty; // 1e10
    uint64 takeProfit; // 1e8
    uint40 timestamp;
}

interface ILimitOrder is IBook {
    event OpenLimitOrder(address indexed user, bytes32 indexed orderHash, OpenDataInput data);
    event UpdateOrderTp(address indexed user, bytes32 indexed orderHash, uint256 oldTp, uint256 tp);
    event UpdateOrderSl(address indexed user, bytes32 indexed orderHash, uint256 oldSl, uint256 sl);
    event ExecuteLimitOrderRejected(address indexed user, bytes32 indexed orderHash, ITradingChecker.Refund refund);
    event LimitOrderRefund(address indexed user, bytes32 indexed orderHash, ITradingChecker.Refund refund);
    event CancelLimitOrder(address indexed user, bytes32 indexed orderHash);
    event ExecuteLimitOrderSuccessful(address indexed user, bytes32 indexed orderHash);

    struct LimitOrderView {
        bytes32 orderHash;
        string pair;
        address pairBase;
        bool isLong;
        address tokenIn;
        uint96 amountIn; // tokenIn decimals
        uint80 qty; // 1e10
        uint64 limitPrice; // 1e8
        uint64 stopLoss; // 1e8
        uint64 takeProfit; // 1e8
        uint24 broker;
        uint40 timestamp;
    }

    struct KeeperExecution {
        bytes32 hash;
        uint64 price;
    }

    struct KeeperExecutionWithPyth {
        bytes32 hash;
        bytes32 priceId;
        bytes priceUpdateData;
    }

    function openLimitOrder(OpenDataInput memory openData) external returns (bytes32 orderHash);

    function openLimitOrderBNB(OpenDataInput memory openData) external payable returns (bytes32 orderHash);

    function updateOrderTp(bytes32 orderHash, uint64 takeProfit) external;

    function updateOrderSl(bytes32 orderHash, uint64 stopLoss) external;

    // stopLoss is allowed to be equal to 0, which means the sl setting is removed.
    // takeProfit must be greater than 0
    function updateOrderTpAndSl(bytes32 orderHash, uint64 takeProfit, uint64 stopLoss) external;

    function executeLimitOrder(KeeperExecution[] calldata) external;

    function executeLimitOrder(KeeperExecutionWithPyth[] calldata) external payable;

    function cancelLimitOrder(bytes32 orderHash) external;

    function getLimitOrderByHash(bytes32 orderHash) external view returns (LimitOrderView memory);

    function getLimitOrders(address user, address pairBase) external view returns (LimitOrderView[] memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./ITrading.sol";

struct CloseInfo {
    uint64 closePrice; // 1e8
    int96 fundingFee; // tokenIn decimals
    uint96 closeFee; // tokenIn decimals
    int96 pnl; // tokenIn decimals
    uint96 holdingFee; // tokenIn decimals
}

interface ITradingClose is ITrading {
    event CloseTradeSuccessful(address indexed user, bytes32 indexed tradeHash, CloseInfo closeInfo);
    event ExecuteCloseSuccessful(
        address indexed user, bytes32 indexed tradeHash, ExecutionType executionType, CloseInfo closeInfo
    );
    event CloseTradeReceived(address indexed user, bytes32 indexed tradeHash, address indexed token, uint256 amount);
    event CloseTradeAddLiquidity(address indexed token, uint256 amount);
    event ExecuteCloseRejected(
        address indexed user,
        bytes32 indexed tradeHash,
        ExecutionType executionType,
        uint64 execPrice,
        uint64 marketPrice
    );

    enum ExecutionType {
        TP,
        SL,
        LIQ
    }

    struct TpSlOrLiq {
        bytes32 tradeHash;
        uint64 price;
        ExecutionType executionType;
    }

    struct TpSlOrLiqWithPyth {
        bytes32 tradeHash;
        ExecutionType executionType;
        bytes32 priceId;
        bytes priceUpdateData;
    }

    struct SettleToken {
        address token;
        uint256 amount;
        uint8 decimals;
    }

    function closeTradeCallback(bytes32 tradeHash, uint256 upperPrice, uint256 lowerPrice) external;

    function executeTpSlOrLiq(TpSlOrLiq[] calldata) external;

    function executeTpSlOrLiq(TpSlOrLiqWithPyth[] calldata) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "../dependencies/ArbSys.sol";

type Price8 is uint64;
type Qty10 is uint80;
type Usd18 is uint96;

library Constants {
    ArbSys public constant arbSys = ArbSys(address(100));

    function currentBlockNumber() internal view returns (uint256) {
        if (address(arbSys).code.length > 0) {
            return arbSys.arbBlockNumber();
        }
        return block.number;
    }

    /*-------------------------------- Role --------------------------------*/
    // 0x0000000000000000000000000000000000000000000000000000000000000000
    bytes32 constant DEFAULT_ADMIN_ROLE = 0x00;
    // 0xa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775
    bytes32 constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    // 0xfc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c
    bytes32 constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");
    // 0x62150a51582c26f4255242a3c4ca35fb04250e7315069523d650676aed01a56a
    bytes32 constant TOKEN_OPERATOR_ROLE = keccak256("TOKEN_OPERATOR_ROLE");
    // 0xa6fbd0d4ef0ac50b4de984ab8f303863596293cce6d67dd6111979bcf56abe74
    bytes32 constant STAKE_OPERATOR_ROLE = keccak256("STAKE_OPERATOR_ROLE");
    // 0xc24d2c87036c9189cc45e221d5dff8eaffb4966ee49ea36b4ffc88a2d85bf890
    bytes32 constant PRICE_FEED_OPERATOR_ROLE = keccak256("PRICE_FEED_OPERATOR_ROLE");
    // 0x04fcf77d802b9769438bfcbfc6eae4865484c9853501897657f1d28c3f3c603e
    bytes32 constant PAIR_OPERATOR_ROLE = keccak256("PAIR_OPERATOR_ROLE");
    // 0xfc8737ab85eb45125971625a9ebdb75cc78e01d5c1fa80c4c6e5203f47bc4fab
    bytes32 constant KEEPER_ROLE = keccak256("KEEPER_ROLE");
    // 0x4e89f34ce8e0125b1b19130806ace319a8a06b7e1b4d6ef98c0eac043b6f119a
    bytes32 constant PREDICTION_KEEPER_ROLE = keccak256("PREDICTION_KEEPER_ROLE");
    // 0x7d867aa9d791a9a4be418f90a2f248aa2c5f1348317792a6f6412f94df9819f7
    bytes32 constant PRICE_FEEDER_ROLE = keccak256("PRICE_FEEDER_ROLE");
    // 0x8227712ef8ad39d0f26f06731ef0df8665eb7ada7f41b1ee089adf3c238862a2
    bytes32 constant MONITOR_ROLE = keccak256("MONITOR_ROLE");
    // 0x35435f8e4f6e13c77fa1de984cb59ae76523c1b3d64ad415287dcd1b0a4cdc14
    bytes32 constant PYTH_PRICE_FEEDER_ROLE = keccak256("PYTH_PRICE_FEEDER_ROLE");

    /*-------------------------------- Decimals --------------------------------*/
    uint8 public constant PRICE_DECIMALS = 8;
    uint8 public constant QTY_DECIMALS = 10;
    uint8 public constant USD_DECIMALS = 18;

    uint16 public constant BASIS_POINTS_DIVISOR = 1e4;
    uint16 public constant MAX_LEVERAGE = 1e3;
    int256 public constant FUNDING_FEE_RATE_DIVISOR = 1e18;
    uint8 public constant FEED_DELAY_BLOCK = 200;
    uint8 public constant MAX_REQUESTS_PER_PAIR_IN_BLOCK = 100;
    uint256 public constant TIME_LOCK_DELAY = 24 hours;
    uint256 public constant TIME_LOCK_GRACE_PERIOD = 48 hours;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./ITradingPortal.sol";
import "./ITradingClose.sol";

/*
|-----------> 8 bit <-----------|
|---|---|---|---|---|---|---|---|
|   |   |   |   |   |   | 1 | 0 |
|---|---|---|---|---|---|---|---|
*/
enum FeatureSwitches {
    AS_MARGIN,
    AS_BET
}

struct LpItem {
    address tokenAddress;
    int256 value;
    uint8 decimals;
    int256 valueUsd; // decimals = 18
    uint16 targetWeight;
    uint16 feeBasisPoints;
    uint16 taxBasisPoints;
    bool dynamicFee;
}

interface IVault {
    event CloseTradeRemoveLiquidity(address indexed token, uint256 amount);

    struct Token {
        address tokenAddress;
        uint16 weight;
        uint16 feeBasisPoints;
        uint16 taxBasisPoints;
        bool stable;
        bool dynamicFee;
        bool asMargin;
        bool asBet;
    }

    struct MarginToken {
        address token;
        bool switchOn;
        uint8 decimals;
        uint256 price;
    }

    function addToken(
        address tokenAddress,
        uint16 feeBasisPoints,
        uint16 taxBasisPoints,
        bool stable,
        bool dynamicFee,
        bool asMargin,
        bool asBet,
        uint16[] calldata weights
    ) external;

    function removeToken(address tokenAddress, uint16[] calldata weights) external;

    function updateToken(address tokenAddress, uint16 feeBasisPoints, uint16 taxBasisPoints, bool dynamicFee) external;

    function updateTokenFeature(address tokenAddress, bool asMargin, bool asBet) external;

    function changeWeight(uint16[] calldata weights) external;

    function tokensV3() external view returns (Token[] memory);

    function getTokenByAddress(address tokenAddress) external view returns (Token memory);

    function getTokenForTrading(address tokenAddress) external view returns (MarginToken memory);

    function getTokenForPrediction(address tokenAddress) external view returns (MarginToken memory);

    function itemValue(address token) external view returns (LpItem memory lpItem);

    function totalValue() external view returns (LpItem[] memory lpItems);

    function getTotalValueUsd() external view returns (int256);

    function increase(address token, uint256 amounts) external;

    function decreaseByCloseTrade(address token, uint256 amount) external returns (ITradingClose.SettleToken[] memory);

    function decrease(address token, uint256 amount) external;

    function maxWithdrawAbleUsd() external view returns (int256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {SlippageType, SlippageConfigView} from "./ISlippageManager.sol";

interface ITradingCore {
    event UpdatePairPositionInfo(
        address indexed pairBase,
        uint256 lastBlock,
        uint256 longQty,
        uint256 shortQty,
        int256 longAccFundingFeePerShare,
        uint64 lpLongAvgPrice,
        uint64 lpShortAvgPrice
    );
    event UpdatePairAccFundingFeePerShare(
        address indexed pairBase, uint256 lastBlock, int256 longAccFundingFeePerShare, uint256 marketPrice
    );
    event AddMarginPoolBalance(address indexed funder, address indexed token, uint256 amount);

    struct PairQty {
        uint256 longQty;
        uint256 shortQty;
    }

    struct PairPositionInfo {
        uint256 lastFundingFeeBlock;
        uint256 longQty; // 1e10
        uint256 shortQty; // 1e10
        // shortAcc = longAcc * -1
        int256 longAccFundingFeePerShare; // 1e18
        uint64 lpLongAvgPrice; // 1e8
        address pairBase;
        uint16 pairIndex;
        uint64 lpShortAvgPrice;
    }

    struct LpMarginTokenUnPnl {
        address token;
        int256 unPnlUsd;
    }

    struct MarginPct {
        address token;
        uint256 pct; // 1e4
    }

    function updatePairPositionInfo(
        address pairBase,
        uint256 userPrice,
        uint256 marketPrice,
        uint256 qty,
        bool isLong,
        bool isOpen
    ) external returns (int256 longAccFundingFeePerShare);

    function updatePairPositionInfo(address pairBase) external;

    function addMarginPoolBalance(address token, uint256 amount) external payable;

    function getPairQty(address pairBase) external view returns (PairQty memory);

    function slippagePrice(address pairBase, uint256 marketPrice, uint256 qty, bool isLong)
        external
        view
        returns (uint256);

    function slippagePrice(
        PairQty memory pairQty,
        SlippageConfigView memory sc,
        uint256 marketPrice,
        uint256 qty,
        bool isLong
    ) external pure returns (uint256);

    function triggerPrice(address pairBase, uint256 limitPrice, uint256 qty, bool isLong)
        external
        view
        returns (uint256);

    function triggerPrice(
        PairQty memory pairQty,
        SlippageConfigView memory sc,
        uint256 limitPrice,
        uint256 qty,
        bool isLong
    ) external pure returns (uint256);

    function lastLongAccFundingFeePerShare(address pairBase) external view returns (int256);

    function lpUnrealizedPnlTotalUsd() external view returns (int256 totalUsd);

    function lpUnrealizedPnlUsd() external view returns (int256 totalUsd, LpMarginTokenUnPnl[] memory);

    function lpUnrealizedPnlUsd(address targetToken) external view returns (int256 totalUsd, int256 tokenUsd);

    function lpNotionalUsd() external view returns (uint256);
}

// 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 25 of 33 : UC.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*//////////////////////////////////////////////////////////////////////////
                                TYPE DEFINITION
//////////////////////////////////////////////////////////////////////////*/

/// @notice Counter type that bypasses checked arithmetic, designed to be used in for loops.
/// @dev Here's an example:
///
/// ```
/// for (UC i = ZERO; i < uc(100); i = i + ONE) {
///   i.into(); // or `i.unwrap()`
/// }
/// ```
type UC is uint256;

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

// Exports 1 as a typed constant.
UC constant ONE = UC.wrap(1);

// Exports 0 as a typed constant.
UC constant ZERO = UC.wrap(0);

/*//////////////////////////////////////////////////////////////////////////
                                LOGIC FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using { add as +, lt as <, lte as <= } for UC global;

/// @notice Sums up `x` and `y` without checked arithmetic.
function add(UC x, UC y) pure returns (UC) {
    unchecked {
        return UC.wrap(UC.unwrap(x) + UC.unwrap(y));
    }
}

/// @notice Checks if `x` is lower than `y`.
function lt(UC x, UC y) pure returns (bool) {
    return UC.unwrap(x) < UC.unwrap(y);
}

/// @notice Checks if `x` is lower than or equal to `y`.
function lte(UC x, UC y) pure returns (bool) {
    return UC.unwrap(x) <= UC.unwrap(y);
}

/*//////////////////////////////////////////////////////////////////////////
                                CASTING FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using { into, unwrap } for UC global;

/// @notice Alias for the `UC.unwrap` function.
function into(UC x) pure returns (uint256 result) {
    result = UC.unwrap(x);
}

/// @notice Alias for the `UC.wrap` function.
function uc(uint256 x) pure returns (UC result) {
    result = UC.wrap(x);
}

/// @notice Alias for the `UC.unwrap` function.
function unwrap(UC x) pure returns (uint256 result) {
    result = UC.unwrap(x);
}

/// @notice Alias for the `UC.wrap` function.
function wrap(uint256 x) pure returns (UC result) {
    result = UC.wrap(x);
}

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

pragma solidity ^0.8.0;

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

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

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../../utils/TransferHelper.sol";
import {ZERO, ONE, UC, uc, into} from "unchecked-counter/src/UC.sol";

library LibBrokerManager {
    using TransferHelper for address;

    bytes32 constant BROKER_MANAGER_STORAGE_POSITION = keccak256("apollox.broker.manager.storage");

    struct Broker {
        string name;
        string url;
        address receiver;
        uint24 id;
        uint24 brokerIndex;
        uint16 commissionP;
        uint16 daoShareP;
        uint16 alpPoolP;
    }

    struct Commission {
        uint256 total;
        uint256 pending;
    }

    struct BrokerManagerStorage {
        mapping(uint24 id => Broker) brokers;
        uint24[] brokerIds;
        mapping(uint24 id => mapping(address token => Commission)) brokerCommissions;
        // id => tokens
        mapping(uint24 id => address[]) brokerCommissionTokens;
        // token => total amount
        mapping(address => uint256) allPendingCommissions;
        uint24 defaultBroker;
    }

    function brokerManagerStorage() internal pure returns (BrokerManagerStorage storage bms) {
        bytes32 position = BROKER_MANAGER_STORAGE_POSITION;
        assembly {
            bms.slot := position
        }
    }

    event AddBroker(uint24 indexed id, Broker broker);
    event RemoveBroker(uint24 indexed id);
    event UpdateBrokerCommissionP(uint24 indexed id, uint16 commissionP, uint16 daoShareP, uint16 alpPoolP);
    event UpdateBrokerReceiver(uint24 indexed id, address oldReceiver, address receiver);
    event UpdateBrokerName(uint24 indexed id, string oldName, string name);
    event UpdateBrokerUrl(uint24 indexed id, string oldUrl, string url);
    event WithdrawBrokerCommission(uint24 indexed id, address indexed token, address indexed operator, uint256 amount);

    function initialize(uint24 id, address receiver, string calldata name, string calldata url) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        require(bms.defaultBroker == 0, "LibBrokerManager: Already initialized");
        bms.defaultBroker = id;
        addBroker(id, 1e4, 0, 0, receiver, name, url);
    }

    function addBroker(
        uint24 id,
        uint16 commissionP,
        uint16 daoShareP,
        uint16 alpPoolP,
        address receiver,
        string calldata name,
        string calldata url
    ) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        require(bms.brokers[id].receiver == address(0), "LibBrokerManager: Broker already exists");
        Broker memory b =
            Broker(name, url, receiver, id, uint24(bms.brokerIds.length), commissionP, daoShareP, alpPoolP);
        bms.brokers[id] = b;
        bms.brokerIds.push(id);
        emit AddBroker(id, b);
    }

    function _checkBrokerExist(BrokerManagerStorage storage bms, uint24 id) private view returns (Broker storage) {
        Broker storage b = bms.brokers[id];
        require(b.receiver != address(0), "LibBrokerManager: broker does not exist");
        return b;
    }

    function removeBroker(uint24 id) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        require(id != bms.defaultBroker, "LibBrokerManager: Default broker cannot be removed.");
        withdrawCommission(id);

        uint24[] storage brokerIds = bms.brokerIds;
        uint256 last = brokerIds.length - 1;
        uint256 removeBrokerIndex = bms.brokers[id].brokerIndex;
        if (removeBrokerIndex != last) {
            uint24 lastBrokerId = brokerIds[last];
            brokerIds[removeBrokerIndex] = lastBrokerId;
            bms.brokers[lastBrokerId].brokerIndex = uint24(removeBrokerIndex);
        }
        brokerIds.pop();
        delete bms.brokers[id];
        emit RemoveBroker(id);
    }

    function updateBrokerCommissionP(uint24 id, uint16 commissionP, uint16 daoShareP, uint16 alpPoolP) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        Broker storage b = _checkBrokerExist(bms, id);
        b.commissionP = commissionP;
        b.daoShareP = daoShareP;
        b.alpPoolP = alpPoolP;
        emit UpdateBrokerCommissionP(id, commissionP, daoShareP, alpPoolP);
    }

    function updateBrokerReceiver(uint24 id, address receiver) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        Broker storage b = _checkBrokerExist(bms, id);
        address oldReceiver = b.receiver;
        b.receiver = receiver;
        emit UpdateBrokerReceiver(id, oldReceiver, receiver);
    }

    function updateBrokerName(uint24 id, string calldata name) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        Broker storage b = _checkBrokerExist(bms, id);
        string memory oldName = b.name;
        b.name = name;
        emit UpdateBrokerName(id, oldName, name);
    }

    function updateBrokerUrl(uint24 id, string calldata url) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        Broker storage b = _checkBrokerExist(bms, id);
        string memory oldUrl = b.url;
        b.url = url;
        emit UpdateBrokerUrl(id, oldUrl, url);
    }

    function withdrawCommission(uint24 id) internal {
        BrokerManagerStorage storage bms = brokerManagerStorage();
        Broker storage b = _checkBrokerExist(bms, id);
        address operator = msg.sender;
        address[] memory tokens = bms.brokerCommissionTokens[id];
        for (UC i = ZERO; i < uc(tokens.length); i = i + ONE) {
            Commission storage c = bms.brokerCommissions[id][tokens[i.into()]];
            if (c.pending > 0) {
                uint256 pending = c.pending;
                c.pending = 0;
                bms.allPendingCommissions[tokens[i.into()]] -= pending;
                tokens[i.into()].transfer(b.receiver, pending);
                emit WithdrawBrokerCommission(id, tokens[i.into()], operator, pending);
            }
        }
    }

    function _getBrokerOrDefault(BrokerManagerStorage storage bms, uint24 id) private view returns (Broker memory) {
        Broker memory b = bms.brokers[id];
        if (b.receiver != address(0)) {
            return b;
        } else {
            return bms.brokers[bms.defaultBroker];
        }
    }

    function updateBrokerCommission(address token, uint256 feeAmount, uint24 id)
        internal
        returns (uint256 commission, uint24 brokerId, uint256 daoAmount, uint256 alpPoolAmount)
    {
        BrokerManagerStorage storage bms = brokerManagerStorage();

        Broker memory b = _getBrokerOrDefault(bms, id);
        commission = feeAmount * b.commissionP / 1e4;
        if (commission > 0) {
            Commission storage c = bms.brokerCommissions[b.id][token];
            if (c.total == 0) {
                bms.brokerCommissionTokens[b.id].push(token);
            }
            c.total += commission;
            c.pending += commission;
            bms.allPendingCommissions[token] += commission;
        }
        return (commission, b.id, feeAmount * b.daoShareP / 1e4, feeAmount * b.alpPoolP / 1e4);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./IPairsManager.sol";
import "../libraries/LibPairsManager.sol";

enum SlippageType {
    FIXED,
    ONE_PERCENT_DEPTH,
    NET_POSITION,
    THRESHOLD
}

struct SlippageConfigView {
    uint256 onePercentDepthAboveUsd;
    uint256 onePercentDepthBelowUsd;
    uint16 slippageLongP; // 1e4
    uint16 slippageShortP; // 1e4
    uint256 longThresholdUsd;
    uint256 shortThresholdUsd;
    SlippageType slippageType;
}

interface ISlippageManager {
    event AddSlippageConfig(
        uint16 indexed index,
        SlippageType indexed slippageType,
        uint256 onePercentDepthAboveUsd,
        uint256 onePercentDepthBelowUsd,
        uint16 slippageLongP,
        uint16 slippageShortP,
        uint256 longThresholdUsd,
        uint256 shortThresholdUsd,
        string name
    );
    event RemoveSlippageConfig(uint16 indexed index);
    event UpdateSlippageConfig(
        uint16 indexed index,
        SlippageType indexed slippageType,
        uint256 onePercentDepthAboveUsd,
        uint256 onePercentDepthBelowUsd,
        uint16 slippageLongP,
        uint16 slippageShortP,
        uint256 longThresholdUsd,
        uint256 shortThresholdUsd
    );

    struct UpdateSlippageConfigParam {
        uint16 index;
        SlippageType slippageType;
        uint256 onePercentDepthAboveUsd;
        uint256 onePercentDepthBelowUsd;
        uint16 slippageLongP; // 1e4
        uint16 slippageShortP; // 1e4
        uint256 longThresholdUsd;
        uint256 shortThresholdUsd;
    }

    function addSlippageConfig(string calldata name, uint16 index, SlippageConfigView calldata sc) external;

    function removeSlippageConfig(uint16 index) external;

    function updateSlippageConfig(UpdateSlippageConfigParam calldata param) external;

    function batchUpdateSlippageConfig(UpdateSlippageConfigParam[] calldata params) external;

    function getSlippageConfigByIndex(uint16 index)
        external
        view
        returns (LibPairsManager.SlippageConfig memory, IPairsManager.PairSimple[] memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./LibFeeManager.sol";
import "../interfaces/IPriceFacade.sol";
import "../interfaces/ITradingCore.sol";
import "../interfaces/IPairsManager.sol";
import {SlippageType} from "../interfaces/ISlippageManager.sol";
import {ZERO, ONE, UC, uc, into} from "unchecked-counter/src/UC.sol";

library LibPairsManager {
    bytes32 constant PAIRS_MANAGER_STORAGE_POSITION = keccak256("apollox.pairs.manager.storage");

    /*
       tier    notionalUsd     maxLeverage      initialLostP        liqLostP
        1      (0 ~ 10,000]        20              95%                97.5%
        2    (10,000 ~ 50,000]     10              90%                 95%
        3    (50,000 ~ 100,000]     5              80%                 90%
        4    (100,000 ~ 200,000]    3              75%                 85%
        5    (200,000 ~ 500,000]    2              60%                 75%
        6    (500,000 ~ 800,000]    1              40%                 50%
    */
    struct LeverageMargin {
        uint256 notionalUsd;
        uint16 tier;
        uint16 maxLeverage;
        uint16 initialLostP; // 1e4
        uint16 liqLostP; // 1e4
    }

    struct SlippageConfig {
        string name;
        uint256 onePercentDepthAboveUsd;
        uint256 onePercentDepthBelowUsd;
        uint16 slippageLongP; // 1e4
        uint16 slippageShortP; // 1e4
        uint16 index;
        SlippageType slippageType;
        bool enable;
        uint256 longThresholdUsd;
        uint256 shortThresholdUsd;
    }

    struct Pair {
        // BTC/USD
        string name;
        // BTC address
        address base;
        uint16 basePosition;
        IPairsManager.PairType pairType;
        IPairsManager.PairStatus status;

        uint16 slippageConfigIndex;
        uint16 slippagePosition;

        uint16 feeConfigIndex;
        uint16 feePosition;

        uint256 maxLongOiUsd;
        uint256 maxShortOiUsd;
        uint256 fundingFeePerBlockP; // 1e18
        uint256 minFundingFeeR; // 1e18
        uint256 maxFundingFeeR; // 1e18
        // tier => LeverageMargin
        mapping(uint16 => LeverageMargin) leverageMargins;
        uint16 maxTier;

        uint40 longHoldingFeeRate; // 1e12
        uint40 shortHoldingFeeRate; // 1e12
    }

    struct PairsManagerStorage {
        // 0/1/2/3/.../ => SlippageConfig
        mapping(uint16 => SlippageConfig) slippageConfigs;
        // SlippageConfig index => pairs.base[]
        mapping(uint16 => address[]) slippageConfigPairs;
        mapping(address => Pair) pairs;
        address[] pairBases;
    }

    function pairsManagerStorage() internal pure returns (PairsManagerStorage storage pms) {
        bytes32 position = PAIRS_MANAGER_STORAGE_POSITION;
        assembly {
            pms.slot := position
        }
    }

    event AddPair(
        address indexed base,
        IPairsManager.PairType indexed pairType,
        IPairsManager.PairStatus indexed status,
        uint16 slippageConfigIndex,
        uint16 feeConfigIndex,
        string name,
        LeverageMargin[] leverageMargins
    );
    event UpdatePairMaxOi(
        address indexed base,
        uint256 oldMaxLongOiUsd,
        uint256 oldMaxShortOiUsd,
        uint256 maxLongOiUsd,
        uint256 maxShortOiUsd
    );
    event UpdatePairHoldingFeeRate(
        address indexed base, uint40 oldLongRate, uint40 oldShortRate, uint40 longRate, uint40 shortRate
    );
    event UpdatePairFundingFeeConfig(
        address indexed base,
        uint256 oldFundingFeePerBlockP,
        uint256 oldMinFundingFeeR,
        uint256 oldMaxFundingFeeR,
        uint256 fundingFeePerBlockP,
        uint256 minFundingFeeR,
        uint256 maxFundingFeeR
    );
    event RemovePair(address indexed base);
    event UpdatePairStatus(
        address indexed base, IPairsManager.PairStatus indexed oldStatus, IPairsManager.PairStatus indexed status
    );
    event UpdatePairSlippage(
        address indexed base, uint16 indexed oldSlippageConfigIndexed, uint16 indexed slippageConfigIndex
    );
    event UpdatePairFee(address indexed base, uint16 indexed oldFeeConfigIndex, uint16 indexed feeConfigIndex);
    event UpdatePairType(
        address indexed base, IPairsManager.PairType indexed oldType, IPairsManager.PairType indexed pairType
    );
    event UpdatePairLeverageMargin(address indexed base, LeverageMargin[] leverageMargins);

    function addPair(
        IPairsManager.PairSimple memory ps,
        uint16 slippageConfigIndex,
        uint16 feeConfigIndex,
        LeverageMargin[] calldata leverageMargins
    ) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[ps.base];
        require(pair.base == address(0), "LibPairsManager: Pair already exists");
        require(
            IPriceFacade(address(this)).getPrice(ps.base) > 0,
            "LibPairsManager: No price feed has been configured for the pair"
        );
        {
            SlippageConfig memory slippageConfig = pms.slippageConfigs[slippageConfigIndex];
            require(slippageConfig.enable, "LibPairsManager: Slippage configuration is not available");
            (LibFeeManager.FeeConfig memory feeConfig, address[] storage feePairs) =
                LibFeeManager.getFeeConfigByIndex(feeConfigIndex);
            require(feeConfig.enable, "LibPairsManager: Fee configuration is not available");

            pair.slippageConfigIndex = slippageConfigIndex;
            address[] storage slippagePairs = pms.slippageConfigPairs[slippageConfigIndex];
            pair.slippagePosition = uint16(slippagePairs.length);
            slippagePairs.push(ps.base);

            pair.feeConfigIndex = feeConfigIndex;
            pair.feePosition = uint16(feePairs.length);
            feePairs.push(ps.base);
        }
        pair.name = ps.name;
        pair.base = ps.base;
        pair.basePosition = uint16(pms.pairBases.length);
        pms.pairBases.push(ps.base);
        pair.pairType = ps.pairType;
        pair.status = ps.status;
        pair.maxTier = uint16(leverageMargins.length);
        for (UC i = ONE; i <= uc(leverageMargins.length); i = i + ONE) {
            pair.leverageMargins[uint16(i.into())] = leverageMargins[uint16(i.into() - 1)];
        }
        emit AddPair(ps.base, ps.pairType, ps.status, slippageConfigIndex, feeConfigIndex, ps.name, leverageMargins);
    }

    function updatePairMaxOi(address base, uint256 maxLongOiUsd, uint256 maxShortOiUsd) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");

        uint256 oldMaxLongOiUsd = pair.maxLongOiUsd;
        uint256 oldMaxShortOiUsd = pair.maxShortOiUsd;
        pair.maxLongOiUsd = maxLongOiUsd;
        pair.maxShortOiUsd = maxShortOiUsd;
        emit UpdatePairMaxOi(base, oldMaxLongOiUsd, oldMaxShortOiUsd, maxLongOiUsd, maxShortOiUsd);
    }

    function updatePairFundingFeeConfig(
        address base,
        uint256 fundingFeePerBlockP,
        uint256 minFundingFeeR,
        uint256 maxFundingFeeR
    ) internal {
        require(maxFundingFeeR > minFundingFeeR, "LibPairsManager: fundingFee parameter is invalid");
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");

        ITradingCore(address(this)).updatePairPositionInfo(base);

        uint256 oldFundingFeePerBlockP = pair.fundingFeePerBlockP;
        uint256 oldMinFundingFeeR = pair.minFundingFeeR;
        uint256 oldMaxFundingFeeR = pair.maxFundingFeeR;
        pair.fundingFeePerBlockP = fundingFeePerBlockP;
        pair.minFundingFeeR = minFundingFeeR;
        pair.maxFundingFeeR = maxFundingFeeR;
        emit UpdatePairFundingFeeConfig(
            base,
            oldFundingFeePerBlockP,
            oldMinFundingFeeR,
            oldMaxFundingFeeR,
            fundingFeePerBlockP,
            minFundingFeeR,
            maxFundingFeeR
        );
    }

    function updatePairHoldingFeeRate(address base, uint40 longHoldingFeeRate, uint40 shortHoldingFeeRate) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");

        uint40 oldLongRate = pair.longHoldingFeeRate;
        uint40 oldShortRate = pair.shortHoldingFeeRate;
        pair.longHoldingFeeRate = longHoldingFeeRate;
        pair.shortHoldingFeeRate = shortHoldingFeeRate;
        emit UpdatePairHoldingFeeRate(base, oldLongRate, oldShortRate, longHoldingFeeRate, shortHoldingFeeRate);
    }

    function removePair(address base) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");

        ITradingCore.PairQty memory pairQty = ITradingCore(address(this)).getPairQty(base);
        require(pairQty.longQty == 0 && pairQty.shortQty == 0, "LibPairsManager: Position is not 0");

        address[] storage slippagePairs = pms.slippageConfigPairs[pair.slippageConfigIndex];
        uint256 lastPositionSlippage = slippagePairs.length - 1;
        uint256 slippagePosition = pair.slippagePosition;
        if (slippagePosition != lastPositionSlippage) {
            address lastBase = slippagePairs[lastPositionSlippage];
            slippagePairs[slippagePosition] = lastBase;
            pms.pairs[lastBase].slippagePosition = uint16(slippagePosition);
        }
        slippagePairs.pop();

        (, address[] storage feePairs) = LibFeeManager.getFeeConfigByIndex(pair.feeConfigIndex);
        uint256 lastPositionFee = feePairs.length - 1;
        uint256 feePosition = pair.feePosition;
        if (feePosition != lastPositionFee) {
            address lastBase = feePairs[lastPositionFee];
            feePairs[feePosition] = lastBase;
            pms.pairs[lastBase].feePosition = uint16(feePosition);
        }
        feePairs.pop();

        address[] storage pairBases = pms.pairBases;
        uint256 lastPositionBase = pairBases.length - 1;
        uint256 basePosition = pair.basePosition;
        if (basePosition != lastPositionBase) {
            address lastBase = pairBases[lastPositionBase];
            pairBases[basePosition] = lastBase;
            pms.pairs[lastBase].basePosition = uint16(basePosition);
        }
        pairBases.pop();
        // Removing a pair does not delete the leverageMargins mapping data from the Pair struct.
        // If the pair is added again, a new leverageMargins value will be set during the addition,
        // which will overwrite the previous old value.
        delete pms.pairs[base];
        emit RemovePair(base);
    }

    function updatePairStatus(address base, IPairsManager.PairStatus status) internal {
        Pair storage pair = pairsManagerStorage().pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");
        require(pair.status != status, "LibPairsManager: No change in status, no modification required");
        IPairsManager.PairStatus oldStatus = pair.status;
        pair.status = status;
        emit UpdatePairStatus(base, oldStatus, status);
    }

    function updatePairType(address base, IPairsManager.PairType pairType) internal {
        Pair storage pair = pairsManagerStorage().pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");
        require(pair.pairType != pairType, "LibPairsManager: No change in pairType, no modification required");
        IPairsManager.PairType oldType = pair.pairType;
        pair.pairType = pairType;
        emit UpdatePairType(base, oldType, pairType);
    }

    function batchUpdatePairStatus(IPairsManager.PairType pairType, IPairsManager.PairStatus status) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        address[] memory pairBases = pms.pairBases;
        for (UC i = ZERO; i < uc(pairBases.length); i = i + ONE) {
            Pair storage pair = pms.pairs[pairBases[i.into()]];
            if (pair.pairType == pairType) {
                IPairsManager.PairStatus oldStatus = pair.status;
                pair.status = status;
                emit UpdatePairStatus(pair.base, oldStatus, status);
            }
        }
    }

    function updatePairSlippage(address base, uint16 slippageConfigIndex) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");
        SlippageConfig memory config = pms.slippageConfigs[slippageConfigIndex];
        require(config.enable, "LibPairsManager: Slippage configuration is not available");

        uint16 oldSlippageConfigIndex = pair.slippageConfigIndex;
        address[] storage oldSlippagePairs = pms.slippageConfigPairs[oldSlippageConfigIndex];
        uint256 lastPositionSlippage = oldSlippagePairs.length - 1;
        uint256 oldSlippagePosition = pair.slippagePosition;
        if (oldSlippagePosition != lastPositionSlippage) {
            pms.pairs[oldSlippagePairs[lastPositionSlippage]].slippagePosition = uint16(oldSlippagePosition);
            oldSlippagePairs[oldSlippagePosition] = oldSlippagePairs[lastPositionSlippage];
        }
        oldSlippagePairs.pop();

        pair.slippageConfigIndex = slippageConfigIndex;
        address[] storage slippagePairs = pms.slippageConfigPairs[slippageConfigIndex];
        pair.slippagePosition = uint16(slippagePairs.length);
        slippagePairs.push(base);
        emit UpdatePairSlippage(base, oldSlippageConfigIndex, slippageConfigIndex);
    }

    function updatePairFee(address base, uint16 feeConfigIndex) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");
        (LibFeeManager.FeeConfig memory feeConfig, address[] storage feePairs) =
            LibFeeManager.getFeeConfigByIndex(feeConfigIndex);
        require(feeConfig.enable, "LibPairsManager: Fee configuration is not available");

        uint16 oldFeeConfigIndex = pair.feeConfigIndex;
        (, address[] storage oldFeePairs) = LibFeeManager.getFeeConfigByIndex(oldFeeConfigIndex);
        uint256 lastPositionFee = oldFeePairs.length - 1;
        uint256 oldFeePosition = pair.feePosition;
        if (oldFeePosition != lastPositionFee) {
            pms.pairs[oldFeePairs[lastPositionFee]].feePosition = uint16(oldFeePosition);
            oldFeePairs[oldFeePosition] = oldFeePairs[lastPositionFee];
        }
        oldFeePairs.pop();

        pair.feeConfigIndex = feeConfigIndex;
        pair.feePosition = uint16(feePairs.length);
        feePairs.push(base);
        emit UpdatePairFee(base, oldFeeConfigIndex, feeConfigIndex);
    }

    function updatePairLeverageMargin(address base, LeverageMargin[] calldata leverageMargins) internal {
        PairsManagerStorage storage pms = pairsManagerStorage();
        Pair storage pair = pms.pairs[base];
        require(pair.base != address(0), "LibPairsManager: Pair does not exist");

        uint256 maxTier = pair.maxTier > leverageMargins.length ? pair.maxTier : leverageMargins.length;
        for (UC i = ONE; i <= uc(maxTier); i = i + ONE) {
            if (i <= uc(leverageMargins.length)) {
                pair.leverageMargins[uint16(i.into())] = leverageMargins[uint16(i.into() - 1)];
            } else {
                delete pair.leverageMargins[uint16(i.into())];
            }
        }
        pair.maxTier = uint16(leverageMargins.length);
        emit UpdatePairLeverageMargin(base, leverageMargins);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ArbSys {
    /**
     * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0)
     * @return block number as int
     */
    function arbBlockNumber() external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "./IBook.sol";
import "./ITrading.sol";

interface ITradingPortal is ITrading, IBook {
    event FundingFeeAddLiquidity(address indexed token, uint256 amount);
    event MarketPendingTrade(address indexed user, bytes32 indexed tradeHash, OpenDataInput trade);
    event UpdateTradeTp(address indexed user, bytes32 indexed tradeHash, uint256 oldTp, uint256 tp);
    event UpdateTradeSl(address indexed user, bytes32 indexed tradeHash, uint256 oldSl, uint256 sl);
    event UpdateMargin(address indexed user, bytes32 indexed tradeHash, uint256 beforeMargin, uint256 margin);

    function openMarketTrade(OpenDataInput memory openData) external returns (bytes32 tradeHash);

    function openMarketTradeBNB(OpenDataInput memory openData) external payable returns (bytes32 tradeHash);

    function updateTradeTp(bytes32 tradeHash, uint64 takeProfit) external;

    function updateTradeSl(bytes32 tradeHash, uint64 stopLoss) external;

    // stopLoss is allowed to be equal to 0, which means the sl setting is removed.
    // takeProfit must be greater than 0
    function updateTradeTpAndSl(bytes32 tradeHash, uint64 takeProfit, uint64 stopLoss) external;

    function settleLpFundingFee(uint256 lpReceiveFundingFeeUsd) external;

    function closeTrade(bytes32 tradeHash) external;

    function batchCloseTrade(bytes32[] calldata tradeHashes) external;

    function addMargin(bytes32 tradeHash, uint96 amount) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Period} from "./IPredictionManager.sol";

enum RequestType {
    CLOSE,
    OPEN,
    PREDICT,
    PREDICT_RELATIVE
}

interface IPriceFacade {
    struct Config {
        uint16 lowPriceGapP;
        uint16 highPriceGapP;
        uint16 maxDelay;
        uint16 triggerLowPriceGapP; // 1e4
        uint16 triggerHighPriceGapP; // 1e4
    }

    struct PriceCallbackParam {
        bytes32 requestId;
        uint64 price;
    }

    struct PriceCallbackPythParamV1 {
        bytes32 requestId;
        bytes32 priceId;
        bytes priceUpdateData;
    }

    struct PriceCallbackPythParam {
        bytes32 requestId;
        bytes32 priceId;
        bool reciprocalPrice;
        uint64 price;
    }

    event RequestRelativePrice(bytes32 indexed requestId, bytes32 indexed marketHash, Period period);
    event RelativePriceUpdated(bytes32 indexed requestId, bytes32 indexed marketHash);
    event RelativeRequestNotExist(bytes32 indexed requestId, bytes32 indexed marketHash);

    function setLowAndHighPriceGapP(uint16 lowPriceGapP, uint16 highPriceGapP) external;

    function setTriggerLowAndHighPriceGapP(uint16 triggerLowPriceGapP, uint16 triggerHighPriceGapP) external;

    function setMaxDelay(uint16 maxDelay) external;

    function getPriceFacadeConfig() external view returns (Config memory);

    function getPrice(address token) external view returns (uint256);

    function getPriceFromCacheOrOracle(address token) external view returns (uint64 price, uint40 updatedAt);

    function requestPrice(bytes32 tradeHash, address token, RequestType requestType) external;

    function requestRelativePrice(bytes32 id, bytes32 marketHash, Period period) external;

    function requestPriceCallback(bytes32 requestId, uint64 price) external;

    function requestPriceCallback(bytes32 requestId, bytes32 priceId, bytes calldata priceUpdateData) external;

    function batchRequestPriceCallback(PriceCallbackParam[] calldata params) external;

    function batchRequestPriceCallback(PriceCallbackPythParamV1[] calldata params) external;

    function batchRequestPriceCallback(PriceCallbackPythParam[] calldata requests, bytes[] calldata priceUpdateData)
        external
        payable;

    function confirmTriggerPrice(address token, uint64 price) external returns (bool, uint64, uint64);

    function requestRelativePriceCallback(bytes32 requestId, bytes32 marketHash, uint64[] calldata prices) external;

    /// @notice Pyth-based callback for relative entry prices.
    /// @dev `priceUpdateData` may contain updates for multiple price feeds in a single VAA.
    ///      Uses the stored canonical priceIds for the market; rejects if any are missing/duplicated.
    ///      Caller should pass enough `msg.value` to cover `pyth.getUpdateFee([priceUpdateData])` (excess is refunded).
    function requestRelativePriceCallback(bytes32 requestId, bytes32 marketHash, bytes calldata priceUpdateData)
        external
        payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

enum Period {
    MINUTE1,
    MINUTE5,
    MINUTE10,
    MINUTE15,
    MINUTE30,
    HOUR1,
    HOUR2,
    HOUR3,
    HOUR4,
    HOUR6,
    HOUR8,
    HOUR12,
    DAY1
}
enum PredictionPairStatus {
    AVAILABLE,
    CLOSE_ONLY,
    CLOSED
}

struct PredictionPeriod {
    uint256 maxUpUsd; // USD 1e18
    uint256 maxDownUsd; // USD 1e18
    Period period;
    PredictionPairStatus status;
    uint16 winRatio; // 1e4
    uint16 openFeeP; // 1e4
    uint16 winCloseFeeP; // 1e4
    uint16 loseCloseFeeP; // 1e4
}

interface IPredictionManager {
    event AddPredictionPair(address indexed base, string name, PredictionPeriod[] predictionPeriods);
    event RemovePredictionPair(address indexed base);
    event UpdatePredictionPairStatus(address indexed base, PredictionPairStatus status);
    event UpdatePredictionPairPeriodMaxCap(
        address indexed base, Period indexed period, uint256 maxUpUsd, uint256 maxDownUsd
    );
    event UpdatePredictionPairPeriodWinRatio(address indexed base, Period indexed period, uint16 winRatio);
    event UpdatePredictionPairPeriodFee(
        address indexed base, Period indexed period, uint16 openFeeP, uint16 winCloseFeeP, uint16 loseCloseFeeP
    );
    event AddPeriodForPredictionPair(address indexed base, PredictionPeriod[] predictionPeriods);
    event ReplacePredictionPairPeriod(address indexed base, PredictionPeriod[] predictionPeriods);
    event UpdatePredictionPairPythPriceId(address indexed base, bytes32 indexed priceId);

    struct PeriodCap {
        Period period;
        uint256 maxUpUsd; // USD 1e18
        uint256 maxDownUsd; // USD 1e18
    }

    struct PeriodWinRatio {
        Period period;
        uint16 winRatio;
    }

    struct PeriodFee {
        Period period;
        uint16 openFeeP; // 1e4
        uint16 winCloseFeeP; // 1e4
        uint16 loseCloseFeeP; // 1e4
    }

    struct PredictionPairView {
        string name;
        address base;
        PredictionPeriod[] predictionPeriods;
    }

    function addPredictionPair(address base, string calldata name, PredictionPeriod[] calldata predictionPeriods)
        external;

    function removePredictionPair(address base) external;

    function updatePredictionPairStatus(address base, PredictionPairStatus status) external;

    function updatePredictionPairMaxCap(address base, PeriodCap[] calldata periodCaps) external;

    function updatePredictionPairWinRatio(address base, PeriodWinRatio[] calldata periodWinRatios) external;

    function updatePredictionPairFee(address base, PeriodFee[] calldata periodFees) external;

    function addPeriodForPredictionPair(address base, PredictionPeriod[] calldata predictionPeriods) external;

    function replacePredictionPairPeriod(address base, PredictionPeriod[] calldata predictionPeriods) external;

    function updatePredictionPairPythPriceId(address base, bytes32 priceId) external;

    function getPredictionPairByBase(address base) external returns (PredictionPairView memory);

    function predictionPairs(uint256 start, uint8 size) external returns (PredictionPairView[] memory);

    function getPredictionPeriod(address base, Period period) external returns (PredictionPeriod memory);

    function getPredictionPairPythPriceId(address base) external view returns (bytes32);
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@chainlink/contracts/=lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/contracts/",
    "@pythnetwork/pyth-sdk-solidity/=lib/pyth-sdk-solidity/",
    "unchecked-counter/=unchecked-counter/",
    "chainlink-brownie-contracts/=lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/contracts/src/v0.6/vendor/@arbitrum/nitro-contracts/src/",
    "ds-test/=lib/foundry-chainlink-toolkit/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/foundry-chainlink-toolkit/lib/forge-std/src/",
    "foundry-chainlink-toolkit/=lib/foundry-chainlink-toolkit/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "pyth-sdk-solidity/=lib/pyth-sdk-solidity/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bytes32","name":"tradeHash","type":"bytes32"},{"components":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"userOpenTradeIndex","type":"uint32"},{"internalType":"uint64","name":"entryPrice","type":"uint64"},{"internalType":"address","name":"pairBase","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"uint64","name":"stopLoss","type":"uint64"},{"internalType":"uint64","name":"takeProfit","type":"uint64"},{"internalType":"uint24","name":"broker","type":"uint24"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"openFee","type":"uint96"},{"internalType":"int256","name":"longAccFundingFeePerShare","type":"int256"},{"internalType":"uint96","name":"executionFee","type":"uint96"},{"internalType":"uint40","name":"timestamp","type":"uint40"},{"internalType":"uint80","name":"qty","type":"uint80"},{"internalType":"uint40","name":"holdingFeeRate","type":"uint40"},{"internalType":"uint256","name":"openBlock","type":"uint256"}],"indexed":false,"internalType":"struct ITrading.OpenTrade","name":"ot","type":"tuple"}],"name":"OpenMarketTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bytes32","name":"tradeHash","type":"bytes32"},{"indexed":false,"internalType":"enum ITradingChecker.Refund","name":"refund","type":"uint8"}],"name":"PendingTradeRefund","type":"event"},{"inputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint64","name":"entryPrice","type":"uint64"},{"internalType":"address","name":"pairBase","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint96","name":"margin","type":"uint96"},{"internalType":"uint64","name":"stopLoss","type":"uint64"},{"internalType":"uint64","name":"takeProfit","type":"uint64"},{"internalType":"uint24","name":"broker","type":"uint24"},{"internalType":"bool","name":"isLong","type":"bool"},{"internalType":"uint96","name":"openFee","type":"uint96"},{"internalType":"uint96","name":"executionFee","type":"uint96"},{"internalType":"uint80","name":"qty","type":"uint80"}],"internalType":"struct LimitOrderDeal","name":"order","type":"tuple"},{"internalType":"uint256","name":"marketPrice","type":"uint256"}],"name":"limitOrderDeal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tradeHash","type":"bytes32"},{"internalType":"uint256","name":"upperPrice","type":"uint256"},{"internalType":"uint256","name":"lowerPrice","type":"uint256"}],"name":"marketTradeCallback","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b506120f0806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80638e580c9c1461003b578063a75d6e5214610050575b600080fd5b61004e610049366004611895565b610063565b005b61004e61005e3660046119e4565b6104c2565b3330146100a85760405162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e481cd95b198818d85b1b60921b60448201526064015b60405180910390fd5b60007fdf4ccd96bfd9057e01ad44fd6c4745693e8ef807b15c7a0f659244231ebda883600085815260018281016020908152604080842081516101608101835281546001600160a01b038082168352600160a01b80830462ffffff1696840196909652600160b81b820460ff161515948301859052600160c01b9091046001600160401b0390811660608401529583015480821660808401528590046001600160601b031660a0830152600283015490811660c0830152939093046001600160501b031660e084015260030154808416610100840152680100000000000000008104909316610120830152600160801b9092046001600160801b03166101408201529293506101b757836101b9565b845b90506000806000306001600160a01b0316630a384f3186866040518363ffffffff1660e01b81526004016101ee929190611af3565b600060405180830381865afa15801561020b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102339190810190611c21565b9250925092508261036c5760c0850151855160a08701516001600160a01b038316600090815260028a016020526040812080546001600160601b0390931692839290610280908490611cf7565b909155505060008c81526001808b01602052604082208281559081018290556002810180546001600160f01b0319169055600301556102c96001600160a01b0384168383610bcd565b8b826001600160a01b03167f2f8b631074b09d8b7f3fbc9184a5ddb3a59bc2bff68c6511e7e6d3d7a26159da876040516103039190611d2c565b60405180910390a3604051633d0ac8dd60e11b81523090637a1591ba90610332908f9086908a90600401611d3a565b600060405180830381600087803b15801561034c57600080fd5b505af1158015610360573d6000803e3d6000fd5b505050505050506104b7565b600080600080848060200190518101906103869190611d5d565b6001600160401b0381166101208e0152929650909450925090506103af8a8a8f87878d88610c99565b8860a001516001600160601b03168a60020160008b60c001516001600160a01b03166001600160a01b0316815260200190815260200160002060008282546103f79190611cf7565b909155505060c089015160008e81526001808d01602090815260408084208481559283018490556002830180546001600160f01b031916905560039092019290925580516307d3576360e11b815290516104b1923092630fa6aec692600480830193928290030181865afa158015610473573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104979190611dbc565b6001600160a01b038316906001600160601b038716610bcd565b50505050505b505050505050505050565b3330146105025760405162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e481cd95b198818d85b1b60921b604482015260640161009f565b60007fdf4ccd96bfd9057e01ad44fd6c4745693e8ef807b15c7a0f659244231ebda88390506105438184608001518560a001516001600160601b031661133b565b6000306001600160a01b031663d89297e8856060015186604001518688610180015189610120015160016040518763ffffffff1660e01b815260040161058e96959493929190611dd9565b6020604051808303816000875af11580156105ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d19190611e22565b6020858101516001600160a01b0390811660009081526004868101909352604080822060808a01516101408b01516101008c01519351631753dfad60e21b815291909516958101959095526001600160601b03909316602485015262ffffff16604484015292935091903090635d4f7eb4906064016020604051808303816000875af1158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190611e3b565b9050600060405180610220016040528088602001516001600160a01b03168152602001848054905063ffffffff16815260200188604001516001600160401b0316815260200188606001516001600160a01b0316815260200188608001516001600160a01b031681526020018860a001516001600160601b031681526020018860c001516001600160401b031681526020018860e001516001600160401b031681526020018362ffffff168152602001886101200151151581526020018861014001516001600160601b031681526020018581526020018861016001516001600160601b031681526020014264ffffffffff1681526020018861018001516001600160501b03168152602001306001600160a01b031663cd7a9f478a606001518b61012001516040518363ffffffff1660e01b81526004016107e29291906001600160a01b039290921682521515602082015260400190565b602060405180830381865afa1580156107ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108239190611e58565b64ffffffffff168152602001610837611404565b8152509050808560030160008960000151815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160020160146101000a8154816001600160601b0302191690836001600160601b0316021790555060c08201518160030160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160030160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160030160106101000a81548162ffffff021916908362ffffff1602179055506101208201518160030160136101000a81548160ff0219169083151502179055506101408201518160030160146101000a8154816001600160601b0302191690836001600160601b0316021790555061016082015181600401556101808201518160050160006101000a8154816001600160601b0302191690836001600160601b031602179055506101a082015181600501600c6101000a81548164ffffffffff021916908364ffffffffff1602179055506101c08201518160050160116101000a8154816001600160501b0302191690836001600160501b031602179055506101e082015181600501601b6101000a81548164ffffffffff021916908364ffffffffff16021790555061020082015181600601559050508287600001519080600181540180825580915050600190039060005260206000200160009091909190915055866000015181600001516001600160a01b03167fa858fcdefab65cbd1997932d8ac8aa1a9a8c46c90b20947575525d9a2a437f8c83604051610b649190611fda565b60405180910390a3865160405163d98a933560e01b8152309163d98a933591610b9291908590600401611fe9565b600060405180830381600087803b158015610bac57600080fd5b505af1158015610bc0573d6000803e3d6000fd5b5050505050505050505050565b600080610bd861147b565b91509150801580610bfb5750816001600160a01b0316856001600160a01b031614155b80610c065750833b15155b15610c2457610c1f6001600160a01b03861685856114b2565b610c92565b604051632e1a7d4d60e01b8152600481018490526001600160a01b03861690632e1a7d4d90602401600060405180830381600087803b158015610c6657600080fd5b505af1158015610c7a573d6000803e3d6000fd5b50610c92925050506001600160a01b03851684611509565b5050505050565b600083858860a00151610cac9190611ffe565b610cb69190611ffe565b9050610cd0888860c00151836001600160601b031661133b565b608087015160e08801516040808a01519051631b1252fd60e31b8152600093309363d89297e893610d0c9389928b929091600190600401611dd9565b6020604051808303816000875af1158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f9190611e22565b60c089015160208a0151604051631753dfad60e21b81526001600160a01b0390921660048301526001600160601b038916602483015262ffffff1660448201529091506000903090635d4f7eb4906064016020604051808303816000875af1158015610dbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de39190611e3b565b905060008a60040160008b600001516001600160a01b03166001600160a01b03168152602001908152602001600020905060006040518061022001604052808c600001516001600160a01b03168152602001838054905063ffffffff168152602001876001600160401b031681526020018c608001516001600160a01b031681526020018c60c001516001600160a01b03168152602001866001600160601b031681526020018c61010001516001600160401b031681526020018c61012001516001600160401b031681526020018462ffffff1681526020018c60400151151581526020018a6001600160601b03168152602001858152602001896001600160601b031681526020014264ffffffffff1681526020018c60e001516001600160501b03168152602001306001600160a01b031663cd7a9f478e608001518f604001516040518363ffffffff1660e01b8152600401610f589291906001600160a01b039290921682521515602082015260400190565b602060405180830381865afa158015610f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f999190611e58565b64ffffffffff168152602001610fad611404565b8152509050808c60030160008c815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160020160146101000a8154816001600160601b0302191690836001600160601b0316021790555060c08201518160030160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160030160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160030160106101000a81548162ffffff021916908362ffffff1602179055506101208201518160030160136101000a81548160ff0219169083151502179055506101408201518160030160146101000a8154816001600160601b0302191690836001600160601b0316021790555061016082015181600401556101808201518160050160006101000a8154816001600160601b0302191690836001600160601b031602179055506101a082015181600501600c6101000a81548164ffffffffff021916908364ffffffffff1602179055506101c08201518160050160116101000a8154816001600160501b0302191690836001600160501b031602179055506101e082015181600501601b6101000a81548164ffffffffff021916908364ffffffffff1602179055506102008201518160060155905050818a9080600181540180825580915050600190039060005260206000200160009091909190915055898b600001516001600160a01b03167fa858fcdefab65cbd1997932d8ac8aa1a9a8c46c90b20947575525d9a2a437f8c836040516112ce9190611fda565b60405180910390a360405163d98a933560e01b8152309063d98a9335906112fb908d908590600401611fe9565b600060405180830381600087803b15801561131557600080fd5b505af1158015611329573d6000803e3d6000fd5b50505050505050505050505050505050565b600683016000805b8254611350908290611622565b1561139c576001600160a01b03851683828154811061137157611371612025565b6000918252602090912001546001600160a01b031603611394576001915061139c565b600101611343565b50806113ce5781546001810183556000838152602090200180546001600160a01b0319166001600160a01b0386161790555b6001600160a01b0384166000908152600586016020526040812080548592906113f890849061203b565b90915550505050505050565b600060643b156114765760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561144d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114719190611e22565b905090565b504390565b60008046608e1981016114a75750733bd359c1119da7da1d913d1c4d2b7c461115433a92600192509050565b506000928392509050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261150490849061162c565b505050565b804710156115595760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161009f565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146115a6576040519150601f19603f3d011682016040523d82523d6000602084013e6115ab565b606091505b50509050806115045760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161009f565b8082105b92915050565b6000611681826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166117019092919063ffffffff16565b90508051600014806116a25750808060200190518101906116a2919061204e565b6115045760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161009f565b6060611710848460008561171a565b90505b9392505050565b60608247101561177b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161009f565b600080866001600160a01b03168587604051611797919061206b565b60006040518083038185875af1925050503d80600081146117d4576040519150601f19603f3d011682016040523d82523d6000602084013e6117d9565b606091505b50915091506117ea878383876117f7565b925050505b949350505050565b6060831561186657825160000361185f576001600160a01b0385163b61185f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161009f565b50816117ef565b6117ef838381511561187b5781518083602001fd5b8060405162461bcd60e51b815260040161009f9190612087565b6000806000606084860312156118aa57600080fd5b505081359360208301359350604090920135919050565b634e487b7160e01b600052604160045260246000fd5b6040516101a081016001600160401b03811182821017156118fa576118fa6118c1565b60405290565b604051601f8201601f191681016001600160401b0381118282101715611928576119286118c1565b604052919050565b6001600160a01b038116811461194557600080fd5b50565b803561195381611930565b919050565b6001600160401b038116811461194557600080fd5b803561195381611958565b6001600160601b038116811461194557600080fd5b803561195381611978565b62ffffff8116811461194557600080fd5b803561195381611998565b801515811461194557600080fd5b8035611953816119b4565b80356001600160501b038116811461195357600080fd5b6000808284036101c08112156119f957600080fd5b6101a080821215611a0957600080fd5b611a116118d7565b915084358252611a2360208601611948565b6020830152611a346040860161196d565b6040830152611a4560608601611948565b6060830152611a5660808601611948565b6080830152611a6760a0860161198d565b60a0830152611a7860c0860161196d565b60c0830152611a8960e0860161196d565b60e0830152610100611a9c8187016119a9565b90830152610120611aae8682016119c2565b90830152610140611ac086820161198d565b90830152610160611ad286820161198d565b90830152610180611ae48682016119cd565b90830152909593013593505050565b82516001600160a01b0316815261018081016020840151611b1b602084018262ffffff169052565b506040840151611b2f604084018215159052565b506060840151611b4a60608401826001600160401b03169052565b506080840151611b6560808401826001600160a01b03169052565b5060a0840151611b8060a08401826001600160601b03169052565b5060c0840151611b9b60c08401826001600160a01b03169052565b5060e0840151611bb660e08401826001600160501b03169052565b50610100848101516001600160401b03908116918401919091526101208086015190911690830152610140938401516001600160801b031693820193909352610160015290565b60005b83811015611c18578181015183820152602001611c00565b50506000910152565b600080600060608486031215611c3657600080fd5b8351611c41816119b4565b6020850151909350600f8110611c5657600080fd5b60408501519092506001600160401b0380821115611c7357600080fd5b818601915086601f830112611c8757600080fd5b815181811115611c9957611c996118c1565b611cac601f8201601f1916602001611900565b9150808252876020828501011115611cc357600080fd5b611cd4816020840160208601611bfd565b5080925050509250925092565b634e487b7160e01b600052601160045260246000fd5b8181038181111561162657611626611ce1565b600f8110611d2857634e487b7160e01b600052602160045260246000fd5b9052565b602081016116268284611d0a565b8381526001600160a01b0383166020820152606081016117ef6040830184611d0a565b60008060008060808587031215611d7357600080fd5b8451611d7e81611978565b6020860151909450611d8f81611978565b6040860151909350611da081611958565b6060860151909250611db181611958565b939692955090935050565b600060208284031215611dce57600080fd5b815161171381611930565b6001600160a01b039690961686526001600160401b0394909416602086015260408501929092526001600160501b0316606084015215156080830152151560a082015260c00190565b600060208284031215611e3457600080fd5b5051919050565b600060208284031215611e4d57600080fd5b815161171381611998565b600060208284031215611e6a57600080fd5b815164ffffffffff8116811461171357600080fd5b80516001600160a01b031682526020810151611ea3602084018263ffffffff169052565b506040810151611ebe60408401826001600160401b03169052565b506060810151611ed960608401826001600160a01b03169052565b506080810151611ef460808401826001600160a01b03169052565b5060a0810151611f0f60a08401826001600160601b03169052565b5060c0810151611f2a60c08401826001600160401b03169052565b5060e0810151611f4560e08401826001600160401b03169052565b506101008181015162ffffff169083015261012080820151151590830152610140808201516001600160601b0390811691840191909152610160808301519084015261018080830151909116908301526101a08082015164ffffffffff908116918401919091526101c0808301516001600160501b0316908401526101e0808301519091169083015261020090810151910152565b61022081016116268284611e7f565b82815261024081016117136020830184611e7f565b6001600160601b0382811682821603908082111561201e5761201e611ce1565b5092915050565b634e487b7160e01b600052603260045260246000fd5b8082018082111561162657611626611ce1565b60006020828403121561206057600080fd5b8151611713816119b4565b6000825161207d818460208701611bfd565b9190910192915050565b60208152600082518060208401526120a6816040850160208701611bfd565b601f01601f1916919091016040019291505056fea2646970667358221220593f3c0c88483ac76f3738c19577d20d6a73a896620e410a9219034d421b17d064736f6c63430008130033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100365760003560e01c80638e580c9c1461003b578063a75d6e5214610050575b600080fd5b61004e610049366004611895565b610063565b005b61004e61005e3660046119e4565b6104c2565b3330146100a85760405162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e481cd95b198818d85b1b60921b60448201526064015b60405180910390fd5b60007fdf4ccd96bfd9057e01ad44fd6c4745693e8ef807b15c7a0f659244231ebda883600085815260018281016020908152604080842081516101608101835281546001600160a01b038082168352600160a01b80830462ffffff1696840196909652600160b81b820460ff161515948301859052600160c01b9091046001600160401b0390811660608401529583015480821660808401528590046001600160601b031660a0830152600283015490811660c0830152939093046001600160501b031660e084015260030154808416610100840152680100000000000000008104909316610120830152600160801b9092046001600160801b03166101408201529293506101b757836101b9565b845b90506000806000306001600160a01b0316630a384f3186866040518363ffffffff1660e01b81526004016101ee929190611af3565b600060405180830381865afa15801561020b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526102339190810190611c21565b9250925092508261036c5760c0850151855160a08701516001600160a01b038316600090815260028a016020526040812080546001600160601b0390931692839290610280908490611cf7565b909155505060008c81526001808b01602052604082208281559081018290556002810180546001600160f01b0319169055600301556102c96001600160a01b0384168383610bcd565b8b826001600160a01b03167f2f8b631074b09d8b7f3fbc9184a5ddb3a59bc2bff68c6511e7e6d3d7a26159da876040516103039190611d2c565b60405180910390a3604051633d0ac8dd60e11b81523090637a1591ba90610332908f9086908a90600401611d3a565b600060405180830381600087803b15801561034c57600080fd5b505af1158015610360573d6000803e3d6000fd5b505050505050506104b7565b600080600080848060200190518101906103869190611d5d565b6001600160401b0381166101208e0152929650909450925090506103af8a8a8f87878d88610c99565b8860a001516001600160601b03168a60020160008b60c001516001600160a01b03166001600160a01b0316815260200190815260200160002060008282546103f79190611cf7565b909155505060c089015160008e81526001808d01602090815260408084208481559283018490556002830180546001600160f01b031916905560039092019290925580516307d3576360e11b815290516104b1923092630fa6aec692600480830193928290030181865afa158015610473573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104979190611dbc565b6001600160a01b038316906001600160601b038716610bcd565b50505050505b505050505050505050565b3330146105025760405162461bcd60e51b815260206004820152600e60248201526d1bdb9b1e481cd95b198818d85b1b60921b604482015260640161009f565b60007fdf4ccd96bfd9057e01ad44fd6c4745693e8ef807b15c7a0f659244231ebda88390506105438184608001518560a001516001600160601b031661133b565b6000306001600160a01b031663d89297e8856060015186604001518688610180015189610120015160016040518763ffffffff1660e01b815260040161058e96959493929190611dd9565b6020604051808303816000875af11580156105ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d19190611e22565b6020858101516001600160a01b0390811660009081526004868101909352604080822060808a01516101408b01516101008c01519351631753dfad60e21b815291909516958101959095526001600160601b03909316602485015262ffffff16604484015292935091903090635d4f7eb4906064016020604051808303816000875af1158015610665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106899190611e3b565b9050600060405180610220016040528088602001516001600160a01b03168152602001848054905063ffffffff16815260200188604001516001600160401b0316815260200188606001516001600160a01b0316815260200188608001516001600160a01b031681526020018860a001516001600160601b031681526020018860c001516001600160401b031681526020018860e001516001600160401b031681526020018362ffffff168152602001886101200151151581526020018861014001516001600160601b031681526020018581526020018861016001516001600160601b031681526020014264ffffffffff1681526020018861018001516001600160501b03168152602001306001600160a01b031663cd7a9f478a606001518b61012001516040518363ffffffff1660e01b81526004016107e29291906001600160a01b039290921682521515602082015260400190565b602060405180830381865afa1580156107ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108239190611e58565b64ffffffffff168152602001610837611404565b8152509050808560030160008960000151815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160020160146101000a8154816001600160601b0302191690836001600160601b0316021790555060c08201518160030160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160030160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160030160106101000a81548162ffffff021916908362ffffff1602179055506101208201518160030160136101000a81548160ff0219169083151502179055506101408201518160030160146101000a8154816001600160601b0302191690836001600160601b0316021790555061016082015181600401556101808201518160050160006101000a8154816001600160601b0302191690836001600160601b031602179055506101a082015181600501600c6101000a81548164ffffffffff021916908364ffffffffff1602179055506101c08201518160050160116101000a8154816001600160501b0302191690836001600160501b031602179055506101e082015181600501601b6101000a81548164ffffffffff021916908364ffffffffff16021790555061020082015181600601559050508287600001519080600181540180825580915050600190039060005260206000200160009091909190915055866000015181600001516001600160a01b03167fa858fcdefab65cbd1997932d8ac8aa1a9a8c46c90b20947575525d9a2a437f8c83604051610b649190611fda565b60405180910390a3865160405163d98a933560e01b8152309163d98a933591610b9291908590600401611fe9565b600060405180830381600087803b158015610bac57600080fd5b505af1158015610bc0573d6000803e3d6000fd5b5050505050505050505050565b600080610bd861147b565b91509150801580610bfb5750816001600160a01b0316856001600160a01b031614155b80610c065750833b15155b15610c2457610c1f6001600160a01b03861685856114b2565b610c92565b604051632e1a7d4d60e01b8152600481018490526001600160a01b03861690632e1a7d4d90602401600060405180830381600087803b158015610c6657600080fd5b505af1158015610c7a573d6000803e3d6000fd5b50610c92925050506001600160a01b03851684611509565b5050505050565b600083858860a00151610cac9190611ffe565b610cb69190611ffe565b9050610cd0888860c00151836001600160601b031661133b565b608087015160e08801516040808a01519051631b1252fd60e31b8152600093309363d89297e893610d0c9389928b929091600190600401611dd9565b6020604051808303816000875af1158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f9190611e22565b60c089015160208a0151604051631753dfad60e21b81526001600160a01b0390921660048301526001600160601b038916602483015262ffffff1660448201529091506000903090635d4f7eb4906064016020604051808303816000875af1158015610dbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de39190611e3b565b905060008a60040160008b600001516001600160a01b03166001600160a01b03168152602001908152602001600020905060006040518061022001604052808c600001516001600160a01b03168152602001838054905063ffffffff168152602001876001600160401b031681526020018c608001516001600160a01b031681526020018c60c001516001600160a01b03168152602001866001600160601b031681526020018c61010001516001600160401b031681526020018c61012001516001600160401b031681526020018462ffffff1681526020018c60400151151581526020018a6001600160601b03168152602001858152602001896001600160601b031681526020014264ffffffffff1681526020018c60e001516001600160501b03168152602001306001600160a01b031663cd7a9f478e608001518f604001516040518363ffffffff1660e01b8152600401610f589291906001600160a01b039290921682521515602082015260400190565b602060405180830381865afa158015610f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f999190611e58565b64ffffffffff168152602001610fad611404565b8152509050808c60030160008c815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a8154816001600160401b0302191690836001600160401b0316021790555060608201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160020160146101000a8154816001600160601b0302191690836001600160601b0316021790555060c08201518160030160006101000a8154816001600160401b0302191690836001600160401b0316021790555060e08201518160030160086101000a8154816001600160401b0302191690836001600160401b031602179055506101008201518160030160106101000a81548162ffffff021916908362ffffff1602179055506101208201518160030160136101000a81548160ff0219169083151502179055506101408201518160030160146101000a8154816001600160601b0302191690836001600160601b0316021790555061016082015181600401556101808201518160050160006101000a8154816001600160601b0302191690836001600160601b031602179055506101a082015181600501600c6101000a81548164ffffffffff021916908364ffffffffff1602179055506101c08201518160050160116101000a8154816001600160501b0302191690836001600160501b031602179055506101e082015181600501601b6101000a81548164ffffffffff021916908364ffffffffff1602179055506102008201518160060155905050818a9080600181540180825580915050600190039060005260206000200160009091909190915055898b600001516001600160a01b03167fa858fcdefab65cbd1997932d8ac8aa1a9a8c46c90b20947575525d9a2a437f8c836040516112ce9190611fda565b60405180910390a360405163d98a933560e01b8152309063d98a9335906112fb908d908590600401611fe9565b600060405180830381600087803b15801561131557600080fd5b505af1158015611329573d6000803e3d6000fd5b50505050505050505050505050505050565b600683016000805b8254611350908290611622565b1561139c576001600160a01b03851683828154811061137157611371612025565b6000918252602090912001546001600160a01b031603611394576001915061139c565b600101611343565b50806113ce5781546001810183556000838152602090200180546001600160a01b0319166001600160a01b0386161790555b6001600160a01b0384166000908152600586016020526040812080548592906113f890849061203b565b90915550505050505050565b600060643b156114765760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561144d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114719190611e22565b905090565b504390565b60008046608e1981016114a75750733bd359c1119da7da1d913d1c4d2b7c461115433a92600192509050565b506000928392509050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261150490849061162c565b505050565b804710156115595760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161009f565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146115a6576040519150601f19603f3d011682016040523d82523d6000602084013e6115ab565b606091505b50509050806115045760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161009f565b8082105b92915050565b6000611681826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166117019092919063ffffffff16565b90508051600014806116a25750808060200190518101906116a2919061204e565b6115045760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161009f565b6060611710848460008561171a565b90505b9392505050565b60608247101561177b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161009f565b600080866001600160a01b03168587604051611797919061206b565b60006040518083038185875af1925050503d80600081146117d4576040519150601f19603f3d011682016040523d82523d6000602084013e6117d9565b606091505b50915091506117ea878383876117f7565b925050505b949350505050565b6060831561186657825160000361185f576001600160a01b0385163b61185f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161009f565b50816117ef565b6117ef838381511561187b5781518083602001fd5b8060405162461bcd60e51b815260040161009f9190612087565b6000806000606084860312156118aa57600080fd5b505081359360208301359350604090920135919050565b634e487b7160e01b600052604160045260246000fd5b6040516101a081016001600160401b03811182821017156118fa576118fa6118c1565b60405290565b604051601f8201601f191681016001600160401b0381118282101715611928576119286118c1565b604052919050565b6001600160a01b038116811461194557600080fd5b50565b803561195381611930565b919050565b6001600160401b038116811461194557600080fd5b803561195381611958565b6001600160601b038116811461194557600080fd5b803561195381611978565b62ffffff8116811461194557600080fd5b803561195381611998565b801515811461194557600080fd5b8035611953816119b4565b80356001600160501b038116811461195357600080fd5b6000808284036101c08112156119f957600080fd5b6101a080821215611a0957600080fd5b611a116118d7565b915084358252611a2360208601611948565b6020830152611a346040860161196d565b6040830152611a4560608601611948565b6060830152611a5660808601611948565b6080830152611a6760a0860161198d565b60a0830152611a7860c0860161196d565b60c0830152611a8960e0860161196d565b60e0830152610100611a9c8187016119a9565b90830152610120611aae8682016119c2565b90830152610140611ac086820161198d565b90830152610160611ad286820161198d565b90830152610180611ae48682016119cd565b90830152909593013593505050565b82516001600160a01b0316815261018081016020840151611b1b602084018262ffffff169052565b506040840151611b2f604084018215159052565b506060840151611b4a60608401826001600160401b03169052565b506080840151611b6560808401826001600160a01b03169052565b5060a0840151611b8060a08401826001600160601b03169052565b5060c0840151611b9b60c08401826001600160a01b03169052565b5060e0840151611bb660e08401826001600160501b03169052565b50610100848101516001600160401b03908116918401919091526101208086015190911690830152610140938401516001600160801b031693820193909352610160015290565b60005b83811015611c18578181015183820152602001611c00565b50506000910152565b600080600060608486031215611c3657600080fd5b8351611c41816119b4565b6020850151909350600f8110611c5657600080fd5b60408501519092506001600160401b0380821115611c7357600080fd5b818601915086601f830112611c8757600080fd5b815181811115611c9957611c996118c1565b611cac601f8201601f1916602001611900565b9150808252876020828501011115611cc357600080fd5b611cd4816020840160208601611bfd565b5080925050509250925092565b634e487b7160e01b600052601160045260246000fd5b8181038181111561162657611626611ce1565b600f8110611d2857634e487b7160e01b600052602160045260246000fd5b9052565b602081016116268284611d0a565b8381526001600160a01b0383166020820152606081016117ef6040830184611d0a565b60008060008060808587031215611d7357600080fd5b8451611d7e81611978565b6020860151909450611d8f81611978565b6040860151909350611da081611958565b6060860151909250611db181611958565b939692955090935050565b600060208284031215611dce57600080fd5b815161171381611930565b6001600160a01b039690961686526001600160401b0394909416602086015260408501929092526001600160501b0316606084015215156080830152151560a082015260c00190565b600060208284031215611e3457600080fd5b5051919050565b600060208284031215611e4d57600080fd5b815161171381611998565b600060208284031215611e6a57600080fd5b815164ffffffffff8116811461171357600080fd5b80516001600160a01b031682526020810151611ea3602084018263ffffffff169052565b506040810151611ebe60408401826001600160401b03169052565b506060810151611ed960608401826001600160a01b03169052565b506080810151611ef460808401826001600160a01b03169052565b5060a0810151611f0f60a08401826001600160601b03169052565b5060c0810151611f2a60c08401826001600160401b03169052565b5060e0810151611f4560e08401826001600160401b03169052565b506101008181015162ffffff169083015261012080820151151590830152610140808201516001600160601b0390811691840191909152610160808301519084015261018080830151909116908301526101a08082015164ffffffffff908116918401919091526101c0808301516001600160501b0316908401526101e0808301519091169083015261020090810151910152565b61022081016116268284611e7f565b82815261024081016117136020830184611e7f565b6001600160601b0382811682821603908082111561201e5761201e611ce1565b5092915050565b634e487b7160e01b600052603260045260246000fd5b8082018082111561162657611626611ce1565b60006020828403121561206057600080fd5b8151611713816119b4565b6000825161207d818460208701611bfd565b9190910192915050565b60208152600082518060208401526120a6816040850160208701611bfd565b601f01601f1916919091016040019291505056fea2646970667358221220593f3c0c88483ac76f3738c19577d20d6a73a896620e410a9219034d421b17d064736f6c63430008130033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.