账户
0xe6...1996
0xe6...1996

0xe6...1996

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.13+commit.abaa5c0e
语言
Solidity
合同源代码
文件 1 的 44:Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return 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);
        }
    }
}
合同源代码
文件 2 的 44:AuxLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "./StructsLib.sol";
import "../common/Structs.sol";
import "../common/MathLib.sol";
import "../market/MarketAuxLib.sol";
import "../common/StrippedInterfaces.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";

library AuxLib {
    using MarketAuxLib for Structs.Asset;

    /// @dev Checks if an address of a token can share in fees
    /// @param state2 State2 object
    /// @param addr The address to check
    /// @return bool True if the token can share in fees
    function isShareable(StructsLib.State2 storage state2, address addr) external view returns (bool) {
        return state2.assetReg.get(addr).sharable;
    }

    /// Checks if an address is a verified token
    /// @param state2 State2 object
    /// @param addr The address to check
    /// @return bool True if the token is verified
    function isVerified(StructsLib.State2 storage state2, address addr) external view returns (bool) {
        return state2.assetReg.get(addr).verified;
    }

    /// @dev Checks if an address is marked as a stable token
    /// @param state2 State2 object
    /// @param addr The address to check
    /// @return bool True if the token is stable asset
    function isStable(StructsLib.State2 storage state2, address addr) external view returns (bool) {
        return state2.assetReg.get(addr).stable;
    }

    /// @dev Checks if a market is composed of two stable tokens
    /// @param assetReg Asset registry contract
    /// @param m Market object
    /// @return bool True if both tokens are stable
    function isStable(IAssetRegistry assetReg, Structs.Market memory m) external view returns (bool) {
        return assetReg.get(m.base.addr).stable && assetReg.get(m.quote.addr).stable;
    }

    /// @dev Checks if at order at index id exist
    /// @param id The order ID or index.
    /// @return bool True if the order exist
    function hasOrder(StructsLib.State storage state, uint256 id) public view returns (bool) {
        return id < state.orders.length && state.orders[id].marketId != address(0);
    }

    /// @dev Checks whether addr can create a dispute.
    /// Reverts if order is unknown.
    /// Returns false if market has a market manager
    /// Returns false if order has been released.
    /// Returns false if addr is not a provider or a taker.
    /// Returns false if order has not be marked as `paid` and time to pay has not been reached.
    /// Returns false if order dispute creation time is in the future
    /// @param state State object
    /// @param id The order ID.
    /// @param addr The address to check if permitted to create dispute.
    /// @return bool True if addr can create a dispute
    function canCreateDispute(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        uint256 id,
        address addr
    ) public view returns (bool) {
        require(hasOrder(state, id), "OC: UNKNOWN_ORDER");
        Structs.Order memory o = state.orders[id];
        if (state2.marketCore.getMarket(o.marketId).manager != address(0)) return false;
        if (o.provider != addr && o.taker != addr) return false;
        if (o.released || o.cancelled || !o.paid) return false;
        if (o.disputeFrom > block.timestamp) return false;
        return true;
    }

    /// @dev Checks whether a market is permissioned.
    /// A market is permissioned if its PUT or SWAP permission is set to true.
    /// @param market Market object
    function isPerm(Structs.Market memory market) public pure returns (bool) {
        return market.perms.put || market.perms.swap;
    }

    /// @dev Checks whether a liquidity is empty
    /// @param liq Liquidity object
    function isLiquidityEmpty(Structs.Liquidity memory liq) public pure returns (bool) {
        return liq.amount == 0 && liq.ids.length == 0;
    }

    /// @dev Check whether a liquidity exists
    /// @param state State object
    /// @param mid The market ID
    /// @param provider The address of the liquidity provider
    /// @param lid The liquidity ID
    /// @return bool True if the liquidity exists
    function isLiquidityExist(StructsLib.State storage state, address mid, address provider, uint256 lid) public view returns (bool) {
        return state.liquidity[mid][provider].length > lid;
    }

    /// @dev Checks whether a liquidity exists and has non-zero (not empty) liquidity
    /// @param state State object
    /// @param mid The market ID
    /// @param provider The address of the liquidity provider
    /// @param lid The liquidity ID
    /// @return bool True if the liquidity exists and has non-zero liquidity
    function hasNonZeroLiquidity(StructsLib.State storage state, address mid, address provider, uint256 lid) public view returns (bool) {
        if (!isLiquidityExist(state, mid, provider, lid)) return false;
        Structs.Liquidity memory liq = state.liquidity[mid][provider][lid];
        return !isLiquidityEmpty(liq);
    }

    /// @dev Checks if an uint256 array has unique values
    /// @param arr The array to check
    /// @return bool True if the array has unique values
    function isUnique(uint256[] memory arr) public pure returns (bool) {
        if (arr.length == 0) return true;
        for (uint256 i = 0; i < arr.length - 1; i++) {
            for (uint256 j = i + 1; j < arr.length; j++) {
                if (arr[i] == arr[j]) return false;
            }
        }
        return true;
    }

    /// @dev Checks whether arr length is not more than 16
    /// @param arr The array to check
    /// @return bool True if the array length is not more than 16
    function isMaxLen16(uint256[] memory arr) public pure returns (bool) {
        return arr.length <= 16;
    }

    /// @dev Checks whether addr is zero
    /// @param addr The address to check
    /// @return bool True if the address is zero
    function isZero(address addr) public pure returns (bool) {
        return addr == address(0);
    }

    /// @dev Return _default if _target is zero, otherwise _target is returned
    /// @param _target The preferred value if not zero
    /// @param _default The fallback value if _target is zero
    function setOrDef(address _target, address _default) public pure returns (address) {
        return (_target != address(0)) ? _target : _default;
    }

    /// @dev Get fee to be charged from the given asset
    /// @param assetReg Asset registry contract
    /// @param asset Asset object
    /// @param defaultBp Default fee basis point if asset registry does not have fee info
    /// for the given asset
    /// @return feeBp Fee basis point
    /// @return fixedFee Fee basis point
    /// @return curRate Fee basis point
    function getFeeInfo(
        IAssetRegistry assetReg,
        Structs.Asset memory asset,
        uint256 defaultBp
    ) public view returns (uint256 feeBp, uint256 fixedFee, uint256 curRate) {
        Info memory inf = assetReg.get(asset.addr);
        if (inf.feeBp == 0) inf.feeBp = defaultBp;
        return (inf.feeBp, inf.feeFixed, inf.curRate);
    }

    /// @dev Check if addr owns a native NFT
    /// @param nft The address of the NFT
    /// @param addr The address to check
    /// @return bool True if addr owns a native NFT
    function hasNativeNFT(address nft, address addr) public view returns (bool) {
        return nft != address(0) && IERC721Strip(nft).balanceOf(addr) > 0;
    }

    /// Check parameters for a SWAP operation involving a fungible base asset
    /// @param sp SwapSlot object
    /// @param mkt Market object
    /// @param liq Liquidity object
    function checkFungibleSwapParams(StructsLib.SwapSlot memory sp, Structs.Market memory mkt, Structs.Liquidity storage liq) public view {
        require(mkt.quote.isNFT() || sp.amount > 0, "OC: AMOUNT_REQ");
        require(!mkt.quote.isNFT() || sp.amount == 0, "OC: AMOUNT_NREQ");
        require((sp.offerId > 0 && mkt.perms.allowZeroOfferPrice) || mkt.quote.isNFT() || sp.maxPrice > 0, "OC: MAX_PRICE_REQ");
        require(!mkt.quote.isNFT() || sp.maxPrice == 0, "OC: MAX_PRICE_NREQ");
        require(!mkt.quote.isNFT() || sp.ids.length > 0, "OC: IDS_REQ");
        require(mkt.quote.isNFT() || sp.ids.length == 0, "OC: IDS_NREQ");
        require(isMaxLen16(sp.ids), "OC: IDS_HSIZE");
        require(isUnique(sp.ids), "OC: IDS_NUNIQ");
        require(!liq.paused, "OC: LIQ_PAUSED");
        require(sp.amount <= liq.amount, "OC: LOW_LIQ");
        require(sp.offerId > 0 || sp.amount >= liq.minSwap, "OC: AMOUNT_BELOW_MIN_SWAP");
        require(sp.offerId > 0 || liq.maxSwap == 0 || sp.amount <= liq.maxSwap, "OC: AMOUNT_ABOVE_MAX_SWAP");
        require(sp.offerId > 0 || sp.maxPrice == 0 || sp.maxPrice >= liq.price, "OC: MAX_PRICE_TOO_LOW");
    }

    /// @dev Check parameters for a SWAP operation involving a non-fungible base asset
    /// @param sp SwapSlot object
    /// @param mkt Market object
    /// @param liq Liquidity object
    function checkNonFungibleSwapParams(
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public view {
        require(sp.amount == 0, "OC: AMOUNT_NREQ");
        require(!liq.paused, "OC: LIQ_PAUSED");
        require((sp.offerId > 0 && mkt.perms.allowZeroOfferPrice) || mkt.quote.isNFT() || sp.maxPrice > 0, "OC: MAX_PRICE_REQ");
        require(!mkt.quote.isNFT() || sp.maxPrice == 0, "OC: MAX_PRICE_NREQ");
        require(sp.offerId > 0 || sp.maxPrice == 0 || sp.maxPrice >= liq.price, "OC: MAX_PRICE_TOO_LOW");
        require(mkt.quote.isNFT() || sp.ids.length == 0, "OC: IDS_NREQ");
        require((sp.offerId > 0 && mkt.perms.allowZeroOfferPrice) || !mkt.quote.isNFT() || sp.ids.length > 0, "OC: IDS_REQ");
    }
}
合同源代码
文件 3 的 44:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
合同源代码
文件 4 的 44:Core.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Ownable.sol";
import "../common/Structs.sol";
import "./StructsLib.sol";
import "../market/interfaces/IMarketCore.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "../pools/interfaces/IFeePool.sol";
import "./WithdrawLib.sol";
import "./AuxLib.sol";
import "./FeeLib.sol";
import "./LiquidityLib.sol";
import "./OfferLib.sol";
import "./SwapLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../token/interfaces/IERC20.sol";
import "./FungibleLiquidityLib.sol";
import "./NonFungibleLiquidityLib.sol";

/// Core manages market orders
contract Core is Ownable, ERC1155Holder {
    StructsLib.State public state;
    StructsLib.State2 public state2;

    event Swap(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        address releaser,
        uint256[6] numTypeInfo,
        bool byDispute,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );
    event SwapOrder(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        uint256[7] numTypeInfo,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );
    event SwapOrderPaid(uint256 indexed orderID, address payer, uint256 disputeDelayDur);
    event OrderCancelled(uint256 indexed orderID, address indexed canceller, bool indexed byDispute);
    event TransferredToFeePool(address indexed token, uint256 amount, address convAsset, uint256 convAmount);
    event FinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);
    event ProtocolFeeUpdated(
        uint256 instantSwapFeeBp,
        uint256 synthSwapFeeBp,
        uint256 stableSwapFeeBp,
        uint256 fixedSynthSwapFee,
        uint256 fixedNFTSwapFee,
        uint256 instantNFTSwapFeeBp
    );
    event ProtocolFeeSplitUpdated(uint256 inventor, uint256 shared, uint256 operator);
    event TransferInventorBalance(address owner, address token, uint256 amount);
    event TransferOperatorBalance(address owner, address token, uint256 amount);
    event UpdatedFungibleLiquidity(
        address indexed provider,
        address indexed marketId,
        address target,
        bool deduct,
        bool paused,
        uint256 liq,
        uint256 amount,
        uint256 price,
        uint256[] wantOrIds,
        uint256[] wantAndIds,
        uint256 minSwap,
        uint256 maxSwap,
        uint256 timeToPay
    );
    event UpdatedNonFungibleLiquidity(
        address indexed provider,
        address indexed marketId,
        address target,
        bool deduct,
        bool paused,
        uint256 lid,
        uint256[] ids,
        uint256 price,
        uint256[] wantOrIds,
        uint256[] wantAndIds,
        uint256 timeToPay
    );
    event UpdatedPrice(address indexed marketId, address indexed provider, address indexed manager, uint256 lid, uint256 price);
    event TogglePause(address indexed marketId, address indexed provider, uint256 lid, bool paused);
    event UpdatedLiquidityManager(address indexed marketId, address indexed provider, address indexed manager, address oldManager);
    event NFTDiscountUpdated(uint256 bp);
    event NewOffer(
        address marketId,
        address indexed provider,
        uint256 lid,
        address indexed offerer,
        uint256 offerId,
        uint256 amount,
        uint256 price,
        uint256[] ids,
        uint256 expireDuration
    );
    event OfferUpdated(
        uint256 indexed offerId,
        address indexed offerer,
        uint256 amount,
        uint256 price,
        uint256[] ids,
        uint256 execDelayDur
    );
    event OfferAccepted(uint256 indexed offerId, address indexed acceptor, uint256 executeAt);
    event OfferCancelled(uint256 indexed offerId, address indexed canceller);
    event OfferExecuted(uint256 indexed offerId, address indexed executor);
    event OfferUnaccepted(uint256 indexed offerId, address indexed unacceptor);
    event UpdatedLiquiditySwapBadges(address market, address provider, uint256 liq, string[2][] badges);
    event UpdatedFeePurse(address addr);
    event UpdatedBadge(address addr);
    event UpdatedMarketCore(address addr);
    event UpdatedFeePool(address addr);
    event UpdatedInventor(address addr);
    event UpdatedFeePoolConfig(uint256 interval);
    event UpdatedAssetRegistry(address addr);
    event UpdatedMinTimeToPay(uint256 val);
    event UpdatedOrderGraceDur(uint256 val);
    event UpdatedMaxOpenOrders(uint256 val);
    event UpdatedMaxOpenDisputes(uint256 val);
    event UpdatedDisputeManager(address addr);
    event UpdatedNFT(address addr);
    event UpdatedOperator(address addr);
    event UpdatedMinerRegistry(address addr);
    event UpdatedAMMInfo(address swapRouter, address ethErc20Addr, uint256 poolFee);
    event UpdatedAMMPoolInfo(address input, address output, uint256 poolFee);

    /// @dev Modifier to ensure a market exist
    /// @param id The market ID
    modifier marketMustExist(address id) {
        require(IMarketCore(state2.marketCore).getMarket(id).createdAt > 0, "OC: UNKNOWN_MARKET");
        _;
    }

    /// @dev Modifier to sender is deployer
    modifier onlyDisputeManager() {
        require(msg.sender == state2.disputeManager, "OC: NOT_DISPUTE_MGR");
        _;
    }

    /// @dev Modifier to ensure an expiry time has not elapsed.
    /// If expireAt = 0, it means not expired.
    /// @param expireAt The expiry time
    modifier notExpired(uint256 expireAt) {
        require(expireAt == 0 || expireAt > block.timestamp, "OC: EXPIRED_TX");
        _;
    }
    /**
     * @dev Throws if called by any account other than the inventor.
     */
    modifier onlyInventor() {
        require(state.inventor == msg.sender, "OC: NOT_INVENTOR");
        _;
    }

    /// @notice Constructor
    /// @param _marketCore The address of the market core contract
    /// @param _token The address of the native token contract
    /// @param _minTimeToPay The minimum time off-chain payers have to make payment
    /// @param _orderGraceDur The grace period after which an order can be cancelled off-chain payment is not made
    /// @param _maxOpenOrders The maximum number of open orders allowed per liquidity provider
    /// @param _maxOpenDisputes The maximum number of open disputes allowed per liquidity provider
    constructor(
        address _marketCore,
        address _token,
        uint256 _minTimeToPay,
        uint256 _orderGraceDur,
        uint256 _maxOpenOrders,
        uint256 _maxOpenDisputes
    ) {
        state.minTimeToPay = _minTimeToPay;
        state.orderGraceDur = _orderGraceDur;
        state.ints.maxOpenOrders = _maxOpenOrders;
        state.ints.maxOpenDisputes = _maxOpenDisputes;
        state.feePoolTxInterval = 12 hours;
        state.inventor = msg.sender;
        state.feeBPs[0] = 5000;
        state.feeBPs[1] = 5000;
        state.feeBPs[2] = 0;
        state2.token = IERC20(_token);
        state2.marketCore = IMarketCore(_marketCore);
        state2.ints.nftDiscountBp = 2500;
    }

    /// @notice Set asset registry
    /// @param addr The address of the asset registry contract
    function setAssetRegistry(address addr) external onlyOwner onlyOwner {
        state2.assetReg = IAssetRegistry(addr);
        emit UpdatedAssetRegistry(addr);
    }

    /// @notice Set default time to pay
    /// @param val The new value
    function setMinTimeToPay(uint256 val) external onlyOwner {
        state.minTimeToPay = val;
        emit UpdatedMinTimeToPay(val);
    }

    /// @notice Set order grace period duration
    /// @param val The new value
    function setOrderGraceDur(uint256 val) external onlyOwner {
        state.orderGraceDur = val;
        emit UpdatedOrderGraceDur(val);
    }

    /// @notice Set max open orders
    /// @param val The new value
    function setMaxOpenOrders(uint256 val) external onlyOwner {
        state.ints.maxOpenOrders = val;
        emit UpdatedMaxOpenOrders(val);
    }

    /// @notice Set max open disputes
    /// @param val The new value
    function setMaxOpenDisputes(uint256 val) external onlyOwner {
        state.ints.maxOpenDisputes = val;
        emit UpdatedMaxOpenDisputes(val);
    }

    /// @notice Updated the dispute manager contract
    /// @param addr The new address
    function setDisputeManager(address addr) external onlyOwner {
        state2.disputeManager = addr;
        emit UpdatedDisputeManager(addr);
    }

    /// @notice Updated the Operator contract
    /// @param addr The new address
    function setOperator(address addr) external onlyOwner {
        state2.operatorMgr = IOperator(addr);
        emit UpdatedOperator(addr);
    }

    /// @notice Updated the MinerRegistry contract
    /// @param addr The new address
    function setMinerRegistry(address addr) external onlyOwner {
        state2.minerReg = IMinerRegistry(addr);
        emit UpdatedMinerRegistry(addr);
    }

    /// @notice Updated the NFT contract
    /// @param addr The new address
    function setNFT(address addr) external onlyOwner {
        state2.nft = addr;
        emit UpdatedNFT(addr);
    }

    /// @notice Set native AMM information
    /// @param swapRouter The address of the swap router
    /// @param ethErc20Addr The address of the ETH ERC20 token
    /// @param poolFee The fee tier of ETH pool on the native AMM DEX
    function setAmmInfo(address swapRouter, address ethErc20Addr, uint256 poolFee) external onlyOwner {
        state2.ammEth = ethErc20Addr;
        state2.ints.ammEthPoolFee = poolFee;
        state.ammSwapRouter = swapRouter;
        emit UpdatedAMMInfo(swapRouter, ethErc20Addr, poolFee);
    }

    /// @notice Set AMM pool info a given input address
    /// @param input The input token address
    /// @param out The output token address
    /// @param poolFee The fee tier of the pool
    function setAmmPoolInfo(address input, address out, uint256 poolFee) external onlyOwner {
        state2.ammSwapPool[input] = StructsLib.AmmSwapPoolInfo(out, poolFee);
        emit UpdatedAMMPoolInfo(input, out, poolFee);
    }

    /// @notice Updated the FeePurse contract
    /// @param val The new address
    function setFeePurse(address val) external onlyOwner {
        state2.feePurse = IFeePurse(val);
        emit UpdatedFeePurse(val);
    }

    /// @notice Updated the Badge contract
    /// @param val The new address
    function setBadgeMgr(address val) external onlyOwner {
        state2.badge = IBadge(val);
        emit UpdatedBadge(val);
    }

    /// @notice Set market core address
    /// @param addr The new address
    function setMarketCore(address addr) external onlyOwner {
        state2.marketCore = IMarketCore(addr);
        emit UpdatedMarketCore(addr);
    }

    /// @notice Set fee rewarder contract address
    /// @param addr The new address
    function setFeePool(address addr) external onlyOwner {
        state2.feePool = IFeePool(addr);
        emit UpdatedFeePool(addr);
    }

    /// @notice Updated the protocol inventor
    /// @param addr The new address
    function setInventor(address addr) external onlyInventor {
        state.inventor = addr;
        emit UpdatedInventor(addr);
    }

    /// @notice Set the reward per fee sharer transfer
    /// @param interval The call interval
    function configureFeePool(uint256 interval) external onlyOwner {
        state.feePoolTxInterval = interval;
        emit UpdatedFeePoolConfig(interval);
    }

    /// @notice Set NFT holder discount
    /// @param bp - The new base point
    function setDiscountForNFTHolder(uint256 bp) external onlyOwner {
        state2.ints.nftDiscountBp = bp;
        emit NFTDiscountUpdated(bp);
    }

    /// @notice Set protocol fee split
    /// @param inventorBP The base point for inventor
    /// @param sharedBP The base point for shared with holders
    /// @param operatorBP The base point for operators
    function setProtoFeeSplit(uint256 inventorBP, uint256 sharedBP, uint256 operatorBP) public onlyOwner {
        require(inventorBP >= 3000, "OC: INV_BP_TOO_LOW");
        require(inventorBP + sharedBP + operatorBP == 10000, "OC: UNBALANCED");
        state.feeBPs[0] = inventorBP;
        state.feeBPs[1] = sharedBP;
        state.feeBPs[2] = operatorBP;
        emit ProtocolFeeSplitUpdated(inventorBP, sharedBP, operatorBP);
    }

    /// @notice Get protocol fee basis points
    function getFeeBPs() public view returns (uint256[3] memory) {
        return state.feeBPs;
    }

    /// Checks if an address is a verified token
    /// @param addr The address to check
    function isVerified(address addr) public view returns (bool) {
        return AuxLib.isVerified(state2, addr);
    }

    /// @notice Updated the protocol fee
    /// @param _instantSwapFeeBp Fee for ERC20/ERC20 swap
    /// @param _synthSwapFeeBp Fee for ERC20/SYNTH swap
    /// @param _stableSwapFeeBp Fee for ERC20(stablecoin)/ERC20(stablecoin)
    /// @param _fixedSynthSwapFee Fee for SYNTH/SYNTH swap (in NATIVE_TOKEN)
    /// @param _fixedNFTSwapFee Fee for NFT/NFT swap (in NATIVE_TOKEN)
    /// @param _instantNFTSwapFeeBp Fee for NFT/ERC20 swap
    function setProtoFee(
        uint256 _instantSwapFeeBp,
        uint256 _synthSwapFeeBp,
        uint256 _stableSwapFeeBp,
        uint256 _fixedSynthSwapFee,
        uint256 _fixedNFTSwapFee,
        uint256 _instantNFTSwapFeeBp
    ) public {
        state.ints.instantSwapFeeBp = _instantSwapFeeBp;
        state.ints.synthSwapFeeBp = _synthSwapFeeBp;
        state.ints.stableSwapFeeBp = _stableSwapFeeBp;
        state.ints.instantNFTSwapFeeBp = _instantNFTSwapFeeBp;
        state.ints.fixedSynthSwapFee = _fixedSynthSwapFee;
        state.ints.fixedNFTSwapFee = _fixedNFTSwapFee;
        emit ProtocolFeeUpdated(
            _instantSwapFeeBp,
            _synthSwapFeeBp,
            _stableSwapFeeBp,
            _fixedSynthSwapFee,
            _fixedNFTSwapFee,
            _instantNFTSwapFeeBp
        );
    }

    /// @notice Returns the balance of fees earned by an address.
    /// @param _owner The owner address
    /// @param _asset The asset whose balance is returned
    function feeBalanceOf(address _owner, address _asset) external view returns (uint256) {
        return state.feeBalance[_owner][_asset];
    }

    /// @notice Returns the balance of fees earned by the protocol
    /// @param asset The target asset
    function inventorBalanceOf(address asset) external view returns (uint256) {
        return state.inventorBalance[asset];
    }

    /// @notice Returns the sharable fee balance of an asset
    /// @param asset The target asset
    function shareablesBalanceOf(address asset) external view returns (uint256) {
        return state.shareablesBalance[asset];
    }

    /// @notice Returns the operator balance fee balance of an asset
    /// @param asset The target asset
    function operatorBalanceOf(address operator, address asset) external view returns (uint256) {
        return state.operatorsBalance[operator][asset];
    }

    /// @notice Withdraw fees earned by sender
    /// @param asset The address of the asset the fee is denominated in.
    function withdrawFeeBalance(address asset) external {
        uint256 bal = state.feeBalance[msg.sender][asset];
        state.feeBalance[msg.sender][asset] = 0;
        IERC20(asset).transfer(msg.sender, bal);
    }

    /// @notice Swap quote asset for base asset.
    /// @param marketId The unique ID of the market.
    /// @param provider The address of the base asset liquidity provider.
    /// @param provider The address of the recipient (optional)
    /// @param lid The liquidity ID
    /// @param amount The quantity of base asset to be swapped.
    /// @param maxPrice The maximum price to pay for amount.
    ///         maxPrice protects the swapper from paying an unexpected cost
    ///         for amount in case the price was updated before the order was placed.
    /// @param ids The ids of non-fungible quote assets (optional if quote is fungible)
    /// @param txDeadline The transaction is reverted if current time >= expireAt
    /// @param operator The ID of the interface operator
    function swapQuoteForBase(
        address marketId,
        address provider,
        address recipient,
        uint256 lid,
        uint256 amount,
        uint256 maxPrice,
        uint256[] memory ids,
        uint256 txDeadline,
        uint256 operator
    ) external payable notExpired(txDeadline) marketMustExist(marketId) {
        StructsLib.SwapSlotAddresses memory addrs = StructsLib.SwapSlotAddresses(marketId, provider, recipient);
        SwapLib.swapQuoteForBase(state, state2, StructsLib.SwapSlot(lid, amount, maxPrice, ids, 0, operator, addrs));
    }

    /// @notice Compute swap fee
    /// @param mid The market ID
    /// @param provider The address of the base asset liquidity provider.
    /// @param lid The liquidity ID
    /// @param taker The address of the recipient
    /// @param orderAmt The quantity of base asset to be swapped.
    /// @param orderPrice The maximum price to pay for amount.
    function computeSwapFee(
        address mid,
        address provider,
        uint256 lid,
        address taker,
        uint256 orderAmt,
        uint256 orderPrice
    ) public view returns (uint256, uint256, uint256, uint256) {
        Structs.Market memory mkt = state2.marketCore.getMarket(mid);
        Structs.Liquidity memory liq = state.liquidity[mid][provider][lid];
        FeeInfo memory info = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                state2.assetReg,
                state2.feePurse,
                state.ints.synthSwapFeeBp,
                state.ints.instantNFTSwapFeeBp,
                state.ints.instantSwapFeeBp,
                state.ints.stableSwapFeeBp,
                state2.ints.nftDiscountBp,
                lid,
                orderAmt,
                orderPrice,
                state2.nft,
                provider,
                taker
            )
        );
        return (info.protoFee, info.mktCreatorFee, info.feeBp, info.mktFeeBp);
    }

    /// @notice Withdraw fees accrued for the protocol inventor
    /// @param asset The address of the asset the fee is denominated in.
    function withdrawInventorBalance(address asset) external onlyInventor {
        WithdrawLib.withdrawInventorBalance(state, state2, asset);
    }

    /// @notice Release an order
    /// @param id The order ID
    function releaseOrder(uint256 id) external {
        require(id <= state.orders.length - 1, "OC: UNKNOWN_ORDER");
        SwapLib.releaseOrder(state, state2, owner(), id, false);
    }

    /// @notice Release an order using a market manager's signature
    /// @param id The order ID
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function releaseOrderByManagerSig(uint256 id, uint256 expireAt, bytes calldata signature) external {
        require(id <= state.orders.length - 1, "OC: UNKNOWN_ORDER");
        SwapLib.releaseOrderByManagerSig(state, state2, owner(), id, expireAt, signature);
    }
    /// @notice Allow the dispute manager to release an order.
    /// @param id The order ID.
    function releaseOrderByDispute(uint256 id) external onlyDisputeManager {
        SwapLib.releaseOrder(state, state2, owner(), id, true);
    }

    /// @notice Cancel an order
    /// @param id The order ID
    function cancelOrder(uint256 id) external {
        require(hasOrder(id), "OC: UNKNOWN_ORDER");
        SwapLib.cancelOrder(state, state2, owner(), id, false);
    }

    /// @notice Cancel an order using a market manager's signature
    /// @param id The order ID
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function cancelOrderByManagerSig(uint256 id, uint256 expireAt, bytes calldata signature) external {
        require(id <= state.orders.length - 1, "OC: UNKNOWN_ORDER");
        SwapLib.cancelOrderByManagerSig(state, state2, owner(), id, expireAt, signature);
    }

    /// @notice Allow the dispute manager to cancel an order.
    /// @param id The order ID.
    function cancelOrderByDispute(uint256 id) external onlyDisputeManager {
        SwapLib.cancelOrder(state, state2, owner(), id, true);
    }

    /// @notice Mark a swap order as paid.
    /// Order must be have been released or cancelled.
    /// When the base asset is ERC20, the sender must be the taker.
    /// When the base asset is synthetic,the sender must be the provider.
    /// @param id The order ID.
    function markOrderAsPaid(uint256 id) external {
        SwapLib.markOrderAsPaid(state, state2.marketCore, id, false);
    }

    /// @notice Mark a swap order as paid using a market manager's signature.
    /// @param id The order ID.
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function markOrderAsPaidByManagerSig(uint256 id, uint256 expireAt, bytes calldata signature) external {
        require(id <= state.orders.length - 1, "OC: UNKNOWN_ORDER");
        SwapLib.markOrderAsPaidByManagerSig(state, state2, id, expireAt, signature);
    }

    /// @notice Checks if at order at index id exist
    /// @param id The order ID or index.
    function hasOrder(uint256 id) public view returns (bool) {
        return AuxLib.hasOrder(state, id);
    }

    /// @notice Checks whether addr can create a dispute.
    /// It will revert if order is unknown.
    /// It will return false if order has been released.
    /// It will return false if addr is not a provider or a taker.
    /// It will return false if order has not be marked as `paid` and time to pay has not been reached.
    /// @param id The order ID.
    /// @param addr The address to check if permitted to create dispute.
    function canCreateDispute(uint256 id, address addr) external view returns (bool) {
        return AuxLib.canCreateDispute(state, state2, id, addr);
    }

    /// @notice Marks an order as `disputed`
    /// @param id The order ID or index.
    function markAsDisputed(uint256 id) external onlyDisputeManager {
        SwapLib.markAsDisputed(state, state2.marketCore, id);
    }

    /// @notice Get an order by ID
    /// @param id The order ID
    function getOrder(uint256 id) external view returns (Structs.Order memory) {
        require(hasOrder(id), "OC: UNKNOWN_ORDER");
        return state.orders[id];
    }

    /// @notice Get an offer by ID
    /// @param id The offer ID
    function getOffer(uint256 id) external view returns (Structs.Offer memory) {
        require(id < state2.offers.length, "OC: UNKNOWN_OFFER");
        return state2.offers[id];
    }

    /// @notice Get a market by ID
    /// @param id The market ID
    function getMarket(address id) external view marketMustExist(id) returns (Structs.Market memory) {
        return state2.marketCore.getMarket(id);
    }

    /// @notice Transfer fees earned on shareable assets to the fee reward contract
    /// @param _token The address of a shareable token
    function transferToFeePool(address _token) external {
        WithdrawLib.transferToFeePool(state, state2, _token);
    }

    /// @notice Withdraw fee earned by an operator for a given asset
    /// @param _token The address of a shareable token
    function withdrawOperatorBalance(address _token) external {
        WithdrawLib.withdrawOperatorBalance(state, state2, _token);
    }

    /// @notice Return liquidity object of an address in a market
    /// @param _mid The market ID
    /// @param _owner The address of the owner of the liquidity
    /// @param _lid The liquidity ID
    function getLiquidity(address _mid, address _owner, uint256 _lid) external view returns (Structs.Liquidity memory) {
        return state.liquidity[_mid][_owner][_lid];
    }

    /// @notice Return liquidity of an address in a market
    /// @param _mid The market ID
    /// @param _owner The address of the owner of the liquidity
    /// @param _lid The liquidity ID
    function hasLiquidity(address _mid, address _owner, uint256 _lid) external view returns (bool) {
        return AuxLib.isLiquidityExist(state, _mid, _owner, _lid);
    }

    /// @notice Allow an address to manage sender's liquidity
    /// @param marketId The market ID
    /// @param manager The address of the manager
    function setLiquidityManager(address marketId, address manager) external {
        LiquidityLib.setLiquidityManager(state, marketId, manager);
    }

    /// @notice Get the liquidity manager address of a liquidity provider
    /// @param marketId The market ID
    /// @param provider The liquidity provider address
    function getLiquidityManager(address marketId, address provider) external view returns (address) {
        return state.liqManagers[provider][marketId];
    }

    /// @notice Update fungible market (ERC20, SYNTH) liquidity
    /// @param marketId The unique ID of the market.
    /// @param lid The liquidity ID
    /// @param deduct Indicate whether the liquidity should be deducted.
    /// @param target Specify an address solely allowed to swap the liquidity
    /// @param amountAndPrice The amount of base asset and quote price per base asset.
    ///         0: amount: The amount of base asset to add as liquidity
    ///         1: price: The quote price per base asset
    /// @param wantOrIds: List of token IDs where at least one token must match (req: when quote is non-fungible).
    /// @param wantAndIds: List of tokens IDs where all tokens must match (req: when quote is non-fungible)
    /// @param limits The swap and transaction limits
    ///         0: minSwap: The minimum swap amount for the sender's liquidity. Must be less than maxSwap.
    ///         1: maxSwap: The maximum swap amount for the sender's liquidity. Must be greater than minSwap.
    ///         2: timeToPay: The maximum seconds within which payer must make payment.
    ///         3: expireAt: The transaction deadline
    ///         4: pause: Indicate whether to pause the liquidity
    function updateFungibleLiquidity(
        address marketId,
        uint256 lid,
        bool deduct,
        address target,
        uint256[2] memory amountAndPrice,
        uint256[] memory wantOrIds,
        uint256[] memory wantAndIds,
        uint256[5] memory limits
    ) external payable notExpired(limits[3]) marketMustExist(marketId) {
        StructsLib.UpdateLiquiditySlot memory slot = StructsLib.UpdateLiquiditySlot(
            marketId,
            lid,
            deduct,
            limits[4] > 0,
            amountAndPrice[0],
            new uint256[](0),
            wantOrIds,
            wantAndIds,
            amountAndPrice[1],
            limits[0],
            limits[1],
            limits[2],
            target
        );
        FungibleLiquidityLib.updateFungibleLiquidity(state, state2, slot);
    }

    /// @notice Update non-fungible market (ERC721, ERC1155) liquidity
    /// @param marketId The unique ID of the market.
    /// @param lid The liquidity ID
    /// @param deduct Indicate whether the liquidity should be deducted.
    /// @param target Specify an address solely allowed to swap the liquidity
    /// @param ids Indicate the token ids to use as liquidity
    /// @param price The quote price to accept for all tokens listed in <ids> (not required if quote asset is non-fungible)
    /// @param wantOrIds: List of token IDs where at least one token must match (req: when quote is non-fungible).
    /// @param wantAndIds: List of tokens IDs where all tokens must match (req: when quote is non-fungible)
    /// @param limits The swap and transaction limits
    ///         0: timeToPay: The maximum seconds within which payer must make payment.
    ///         1: expireAt: The transaction deadline
    ///         2: pause: Indicate whether to pause the liquidity
    function updateNonFungibleLiquidity(
        address marketId,
        uint256 lid,
        bool deduct,
        address target,
        uint256[] memory ids,
        uint256 price,
        uint256[] memory wantOrIds,
        uint256[] memory wantAndIds,
        uint256[3] memory limits
    ) external notExpired(limits[1]) marketMustExist(marketId) {
        StructsLib.UpdateLiquiditySlot memory slot = StructsLib.UpdateLiquiditySlot(
            marketId,
            lid,
            deduct,
            limits[2] > 0,
            0,
            ids,
            wantOrIds,
            wantAndIds,
            price,
            0,
            0,
            limits[0],
            target
        );
        NonFungibleLiquidityLib.updateNonFungibleLiquidity(state, state2, slot);
    }

    /// @notice Set required swap badges
    /// @param marketId The market ID
    /// @param lid The liquidity ID
    /// @param badgeIds The badge IDs
    function setSwapBadgeForLiquidity(address marketId, uint256 lid, string[2][] memory badgeIds) external {
        LiquidityLib.setSwapBadgeForLiquidity(state, state2, marketId, lid, badgeIds);
    }

    /// @notice Get required swap badges of an address
    /// @param marketId The market ID
    /// @param lid The liquidity ID
    /// @param addr The address
    function getReqSwapBadges(address marketId, uint256 lid, address addr) external view returns (string[2][] memory) {
        return state2.reqSwapBadges[marketId][addr][lid];
    }

    /// @notice Increment open dispute count of a provider's liquidity
    /// @param marketId The market ID
    /// @param provider The provider address
    /// @param lid The liquidity ID
    /// @param incr If true, count is incremented, else decremented
    function updateOpenDisputesCount(address marketId, address provider, uint256 lid, bool incr) public onlyDisputeManager {
        LiquidityLib.updateOpenDisputesCount(state, marketId, provider, lid, incr);
    }

    /// @notice Allows liquidity manager to update a provider's liquidity
    /// @param _marketId The market ID
    /// @param _owner The owner of the liquidity
    /// @param _lid The liquidity ID
    /// @param _price The new _price of the liquidity
    /// @param _expireAt The transaction is reverted if current time >= _expireAt
    function updateLiquidityPrice(
        address _marketId,
        address _owner,
        uint256 _lid,
        uint256 _price,
        uint256 _expireAt
    ) external notExpired(_expireAt) marketMustExist(_marketId) {
        LiquidityLib.updateLiquidityPrice(state, state2, _marketId, _owner, _lid, _price);
    }

    /// @notice Create an offer
    /// @param marketId The market ID
    /// @param provider The provider's address
    /// @param lid The liquidity ID
    /// @param amount The offer amount
    /// @param price The offer price
    /// @param ids The IDs of non-fungible assets
    /// @param deadline The time when the offer expires.
    function createOffer(
        address marketId,
        address provider,
        address recipient,
        uint256 lid,
        uint256 amount,
        uint256 price,
        uint256[] memory ids,
        uint256 deadline,
        uint256 operator
    ) public {
        OfferLib.CreateOfferAddressParams memory offerAddrs = OfferLib.CreateOfferAddressParams(marketId, provider, recipient);
        OfferLib.CreateOfferIntParams memory offerInts = OfferLib.CreateOfferIntParams(lid, amount, price, deadline, operator);
        OfferLib.createOffer(state, state2, offerAddrs, offerInts, ids);
    }

    /// @notice Create an offer
    /// @param offerId The offer ID
    /// @param amount The offer amount
    /// @param price The offer price
    /// @param ids The IDs of non-fungible assets
    /// @param deadline The time when the offer expires.
    function updateOffer(uint256 offerId, uint256 amount, uint256 price, uint256[] memory ids, uint256 deadline) public {
        OfferLib.updateOffer(state, state2, offerId, amount, price, ids, deadline);
    }

    /// @notice Accept an offer
    /// @param offerId The offer ID
    /// @param delay The number of seconds that must pass before offer can be executed
    function acceptOffer(uint256 offerId, uint256 delay) public {
        OfferLib.acceptOffer(state2, offerId, delay);
    }

    /// @notice Unaccept an offer
    /// @param offerId The offer ID
    function unacceptOffer(uint256 offerId) public {
        OfferLib.unacceptOffer(state2, offerId);
    }

    /// @notice Cancel an offer
    /// @param offerId The offer ID
    function cancelOffer(uint256 offerId) public {
        OfferLib.cancelOffer(state2, offerId);
    }

    /// @notice Execute an offer
    /// @param offerId The offer ID
    /// @param txDeadline The transaction is reverted if current time >= expireAt
    function executeOffer(uint256 offerId, uint256 txDeadline) public notExpired(txDeadline) {
        OfferLib.executeOffer(state, state2, offerId);
    }

    /// @notice Toggle liquidity pause state
    /// @param marketId The market ID
    /// @param lid The liquidity ID
    /// @param expireAt The transaction is reverted if current time >= expireAt
    function togglePause(address marketId, uint256 lid, uint256 expireAt) public notExpired(expireAt) {
        LiquidityLib.togglePause(state, marketId, lid);
    }
}
合同源代码
文件 5 的 44:ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

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

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

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

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

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

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

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

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

        return (signer, RecoverError.NoError);
    }

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

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

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

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}
合同源代码
文件 6 的 44:ERC1155Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.0;

import "./ERC1155Receiver.sol";

/**
 * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 *
 * @dev _Available since v3.1._
 */
contract ERC1155Holder is ERC1155Receiver {
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}
合同源代码
文件 7 的 44:ERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
    }
}
合同源代码
文件 8 的 44:ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 9 的 44:FeeLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../common/SigLib.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../market/MarketAuxLib.sol";
import "../feepurse/interfaces/IFeePurse.sol";

struct FeeInfo {
    uint256 cost;
    uint256 feeBp;
    uint256 amtToCharge;
    uint256 mktCreatorFee;
    uint256 protoFee;
    uint256 totalFees;
    uint256 remaining;
    uint256 protoFeeRate;
    uint256 mktCreatorFeeRate;
    uint256 mktFeeBp;
}

struct ComputeFeeParams {
    Structs.Market mkt;
    Structs.Liquidity liq;
    IAssetRegistry assetReg;
    IFeePurse feePurse;
    uint256 synthSwapFeeBp;
    uint256 instantNFTSwapFeeBp;
    uint256 instantSwapFeeBp;
    uint256 stableSwapFeeBp;
    uint256 nftDiscountBp;
    uint256 lid;
    uint256 amount;
    uint256 price;
    address nft;
    address provider;
    address taker;
}

library FeeLib {
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;

    /// @dev Compute fee for all swap types.
    /// @param p The parameters needed to compute the fee.
    /// @return feeInfo The fee information.
    function computeFee(ComputeFeeParams memory p) public view returns (FeeInfo memory) {
        FeeInfo memory feeInfo;

        // ERC20/ERC20, ERC20/NATIVE, NATIVE/ERC20, NATIVE/NATIVE fee calculation.
        if (
            (p.mkt.base.isERC20() && p.mkt.quote.isERC20()) ||
            (p.mkt.base.isERC20() && p.mkt.quote.isNative()) ||
            (p.mkt.base.isNative() && p.mkt.quote.isERC20()) ||
            (p.mkt.base.isNative() && p.mkt.quote.isNative())
        ) {
            (feeInfo.feeBp, , ) = AuxLib.getFeeInfo(p.assetReg, p.mkt.base, p.instantSwapFeeBp);
            feeInfo.feeBp = AuxLib.isStable(p.assetReg, p.mkt) ? p.stableSwapFeeBp : feeInfo.feeBp;
            feeInfo.mktFeeBp = p.mkt.commission;

            // If sender has NFT, apply NFT fee discount
            uint256 denom = 10000;
            if (p.nft != address(0) && AuxLib.hasNativeNFT(p.nft, p.taker)) {
                feeInfo.feeBp = feeInfo.feeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.feeBp * 10 ** 2) / denom);
                feeInfo.mktFeeBp = feeInfo.mktFeeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.mktFeeBp * 10 ** 2) / denom);
                denom = 1000000;
            }

            // Calculate fees
            feeInfo.mktCreatorFee = (p.amount * feeInfo.mktFeeBp) / denom;
            feeInfo.protoFee = (p.amount * feeInfo.feeBp) / denom;
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        // ERC20/SYNTH, SYNTH/ERC20, NATIVE/SYNTH, SYNTH/NATIVE fee calculation.
        if (p.mkt.isFungible() && p.mkt.hasERC20OrNative()) {
            Structs.Asset memory asset = p.mkt.base.isOnchain() ? p.mkt.base : p.mkt.quote;

            uint decimals = (p.mkt.quote.isSynth() || p.mkt.quote.isNative()) ? 10 ** 18 : 10 ** IERC20(p.mkt.base.addr).decimals();
            feeInfo.cost = (p.amount * p.price) / decimals;
            feeInfo.mktFeeBp = p.mkt.commission;
            (feeInfo.feeBp, , ) = AuxLib.getFeeInfo(p.assetReg, asset, p.synthSwapFeeBp);

            // If taker's asset is synthetic (off-chain) and has the native NFT, apply discount.
            // If provider's asset is synthetic (off-chain) and has the native NFT, apply discount.
            uint256 denom = 10000;
            if (AuxLib.hasNativeNFT(p.nft, (p.mkt.quote.isSynth()) ? p.taker : p.provider)) {
                feeInfo.mktFeeBp = feeInfo.mktFeeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.mktFeeBp * 10 ** 2) / denom);
                feeInfo.feeBp = feeInfo.feeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.feeBp * 10 ** 2) / denom);
                denom = 1000000;
            }

            // Determine which amount is subject to fee deduction.
            // When the base asset is ERC20 or Native, charge fee from order amount.
            // Otherwise charge fee from order cost.
            feeInfo.amtToCharge = (p.mkt.base.isSynth()) ? feeInfo.cost : p.amount;

            // Calculate fees
            feeInfo.mktCreatorFee = (feeInfo.amtToCharge * feeInfo.mktFeeBp) / denom;
            feeInfo.protoFee = (feeInfo.amtToCharge * feeInfo.feeBp) / denom;
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        // ERC20/NFT, NATIVE/NFT fee calculation.
        if ((p.mkt.base.isERC20() || p.mkt.base.isNative()) && p.mkt.quote.isNFT()) {
            (feeInfo.feeBp, , ) = AuxLib.getFeeInfo(p.assetReg, p.mkt.base, p.instantNFTSwapFeeBp);
            feeInfo.mktFeeBp = p.mkt.commission;

            // If sender has NFT, apply NFT fee discount
            uint256 denom = 10000;
            if (p.nft != address(0) && AuxLib.hasNativeNFT(p.nft, p.taker)) {
                feeInfo.feeBp = feeInfo.feeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.feeBp * 10 ** 2) / denom);
                feeInfo.mktFeeBp = feeInfo.mktFeeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.mktFeeBp * 10 ** 2) / denom);
                denom = 1000000;
            }

            // Calculate fees
            feeInfo.mktCreatorFee = (p.liq.amount * feeInfo.mktFeeBp) / denom;
            feeInfo.protoFee = (p.liq.amount * feeInfo.feeBp) / denom;
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
        }

        // SYNTH/SYNTH & SYNTH/NFT fee calculation
        if (p.mkt.base.isSynth() && (p.mkt.quote.isSynth() || p.mkt.quote.isNFT())) {
            feeInfo.remaining = p.liq.amount;
            feeInfo.protoFeeRate = p.liq.protoFeeRate;
            feeInfo.mktCreatorFeeRate = p.liq.mktCreatorFeeRate;

            // Calculate fees
            feeInfo.protoFee = (feeInfo.protoFeeRate * p.amount) / 10 ** 18;
            feeInfo.mktCreatorFee = (feeInfo.mktCreatorFeeRate * p.amount) / 10 ** 18;
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        // NFT/ERC20, NFT/NATIVE fee calculation
        if (p.mkt.base.isNFT() && (p.mkt.quote.isERC20() || p.mkt.quote.isNative())) {
            (feeInfo.feeBp, , ) = AuxLib.getFeeInfo(p.assetReg, p.mkt.base, p.instantNFTSwapFeeBp);
            feeInfo.mktFeeBp = p.mkt.commission;

            // If provider has NFT, apply NFT fee discount
            uint256 denom = 10000;
            if (p.nft != address(0) && AuxLib.hasNativeNFT(p.nft, p.provider)) {
                feeInfo.feeBp = feeInfo.feeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.feeBp * 10 ** 2) / denom);
                feeInfo.mktFeeBp = feeInfo.mktFeeBp * 10 ** 2 - ((p.nftDiscountBp * feeInfo.mktFeeBp * 10 ** 2) / denom);
                denom = 1000000;
            }

            // Calculate fees owed.
            feeInfo.mktCreatorFee = (p.price * feeInfo.mktFeeBp) / denom;
            feeInfo.protoFee = (p.price * feeInfo.feeBp) / denom;
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        // NFT/SYNTH fee calculation
        if (p.mkt.base.isNFT() && p.mkt.quote.isSynth()) {
            feeInfo.protoFee = p.feePurse.getLocked(p.liq.protoFeePurseId);
            feeInfo.mktCreatorFee = p.feePurse.getLocked(p.liq.mktCreatorFeePurseId);
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        // NFT/NFT fee calculation
        if (p.mkt.base.isNFT() && p.mkt.quote.isNFT()) {
            feeInfo.protoFee = p.feePurse.getLocked(p.liq.protoFeePurseId);
            feeInfo.mktCreatorFee = p.feePurse.getLocked(p.liq.mktCreatorFeePurseId);
            feeInfo.totalFees = feeInfo.mktCreatorFee + feeInfo.protoFee;
            return feeInfo;
        }

        return feeInfo;
    }
}
合同源代码
文件 10 的 44:FungibleLiquidityLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../token/interfaces/IERC721.sol";
import "../token/interfaces/IERC1155.sol";
import "../common/StrippedInterfaces.sol";
import "../common/MathLib.sol";
import "./AuxLib.sol";
import "../market/MarketAuxLib.sol";
import "./StructsLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../feepurse/interfaces/IFeePurse.sol";
import "./LiqAuxLib.sol";

library FungibleLiquidityLib {
    using SafeERC20 for IERC20;
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;
    using AuxLib for uint256[];
    using AuxLib for Structs.Market;
    using AuxLib for Structs.Liquidity;
    using AuxLib for address;

    event UpdatedFungibleLiquidity(
        address indexed provider,
        address indexed marketId,
        address target,
        bool deduct,
        bool paused,
        uint256 liq,
        uint256 amount,
        uint256 price,
        uint256[] wantOrIds,
        uint256[] wantAndIds,
        uint256 minSwap,
        uint256 maxSwap,
        uint256 timeToPay
    );

    /// @dev Create or update fungible liquidity.
    /// Covers ERC20/ERC20, ERC20/SYNTH, ERC20/NFT, ERC20/NATIVE,
    /// SYNTH/ERC20, SYNTH/SYNTH, SYNTH/NFT,
    /// NATIVE/ERC20, NATIVE/SYNTH, NATIVE/NFT, NATIVE/NATIVE
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    /// @param slot The parameters needed to create or update liquidity.
    function updateFungibleLiquidity(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.UpdateLiquiditySlot memory slot
    ) external {
        require(slot.deduct || s2.marketCore.isPutPermitted(slot.marketId, msg.sender));
        Structs.Market memory mkt = s2.marketCore.getMarket(slot.marketId);
        require(mkt.base.isFungible(), "OC: UNEXP_BASE");
        require(!mkt.isInstantMarket() || slot.timeToPay == 0, "OC: TTP_NREQ");
        require(slot.timeToPay == 0 || slot.timeToPay >= s1.minTimeToPay, "OC: LOW_TTP");
        require(mkt.quote.isNFT() || slot.price > 0, "OC: PRICE_REQ");
        require(!mkt.quote.isNFT() || slot.price == 0, "OC: PRICE_NREQ");
        require(mkt.quote.isNFT() || (slot.wantOrIds.length == 0 && slot.wantAndIds.length == 0), "OC: WANT_IDS_NREQ");
        require(slot.wantOrIds.isUnique() && slot.wantAndIds.isUnique(), "OC: WANT_IDS_NUNIQ");
        require(slot.wantOrIds.isMaxLen16() && slot.wantAndIds.isMaxLen16(), "OC: WANT_IDS_HSIZE");
        require(!mkt.quote.isNFT() || (slot.minSwap == 0 && slot.maxSwap == 0), "OC: MIN_MAX_SWAP_NREQ");
        require(slot.minSwap == 0 || slot.maxSwap == 0 || slot.minSwap < slot.maxSwap, "OC: MIN_MAX_SWAP_INV");

        // Check if the sender has the required market PUT badges.
        LiqAuxLib.checkMarketPutBadges(s2, slot.marketId);

        // Get the liquidity. If the liquidity does not exist, create a new one
        Structs.Liquidity storage liq;
        if (AuxLib.isLiquidityExist(s1, slot.marketId, msg.sender, slot.lid)) {
            liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
        } else {
            uint256[] memory zero = new uint256[](0);
            s1.liquidity[slot.marketId][msg.sender].push(
                Structs.Liquidity(zero, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, zero, zero, false, slot.target)
            );
            slot.lid = s1.liquidity[slot.marketId][msg.sender].length - 1;
            liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
        }

        // For new liquidity, ensure amount is provided.
        require(!liq.isLiquidityEmpty() || slot.amount > 0, "OC: AMOUNT_REQ");

        // Handle increment
        // - Increment listing pool balance if BASE asset is verified and market is not permissioned
        // - Transfer BASE if it is an ERC20
        // - If market does not involve an ERC20, lock fee in FeePurse
        if (!slot.deduct && slot.amount > 0) {
            liq.amount += slot.amount;
            mkt.numLiquidity += slot.amount;

            if (mkt.base.isERC20()) {
                IERC20(mkt.base.addr).safeTransferFrom(msg.sender, address(this), slot.amount);
            }

            if (mkt.base.isNative()) {
                require(msg.value >= slot.amount, "OC: LOW_DEPOSIT");
            }

            if (!mkt.hasERC20OrNative()) {
                LiqAuxLib.lockFeeForProtocol(s1, s2, mkt, liq, slot.amount);
                LiqAuxLib.lockFeeForMarketCreator(s2, mkt, liq, slot.amount);
            }
        }

        // Handle decrement
        // - There must be sufficient liquidity
        // - When both base and quote side are synthetic and liquidity is has been fully removed, transfer fee back to sender
        // - Decrement listing pool balance if BASE is verified and market is not permissioned
        // - Transfer BASE back to sender if it is an ERC20
        // - If market does not involve an ERC20, release and unlock fee in FeePurse
        if (slot.deduct && slot.amount > 0) {
            require(liq.amount >= slot.amount, "OC: LOW_LIQ");
            liq.amount -= slot.amount;
            mkt.numLiquidity -= slot.amount;

            if (mkt.base.isERC20()) {
                IERC20(mkt.base.addr).safeTransfer(msg.sender, slot.amount);
            }

            if (mkt.base.isNative()) {
                payable(msg.sender).transfer(slot.amount);
            }

            if (!mkt.hasERC20OrNative()) {
                LiqAuxLib.unlockFeeForProtocol(s2, mkt, liq);
                LiqAuxLib.unlockFeeForMarketCreator(s2, mkt, liq);
            }
        }

        liq.timeToPay = slot.timeToPay;
        liq.price = slot.price;
        liq.minSwap = slot.minSwap;
        liq.maxSwap = slot.maxSwap;
        liq.wantAndIds = slot.wantAndIds;
        liq.wantOrIds = slot.wantOrIds;
        liq.target = slot.target;
        liq.paused = slot.pause;
        s2.marketCore.setCounters(slot.marketId, false, false, true, mkt.numLiquidity);

        emit UpdatedFungibleLiquidity(
            msg.sender,
            slot.marketId,
            slot.target,
            slot.deduct,
            slot.pause,
            slot.lid,
            slot.amount,
            slot.price,
            slot.wantOrIds,
            slot.wantAndIds,
            slot.minSwap,
            slot.maxSwap,
            slot.timeToPay
        );
    }
}
合同源代码
文件 11 的 44:IAssetRegistry.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

struct Info {
    uint256 curRate;
    uint256 feeBp;
    uint256 feeFixed;
    bool verified;
    bool stable;
    bool sharable;
}

interface IAssetRegistry {
    function set(address addr, uint256 curRate, uint256 feeBp, uint256 feeFixed, bool verified, bool stable, bool sharable) external;

    function setRate(address addr, uint256 rate) external;

    function setFee(uint256 feeBp, uint256 feeFixed) external;

    function get(address addr) external view returns (Info memory);

    function setVerified(address addr, bool verified) external;

    function setSharable(address addr, bool sharable) external;

    function getRate(address addr) external view returns (uint256);

    function setStable(address addr, bool stable) external;

    function toCur(address addr, uint256 value) external returns (uint256);
}
合同源代码
文件 12 的 44:IBadge.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../../common/Structs.sol";

interface IBadge {
    function hasBadge(address addr, string memory badge, string memory path) external view returns (bool);
}
合同源代码
文件 13 的 44:IERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC1155 {
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
}
合同源代码
文件 14 的 44:IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}
合同源代码
文件 15 的 44:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
合同源代码
文件 16 的 44:IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function decimals() external view returns (uint8);

    function allowance(address owner, address spender) external view returns (uint256);

    function burn(uint256 amount) external;

    function mint(address to, uint256 amount) external;

    function approve(address spender, uint256 amount) external returns (bool);
}
合同源代码
文件 17 的 44:IERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC721 {
    function transferFrom(address from, address to, uint256 tokenId) external;
}
合同源代码
文件 18 的 44:IFeePool.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

interface IFeePool {
    function notifyWithdraw(address account, uint256 _amount) external;

    function notifyStake(address account, uint256 _amount) external;

    function notifyFeeDeposit(address token, uint256 amount) external;

    function withdrawReward(address feeToken, uint256 _amount) external;

    function earned(address account, address feeToken) external view returns (uint256);
}
合同源代码
文件 19 的 44:IFeePurse.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

interface IFeePurse {
    function balanceOf(address owner) external view returns (uint256);

    function lock(address owner, uint256 amt) external returns (uint256);

    function unlock(address owner, uint256 id) external;

    function transferFrom(address from, address to, uint256 amt) external;

    function getLocked(uint256 id) external view returns (uint256);

    function withdraw(uint256 amt, address to) external;

    function withdrawFrom(uint256 amt, address from, address to) external;

    function getFeeToken() external view returns (address);
}
合同源代码
文件 20 的 44:IMarketCore.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../../common/Ownable.sol";
import "../../common/Structs.sol";

interface IMarketCore {
    function setInventor(address val) external;

    function createMarket(
        address base,
        address quote,
        bool isBaseLocal,
        bool isQuoteLocal,
        bool permPut,
        bool permSwap,
        bool permMediate,
        uint256 commission,
        uint256 mediatorBond,
        uint256 numMediators
    ) external;

    function getNativeAssetAddr() external view returns (address);

    function getMarket(address id) external view returns (Structs.Market memory);

    function permitPut(address id, address addr) external;

    function revokePut(address id, address addr) external;

    function permitSwap(address id, address addr) external;

    function revokeSwap(address id, address addr) external;

    function hasMarket(address id) external view returns (bool);

    function setCounters(address id, bool numOrders, bool numDisputes, bool numLiquidity, uint256 value) external;

    function setCommission(address id, uint256 val) external;

    function setLiquidityManager(address marketId, address manager) external;

    function getLiquidityManager(address marketId, address provider, uint256 lid) external view returns (address);

    function isPutPermitted(address id, address sender) external view returns (bool);

    function isSwapPermitted(address id, address sender) external view returns (bool);

    function getReqBadges(address id) external view returns (string[2][] memory, string[2][] memory);
}
合同源代码
文件 21 的 44:IMinerRegistry.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

enum MinerOperation {
    Draft,
    Undraft,
    Execute,
    Cancel
}

interface IMinerRegistry {
    function isMiner(address _address) external view returns (bool);
    function addReward(MinerOperation op, address miner) external;
}
合同源代码
文件 22 的 44:IOperator.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

interface IOperator {
    function isOperator(uint256 id) external returns (bool);

    function getOperator(uint256 id) external returns (address);
}
合同源代码
文件 23 的 44:ISwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
合同源代码
文件 24 的 44:IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}
合同源代码
文件 25 的 44:LiqAuxLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../token/interfaces/IERC721.sol";
import "../token/interfaces/IERC1155.sol";
import "../common/StrippedInterfaces.sol";
import "../common/MathLib.sol";
import "./AuxLib.sol";
import "../market/MarketAuxLib.sol";
import "./StructsLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../feepurse/interfaces/IFeePurse.sol";

library LiqAuxLib {
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;

    /// @dev Calculate and lock liquidity fee for protocol in non-ERC20 market
    /// @param s1 A state object
    /// @param s2 A state object
    /// @param mkt The market object
    /// @param liq The liquidity object
    /// @param amount The amount of liquidity to lock fee for
    function lockFeeForProtocol(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq,
        uint256 amount
    ) internal {
        (, uint256 fixedFee, ) = AuxLib.getFeeInfo(s2.assetReg, mkt.base, 0);
        uint256 fixedFeeInternal = mkt.base.isSynth() ? s1.ints.fixedSynthSwapFee : s1.ints.fixedNFTSwapFee;
        uint256 fee = amount * ((fixedFee > 0) ? fixedFee : fixedFeeInternal);
        fee = mkt.base.isSynth() ? fee / 10 ** 18 : fee;

        if (fee > 0 && AuxLib.hasNativeNFT(s2.nft, msg.sender) && s2.ints.nftDiscountBp > 0) {
            fee = fee - (fee * s2.ints.nftDiscountBp) / 10000;
        }

        if (fee > 0) {
            uint256 prevLockedFee = s2.feePurse.getLocked(liq.protoFeePurseId);
            if (prevLockedFee > 0) s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
            liq.protoFeePurseId = s2.feePurse.lock(msg.sender, prevLockedFee + fee);
        }

        // Update fee rate of liquidity
        uint256 curLiqAmt = mkt.base.isSynth() ? liq.amount : liq.ids.length;
        uint256 curLocked = s2.feePurse.getLocked(liq.protoFeePurseId);
        liq.protoFeeRate = (mkt.base.isSynth() ? (curLocked * 10 ** 18) : curLocked) / curLiqAmt;
    }

    /// @dev Calculate and unlock liquidity fee for protocol in a non-ERC20 market
    /// @param s2 A state object
    /// @param mkt The market object
    /// @param liq The liquidity object
    function unlockFeeForProtocol(StructsLib.State2 storage s2, Structs.Market memory mkt, Structs.Liquidity storage liq) internal {
        uint256 remaining = mkt.base.isSynth() ? liq.amount : liq.ids.length;

        if (remaining == 0) {
            s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
            liq.protoFeeRate = 0;
            liq.protoFeePurseId = 0;
            return;
        }

        s2.feePurse.unlock(msg.sender, liq.protoFeePurseId);
        uint256 newAmtToLck = liq.protoFeeRate * remaining;
        newAmtToLck = mkt.base.isSynth() ? newAmtToLck / 10 ** 18 : newAmtToLck;
        liq.protoFeePurseId = (newAmtToLck > 0) ? s2.feePurse.lock(msg.sender, newAmtToLck) : 0;

        // Update fee rate of liquidity
        uint256 curLocked = s2.feePurse.getLocked(liq.protoFeePurseId);
        liq.protoFeeRate = (mkt.base.isSynth() ? (curLocked * 10 ** 18) : curLocked) / remaining;
    }

    /// @dev Calculate and lock liquidity fee for market creator in non-ERC20 market
    /// @param s2 A state object
    /// @param mkt The market object
    /// @param liq The liquidity object
    /// @param amount The amount of liquidity to lock fee for
    function lockFeeForMarketCreator(
        StructsLib.State2 storage s2,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq,
        uint256 amount
    ) internal {
        uint256 fee = amount * mkt.commission;
        fee = mkt.base.isSynth() ? fee / 10 ** 18 : fee;

        if (fee > 0 && AuxLib.hasNativeNFT(s2.nft, msg.sender) && s2.ints.nftDiscountBp > 0) {
            fee = fee - (fee * s2.ints.nftDiscountBp) / 10000;
        }

        if (fee > 0) {
            uint256 prevLockedFee = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
            if (prevLockedFee > 0) s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
            liq.mktCreatorFeePurseId = s2.feePurse.lock(msg.sender, prevLockedFee + fee);
        }

        // Update fee rate of liquidity
        uint256 curLiqAmt = mkt.base.isSynth() ? liq.amount : liq.ids.length;
        uint256 curLocked = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
        liq.mktCreatorFeeRate = (mkt.base.isSynth() ? (curLocked * 10 ** 18) : curLocked) / curLiqAmt;
    }

    /// @dev Calculate and unlock liquidity fee for market creator in a non-ERC20 market
    /// @param s2 A state object
    /// @param mkt The market object
    /// @param liq The liquidity object
    function unlockFeeForMarketCreator(StructsLib.State2 storage s2, Structs.Market memory mkt, Structs.Liquidity storage liq) internal {
        uint256 remaining = mkt.base.isSynth() ? liq.amount : liq.ids.length;

        if (remaining == 0) {
            s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
            liq.mktCreatorFeeRate = 0;
            liq.mktCreatorFeePurseId = 0;
            return;
        }

        s2.feePurse.unlock(msg.sender, liq.mktCreatorFeePurseId);
        uint256 newAmtToLck = liq.mktCreatorFeeRate * remaining;
        newAmtToLck = mkt.base.isSynth() ? newAmtToLck / 10 ** 18 : newAmtToLck;
        liq.mktCreatorFeePurseId = (newAmtToLck > 0) ? s2.feePurse.lock(msg.sender, newAmtToLck) : 0;

        // Update fee rate of liquidity
        uint256 curLocked = s2.feePurse.getLocked(liq.mktCreatorFeePurseId);
        liq.mktCreatorFeeRate = (mkt.base.isSynth() ? (curLocked * 10 ** 18) : curLocked) / remaining;
    }

    /// @dev Check if sender has the required badges for the market's liquidity providers
    /// @param s2 A state object
    /// @param marketId The market ID
    function checkMarketPutBadges(StructsLib.State2 storage s2, address marketId) internal view {
        (string[2][] memory reqPutBadges, ) = s2.marketCore.getReqBadges(marketId);
        if (reqPutBadges.length > 0) {
            uint256 len = reqPutBadges.length;
            for (uint256 i = 0; i < len; i++) {
                require(s2.badge.hasBadge(msg.sender, reqPutBadges[i][0], reqPutBadges[i][1]), "OC: MKT_BADGE_REQ");
            }
        }
    }
}
合同源代码
文件 26 的 44:LiquidityLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../token/interfaces/IERC721.sol";
import "../token/interfaces/IERC1155.sol";
import "../common/StrippedInterfaces.sol";
import "../common/MathLib.sol";
import "./AuxLib.sol";
import "../market/MarketAuxLib.sol";
import "./StructsLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../feepurse/interfaces/IFeePurse.sol";

library LiquidityLib {
    using SafeERC20 for IERC20;
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;
    using AuxLib for uint256[];
    using AuxLib for Structs.Market;
    using AuxLib for Structs.Liquidity;
    using AuxLib for address;

    event UpdatedPrice(address indexed marketId, address indexed provider, address indexed manager, uint256 lid, uint256 price);
    event TogglePause(address indexed marketId, address indexed provider, uint256 lid, bool paused);
    event UpdatedLiquidityManager(address indexed marketId, address indexed provider, address indexed manager, address oldManager);
    event UpdatedLiquiditySwapBadges(address marketId, address provider, uint256 lid, string[2][] badges);

    /// @dev Returns matching want IDs.
    /// @param ids The IDs to search
    /// @param wantOrIds The IDs to match at least one of
    /// If wantOrIds is not empty, at least one entry must be included in ids.
    /// @param wantAndIds The IDs to match all of
    /// If wantAndIds and wantOrIds are empty, return entries in ids.
    /// If wantAndIds is not empty, all entries must be included in ids.
    /// @return matchIds The matching IDs
    function findMatchInWantIds(
        uint256[] memory ids,
        mapping(uint256 => bool) storage intIdx,
        uint256[] memory wantOrIds,
        uint256[] memory wantAndIds
    ) public returns (uint256[] memory) {
        if (wantOrIds.length == 0 && wantAndIds.length == 0) return ids;

        uint256[] memory matchIds = new uint256[](ids.length);
        mapping(uint256 => bool) storage matchIdx = intIdx;
        uint256 idx;

        uint256 andFound;
        for (uint256 i = 0; i < wantAndIds.length; i++) {
            for (uint256 j = 0; j < ids.length; j++) {
                if (wantAndIds[i] == ids[j] && !matchIdx[ids[j]]) {
                    matchIds[idx] = ids[j];
                    matchIdx[ids[j]] = true;
                    idx++;
                    andFound++;
                }
            }
        }

        if (andFound != wantAndIds.length) return new uint256[](0);

        bool orFound = wantOrIds.length == 0;
        for (uint256 i = 0; i < wantOrIds.length; i++) {
            for (uint256 j = 0; j < ids.length; j++) {
                if (wantOrIds[i] == ids[j]) {
                    if (!matchIdx[ids[j]]) matchIds[idx] = ids[j];
                    orFound = true;
                    break;
                }
            }
            if (orFound) break;
        }

        return (orFound) ? matchIds : new uint256[](0);
    }

    /// @dev Update the count of open disputes for a liquidity
    /// @param state State object
    /// @param marketId The market ID
    /// @param provider The address of the liquidity provider
    /// @param lid The liquidity ID
    /// @param incr True to increment, false to decrement
    function updateOpenDisputesCount(StructsLib.State storage state, address marketId, address provider, uint256 lid, bool incr) external {
        Structs.Liquidity storage liq = state.liquidity[marketId][provider][lid];
        if (incr) liq.openDisputes++;
        else liq.openDisputes--;
    }

    /// @dev Set an address as the liquidity manager for a market
    /// @param marketId The market ID
    /// @param manager The address of the manager
    function setLiquidityManager(StructsLib.State storage state, address marketId, address manager) external {
        address old = state.liqManagers[msg.sender][marketId];
        state.liqManagers[msg.sender][marketId] = manager;
        emit UpdatedLiquidityManager(marketId, msg.sender, manager, old);
    }

    /// @dev Allows liquidity manager to update a provider's liquidity
    /// @param state State object
    /// @param state2 State object
    /// @param owner The address of the liquidity owner
    /// @param lid The liquidity ID
    /// @param price The new price for the liquidity
    function updateLiquidityPrice(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address marketId,
        address owner,
        uint256 lid,
        uint256 price
    ) external {
        require(state.liqManagers[owner][marketId] == msg.sender, "OC: NOT_MANAGER");
        Structs.Liquidity storage liq = state.liquidity[marketId][owner][lid];
        require(price > 0, "OC: PRICE_REQ");
        Structs.Market memory market = state2.marketCore.getMarket(marketId);
        require(MarketAuxLib.isFungible(market.quote), "OC: PRICE_NREQ");
        liq.price = price;
        emit UpdatedPrice(marketId, owner, msg.sender, lid, price);
    }

    /// @dev Toggle liquidity pause state
    /// @param state The State object
    /// @param mid The market ID
    /// @param lid The liquidity ID
    function togglePause(StructsLib.State storage state, address mid, uint256 lid) external {
        require(AuxLib.hasNonZeroLiquidity(state, mid, msg.sender, lid), "OC: ZERO_LIQ");
        state.liquidity[mid][msg.sender][lid].paused = !state.liquidity[mid][msg.sender][lid].paused;
        emit TogglePause(mid, msg.sender, lid, state.liquidity[mid][msg.sender][lid].paused);
    }

    /// @dev Set swap badges for a liquidity
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param mid The market ID
    /// @param lid The liquidity ID
    /// @param badges The badges to set
    function setSwapBadgeForLiquidity(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address mid,
        uint256 lid,
        string[2][] memory badges
    ) external {
        require(AuxLib.hasNonZeroLiquidity(state, mid, msg.sender, lid), "OC: ZERO_LIQ");
        require(badges.length <= 8, "OC: TOO_MANY_BADGES");
        state2.reqSwapBadges[mid][msg.sender][lid] = badges;
        emit UpdatedLiquiditySwapBadges(mid, msg.sender, lid, badges);
    }
}
合同源代码
文件 27 的 44:MarketAuxLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";

library MarketAuxLib {
    /// @dev Check if an asset is a native asset.
    /// @param asset The asset object
    /// @return True if the asset is a native asset
    function isNative(Structs.Asset memory asset) public pure returns (bool) {
        return (asset.assetType == Structs.AssetType.NATIVE);
    }

    /// @dev Check if an asset is a synthetic asset.
    /// @param asset The asset object
    /// @return True if the asset is a synthetic asset
    function isSynth(Structs.Asset memory asset) public pure returns (bool) {
        return (asset.assetType == Structs.AssetType.SYNTH);
    }

    /// @dev Check if an asset is an ERC20 asset.
    /// @param asset The asset object
    /// @return True if the asset is an ERC20 asset
    function isERC20(Structs.Asset memory asset) public pure returns (bool) {
        return (asset.assetType == Structs.AssetType.ERC20);
    }

    /// @dev Check if an asset is an ERC721 asset.
    /// @param asset The asset object
    /// @return True if the asset is an ERC721 asset
    function isERC721(Structs.Asset memory asset) public pure returns (bool) {
        return (asset.assetType == Structs.AssetType.ERC721);
    }

    /// @dev Check if an asset is an ERC1155 asset.
    /// @param asset The asset object
    /// @return True if the asset is an ERC1155 asset
    function isERC1155(Structs.Asset memory asset) public pure returns (bool) {
        return (asset.assetType == Structs.AssetType.ERC1155);
    }

    /// @dev Check if an asset is either an ERC721 or ERC1155 asset.
    /// @param asset The asset object
    /// @return True if the asset is one of the two
    function isNFT(Structs.Asset memory asset) public pure returns (bool) {
        return isERC721(asset) || isERC1155(asset);
    }

    /// @dev Check if an asset is either an ERC20, synthetic, or native asset.
    /// @param asset The asset object
    /// @return True if the asset is one of the two
    function isFungible(Structs.Asset memory asset) public pure returns (bool) {
        return isSynth(asset) || isERC20(asset) || isNative(asset);
    }

    /// @dev Check if a market includes an ERC20, synthetic or native asset on the base or quote side.
    /// @param mkt The market object
    /// @return True if the market includes an ERC20 or synthetic asset
    function isFungible(Structs.Market memory mkt) public pure returns (bool) {
        return
            (isERC20(mkt.base) || isSynth(mkt.base) || isNative(mkt.base)) &&
            (isERC20(mkt.quote) || isSynth(mkt.quote) || isNative(mkt.quote));
    }

    /// @dev Check if a market is an instant market (both assets are on-chain).
    /// @param mkt The market object
    /// @return True if the market is an instant market
    function isInstantMarket(Structs.Market memory mkt) public pure returns (bool) {
        return
            ((isERC20(mkt.base) && isERC20(mkt.quote)) ||
                (isERC20(mkt.base) && isNFT(mkt.quote)) ||
                (isERC20(mkt.base) && isNative(mkt.quote)) ||
                (isNFT(mkt.base) && isERC20(mkt.quote)) ||
                (isNFT(mkt.base) && isNFT(mkt.quote))) ||
            (isNFT(mkt.base) && isNative(mkt.quote)) ||
            (isNative(mkt.base) && isNative(mkt.quote)) ||
            (isNative(mkt.base) && isERC20(mkt.quote)) ||
            (isNative(mkt.base) && isNFT(mkt.quote));
    }

    /// @dev Check if a market includes an ERC20 or native asset on the base or quote side.
    /// @param mkt The market object
    /// @return True if the market includes an ERC20 asset
    function hasERC20OrNative(Structs.Market memory mkt) public pure returns (bool) {
        return isERC20(mkt.base) || isERC20(mkt.quote) || isNative(mkt.base) || isNative(mkt.quote);
    }

    /// @dev Check if an asset exist on-chain
    /// @param asset The asset object
    /// @return True if the asset exist on-chain
    function isOnchain(Structs.Asset memory asset) public pure returns (bool) {
        return isERC20(asset) || isNFT(asset) || isNative(asset);
    }

    /// @dev Determine the payer of the swap order.
    /// @param mkt The market object
    /// @param o The order object
    function getPayer(Structs.Market memory mkt, Structs.Order storage o) public view returns (address) {
        address payer;
        if (isOnchain(mkt.base) && isSynth(mkt.quote)) {
            payer = o.taker;
        } else if (isSynth(mkt.base) && isSynth(mkt.quote)) {
            payer = o.taker;
        } else if (isSynth(mkt.base) && isOnchain(mkt.quote)) {
            payer = o.provider;
        }
        return payer;
    }
}
合同源代码
文件 28 的 44:Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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 10, 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 * 8) < value ? 1 : 0);
        }
    }
}
合同源代码
文件 29 的 44:MathLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

library MathLib {
    function sqrt(uint256 x) internal pure returns (uint256 y) {
        uint256 z = (x + 1) / 2;
        y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }

    /**
     * @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 / b + (a % b == 0 ? 0 : 1);
    }
}
合同源代码
文件 30 的 44:NonFungibleLiquidityLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../token/interfaces/IERC721.sol";
import "../token/interfaces/IERC1155.sol";
import "../common/StrippedInterfaces.sol";
import "../common/MathLib.sol";
import "./AuxLib.sol";
import "../market/MarketAuxLib.sol";
import "./StructsLib.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../feepurse/interfaces/IFeePurse.sol";
import "./LiqAuxLib.sol";

library NonFungibleLiquidityLib {
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;
    using AuxLib for uint256[];
    using AuxLib for Structs.Market;
    using AuxLib for Structs.Liquidity;
    using AuxLib for address;

    event UpdatedNonFungibleLiquidity(
        address indexed provider,
        address indexed marketId,
        address target,
        bool deduct,
        bool paused,
        uint256 lid,
        uint256[] ids,
        uint256 price,
        uint256[] wantOrIds,
        uint256[] wantAndIds,
        uint256 timeToPay
    );

    /// @dev Create or update non-fungible liquidity.
    /// Covers NFT/ERC20, NFT/SYNTH, NFT/NFT
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    /// @param slot The parameters needed to create or update liquidity.
    function updateNonFungibleLiquidity(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.UpdateLiquiditySlot memory slot
    ) external {
        require(slot.deduct || s2.marketCore.isPutPermitted(slot.marketId, msg.sender));
        Structs.Market memory mkt = s2.marketCore.getMarket(slot.marketId);
        require(mkt.base.isNFT(), "OC: UNEXP_BASE");
        require(!mkt.isInstantMarket() || slot.timeToPay == 0, "OC: TTP_NREQ");
        require(slot.timeToPay == 0 || slot.timeToPay >= s1.minTimeToPay, "OC: LOW_TTP");
        require(mkt.quote.isFungible() || slot.price == 0, "OC: PRICE_NREQ");
        require(!mkt.quote.isFungible() || slot.price > 0, "OC: PRICE_REQ");
        require(!mkt.quote.isFungible() || (slot.wantOrIds.length == 0 && slot.wantAndIds.length == 0), "OC: WANT_IDS_NREQ");
        require(slot.wantOrIds.isUnique() && slot.wantAndIds.isUnique(), "OC: WANT_IDS_NUNIQ");
        require(slot.wantOrIds.isMaxLen16() && slot.wantAndIds.isMaxLen16(), "OC: WANT_IDS_HSIZE");
        require(slot.ids.isMaxLen16(), "OC: IDS_HSIZE");
        require(slot.ids.isUnique(), "OC: IDS_NUNIQ");

        // Check if the sender has the required market PUT badges.
        LiqAuxLib.checkMarketPutBadges(s2, slot.marketId);

        // Get the liquidity. If the liquidity does not exist, create a new one
        Structs.Liquidity storage liq;
        if (AuxLib.isLiquidityExist(s1, slot.marketId, msg.sender, slot.lid)) {
            liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
        } else {
            uint256[] memory zero = new uint256[](0);
            s1.liquidity[slot.marketId][msg.sender].push(
                Structs.Liquidity(zero, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, zero, zero, false, slot.target)
            );
            slot.lid = s1.liquidity[slot.marketId][msg.sender].length - 1;
            liq = s1.liquidity[slot.marketId][msg.sender][slot.lid];
        }

        // For new liquidity, ensure ids is not empty
        require(!liq.isLiquidityEmpty() || slot.ids.length > 0, "OC: REQ_IDS");

        // Handle increment
        // - Transfer the base asset into the contract
        // - Increment listing pool balance if BASE asset is verified and market is not permissioned
        if (!slot.deduct) {
            uint256 numIds = slot.ids.length;
            mkt.numLiquidity += numIds;

            for (uint256 i = 0; i < numIds; i++) {
                liq.ids.push(slot.ids[i]);
                if (mkt.base.isERC721()) {
                    IERC721(mkt.base.addr).transferFrom(msg.sender, address(this), slot.ids[i]);
                } else {
                    IERC1155(mkt.base.addr).safeTransferFrom(msg.sender, address(this), slot.ids[i], 1, "");
                }
            }

            if (!mkt.hasERC20OrNative()) {
                LiqAuxLib.lockFeeForProtocol(s1, s2, mkt, liq, numIds);
                LiqAuxLib.lockFeeForMarketCreator(s2, mkt, liq, numIds);
            }
        }

        // Handle decrement
        // - Remove indexes provided in slot.ids
        // - Transfer asset back to owner
        // - Decrement listing pool balance if BASE is verified and market is not permissioned
        if (slot.deduct && slot.ids.length > 0) {
            uint256 popped = 0;
            for (uint256 i = 0; i < slot.ids.length; i++) {
                if (slot.ids[i] - popped >= liq.ids.length) continue;
                uint256 id = liq.ids[slot.ids[i] - popped];
                liq.ids[slot.ids[i] - popped] = liq.ids[liq.ids.length - 1];
                liq.ids.pop();
                popped++;

                if (mkt.base.isERC721()) {
                    IERC721(mkt.base.addr).transferFrom(address(this), msg.sender, id);
                } else {
                    IERC1155(mkt.base.addr).safeTransferFrom(address(this), msg.sender, id, 1, "");
                }
            }

            if (!mkt.hasERC20OrNative()) {
                LiqAuxLib.unlockFeeForProtocol(s2, mkt, liq);
                LiqAuxLib.unlockFeeForMarketCreator(s2, mkt, liq);
            }

            mkt.numLiquidity -= popped;
        }

        liq.timeToPay = slot.timeToPay;
        liq.price = slot.price;
        liq.wantAndIds = slot.wantAndIds;
        liq.wantOrIds = slot.wantOrIds;
        liq.target = slot.target;
        liq.paused = slot.pause;
        s2.marketCore.setCounters(slot.marketId, false, false, true, mkt.numLiquidity);

        emit UpdatedNonFungibleLiquidity(
            msg.sender,
            slot.marketId,
            slot.target,
            slot.deduct,
            slot.pause,
            slot.lid,
            slot.ids,
            slot.price,
            slot.wantOrIds,
            slot.wantAndIds,
            slot.timeToPay
        );
    }
}
合同源代码
文件 31 的 44:OfferLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../core/AuxLib.sol";
import "../core/SwapFungibleLib.sol";
import "../core/SwapNonFungibleLib.sol";
import "../core/StructsLib.sol";
import "../market/MarketAuxLib.sol";
import "../token/interfaces/IERC20.sol";
import "../common/StrippedInterfaces.sol";


library OfferLib {
    using MarketAuxLib for Structs.Asset;
    using AuxLib for uint256[];

    event NewOffer(
        address marketId,
        address indexed provider,
        uint256 lid,
        address indexed offerer,
        uint256 offerId,
        uint256 amount,
        uint256 price,
        uint256[] ids,
        uint256 expireDuration
    );
    event OfferUpdated(
        uint256 indexed offerId,
        address indexed offerer,
        uint256 amount,
        uint256 price,
        uint256[] ids,
        uint256 expireDuration
    );
    event OfferAccepted(uint256 indexed offerId, address indexed acceptor, uint256 execDelayDur);
    event OfferUnaccepted(uint256 indexed offerId, address indexed unacceptor);
    event OfferCancelled(uint256 indexed offerId, address indexed canceller);
    event OfferExecuted(uint256 indexed offerId, address indexed executor);

    struct CheckOfferAddresses {
        address marketId;
        address provider;
        address recipient;
    }

    struct CheckOfferIntSlots {
        uint256 lid;
        uint256 amount;
        uint256 price;
        uint256 deadline;
        uint256 offerId;
        uint256 operator;
    }

    struct CheckOfferSlot {
        CheckOfferAddresses addrs;
        uint256[] ids;
        CheckOfferIntSlots ints;
    }

    /// @dev Check offer parameters
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    /// @param slot The parameters needed to check the offer.
    function checkOffer(StructsLib.State storage s1, StructsLib.State2 storage s2, CheckOfferSlot memory slot) public view {
        require(AuxLib.hasNonZeroLiquidity(s1, slot.addrs.marketId, slot.addrs.provider, slot.ints.lid), "OC: ZERO_LIQ");
        require(slot.ints.deadline > block.timestamp, "OC: PAST_DEADLINE");
        Structs.Liquidity storage liq = s1.liquidity[slot.addrs.marketId][slot.addrs.provider][slot.ints.lid];
        Structs.Market memory mkt = s2.marketCore.getMarket(slot.addrs.marketId);
        StructsLib.SwapSlotAddresses memory addrs = StructsLib.SwapSlotAddresses(
            slot.addrs.marketId,
            slot.addrs.provider,
            slot.addrs.recipient
        );
        StructsLib.SwapSlot memory ss = StructsLib.SwapSlot(
            slot.ints.lid,
            slot.ints.amount,
            slot.ints.price,
            slot.ids,
            slot.ints.offerId,
            slot.ints.operator,
            addrs
        );

        if (mkt.base.isFungible()) AuxLib.checkFungibleSwapParams(ss, mkt, liq);
        else AuxLib.checkNonFungibleSwapParams(ss, mkt, liq);

        // Ensure sender has enough quote token to make offer
        if (mkt.quote.isERC20() || mkt.quote.isNative()) {
            uint256 cost;
            if (mkt.base.isNFT()) cost = slot.ints.price;
            else cost = (slot.ints.amount * slot.ints.price) / 10 ** ((mkt.base.isSynth()) ? 18 : IERC20(mkt.base.addr).decimals());
            require(mkt.quote.isNative() || IERC20(mkt.quote.addr).balanceOf(msg.sender) >= cost, "OC: LOW_BAL");
            require(!mkt.quote.isNative() || msg.sender.balance >= cost, "OC: LOW_BAL");
        }

        // Ensure sender owns offered NFT
        if (mkt.quote.isNFT()) {
            uint256 n = slot.ids.length;
            for (uint256 i = 0; i < n; i++) {
                if (mkt.quote.isERC721()) require(IERC721Strip(mkt.quote.addr).ownerOf(slot.ids[i]) == msg.sender, "OC: NOT_TOK_OWNER");
                else require(IERC1155Strip(mkt.quote.addr).balanceOf(msg.sender, slot.ids[i]) > 0, "OC: NOT_TOK_OWNER");
            }
        }
    }

    struct CreateOfferAddressParams {
        address marketId;
        address provider;
        address recipient;
    }

    struct CreateOfferIntParams {
        uint256 lid;
        uint256 amount;
        uint256 price;
        uint256 expireDur;
        uint256 operator;
    }

    /// @dev Create an offer
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    function createOffer(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        CreateOfferAddressParams memory offerAddrs,
        CreateOfferIntParams memory offerInts,
        uint256[] memory ids
    ) public returns (uint256) {
        CheckOfferAddresses memory addrs = CheckOfferAddresses(offerAddrs.marketId, offerAddrs.provider, offerAddrs.recipient);
        CheckOfferIntSlots memory ints = CheckOfferIntSlots(
            offerInts.lid,
            offerInts.amount,
            offerInts.price,
            block.timestamp + offerInts.expireDur,
            s2.offers.length + 1,
            offerInts.operator
        );
        CheckOfferSlot memory slot = CheckOfferSlot(addrs, ids, ints);
        checkOffer(s1, s2, slot);

        s2.offers.push(
            Structs.Offer(
                msg.sender,
                offerAddrs.marketId,
                offerAddrs.provider,
                offerAddrs.recipient,
                offerInts.lid,
                offerInts.amount,
                offerInts.price,
                ids,
                0,
                block.timestamp + offerInts.expireDur,
                0,
                offerInts.operator,
                false,
                false,
                false
            )
        );

        emit NewOffer(
            offerAddrs.marketId,
            offerAddrs.provider,
            offerInts.lid,
            msg.sender,
            s2.offers.length,
            offerInts.amount,
            offerInts.price,
            ids,
            offerInts.expireDur
        );

        return s2.offers.length;
    }

    /// @dev Update an offer
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    /// @param offerId The offer ID
    /// @param amount The amount of liquidity to offer
    /// @param price The price of the liquidity
    /// @param ids The IDs of the NFTs to offer if quote is NFT
    /// @param expireDur The duration of the offer
    function updateOffer(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        uint256 offerId,
        uint256 amount,
        uint256 price,
        uint256[] memory ids,
        uint256 expireDur
    ) public {
        require(offerId < s2.offers.length, "OC: UNKNOWN_OFFER");
        Structs.Offer storage offer = s2.offers[offerId];
        require(offer.creator == msg.sender, "OC: NOT_CREATOR");
        require(offer.deadline > block.timestamp, "OC: OFFER_EXPIRED");
        require(!offer.cancelled, "OC: OFFER_CANCELLED");
        require(!offer.executed, "OC: OFFER_EXECUTED");
        uint256 deadline = (expireDur == 0) ? offer.deadline : block.timestamp + expireDur;

        checkOffer(
            s1,
            s2,
            CheckOfferSlot(
                CheckOfferAddresses(offer.marketId, offer.provider, offer.recipient),
                ids,
                CheckOfferIntSlots(offer.lid, amount, price, deadline, offerId + 1, offer.operator)
            )
        );

        offer.amount = amount;
        offer.price = price;
        offer.ids = ids;
        offer.deadline = deadline;
        offer.accepted = false;
        emit OfferUpdated(offerId, msg.sender, amount, price, ids, expireDur);
    }

    /// @dev Accept an offer
    /// @param s2 Storage struct 2.
    /// @param offerId The offer ID
    /// @param delay The delay before the offer can be executed
    function acceptOffer(StructsLib.State2 storage s2, uint256 offerId, uint256 delay) public {
        require(offerId < s2.offers.length, "OC: UNKNOWN_OFFER");
        Structs.Offer storage offer = s2.offers[offerId];
        require(offer.provider == msg.sender, "OC: NOT_PROVIDER");
        require(offer.deadline > block.timestamp, "OC: OFFER_EXPIRED");
        require(!offer.cancelled, "OC: OFFER_CANCELLED");
        require(!offer.executed, "OC: OFFER_EXECUTED");
        require(!offer.accepted, "OC: ALREADY_ACCEPTED");
        offer.accepted = true;
        offer.execAt = block.timestamp + delay;
        emit OfferAccepted(offerId, msg.sender, delay);
    }

    /// @dev Unaccept an offer
    /// @param s2 Storage struct 2.
    /// @param offerId The offer ID
    function unacceptOffer(StructsLib.State2 storage s2, uint256 offerId) public {
        require(offerId < s2.offers.length, "OC: UNKNOWN_OFFER");
        Structs.Offer storage offer = s2.offers[offerId];
        require(offer.provider == msg.sender, "OC: NOT_PROVIDER");
        require(offer.accepted, "OC: NOT_ACCEPTED");
        require(!offer.executed, "OC: OFFER_EXECUTED");
        offer.accepted = false;
        offer.execAt = 0;
        emit OfferUnaccepted(offerId, msg.sender);
    }

    /// @dev Cancel an offer
    /// @param s2 Storage struct 2.
    /// @param offerId The offer ID
    function cancelOffer(StructsLib.State2 storage s2, uint256 offerId) public {
        require(offerId < s2.offers.length, "OC: UNKNOWN_OFFER");
        Structs.Offer storage offer = s2.offers[offerId];
        require(offer.creator == msg.sender, "OC: NOT_CREATOR");
        require(offer.deadline > block.timestamp, "OC: OFFER_EXPIRED");
        require(!offer.executed, "OC: OFFER_EXECUTED");
        require(!offer.cancelled, "OC: ALREADY_CANCELLED");
        offer.cancelled = true;
        emit OfferCancelled(offerId, msg.sender);
    }

    /// @dev Execute an offer
    /// @param s1 Storage struct.
    /// @param s2 Storage struct 2.
    /// @param offerId The offer ID
    function executeOffer(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        uint256 offerId
    ) public returns (Structs.Offer storage) {
        require(offerId < s2.offers.length, "OC: UNKNOWN_OFFER");
        Structs.Offer storage offer = s2.offers[offerId];
        require(offer.creator == msg.sender, "OC: NOT_CREATOR");
        require(offer.accepted, "OC: NOT_ACCEPTED");
        require(!offer.executed, "OC: ALREADY_EXECUTED");
        require(offer.execAt <= block.timestamp, "OC: DELAY_ACTIVE");
        offer.executed = true;

        Structs.Market memory mkt = s2.marketCore.getMarket(offer.marketId);
        StructsLib.SwapSlotAddresses memory addrs = StructsLib.SwapSlotAddresses(offer.marketId, offer.provider, offer.recipient);
        StructsLib.SwapSlot memory sp = StructsLib.SwapSlot(
            offer.lid,
            offer.amount,
            offer.price,
            offer.ids,
            offerId + 1,
            offer.operator,
            addrs
        );

        emit OfferExecuted(offerId, msg.sender);

        if (mkt.base.isFungible()) SwapFungibleLib.swap(s1, s2, sp, mkt);
        else SwapNonFungibleLib.swap(s1, s2, sp, mkt);

        return offer;
    }
}
合同源代码
文件 32 的 44:OrderLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../common/SigLib.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "../market/MarketAuxLib.sol";
import "./LiquidityLib.sol";

import {IERC721Strip, IERC1155Strip} from "../common/StrippedInterfaces.sol";

library OrderLib {
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;
    using AuxLib for address;

    event SwapOrder(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        uint256[7] numTypeInfo,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );
    event OrderCancelled(uint256 indexed orderID, address indexed canceller, bool indexed byDispute);
    event SwapOrderPaid(uint256 indexed orderID, address payer, uint256 disputeDelayDur);
    event FinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);

    /// @notice Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH, SYNTH/NFT, NFT/SYNTH, NATIVE/SYNTH, SYNTH/NATIVE
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param sp The swap parameters
    /// @param mkt The market object
    /// @param liq The liquidity object
    function createOrder(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public {
        // Ensure number of open orders and disputes are below limit
        require(liq.openOrders < s1.ints.maxOpenOrders, "OC: MAX_OPEN_ORDERS");
        require(liq.openDisputes < s1.ints.maxOpenDisputes, "OC: MAX_OPEN_DISPUTES");

        Structs.Order memory o;
        o.offerId = sp.offerId;
        o.amount = sp.amount;
        o.price = (sp.offerId > 0) ? sp.maxPrice : liq.price;

        // When quote is NFT, transfer NFTs into contract
        if (mkt.quote.isNFT()) {
            // Find the IDs that match the wants request.
            // If swap was initiated via offer, use offer supplied ids.
            o.takerIds = (sp.offerId > 0) ? sp.ids : LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
            require(o.takerIds.length > 0, "OC: NO_ID_MATCH");
            for (uint256 i = 0; i < o.takerIds.length; i++) {
                if (o.takerIds[i] == 0) continue;
                if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(msg.sender, address(this), o.takerIds[i]);
                else IERC1155Strip(mkt.quote.addr).safeTransferFrom(msg.sender, address(this), o.takerIds[i], 1, "");
            }
        }

        // When quote is ERC20, transfer quote into contract.
        // When quote is native, make sure the deposit sent is enough.
        if (mkt.quote.isERC20()) {
            uint256 cost = (sp.amount * o.price) / 10 ** 18;
            IERC20(mkt.quote.addr).transferFrom(msg.sender, address(this), cost);
        } else if (mkt.quote.isNative()) {
            uint256 cost = (sp.amount * o.price) / 10 ** 18;
            require(msg.value >= cost, "OC: LOW_DEPOSIT");
        }

        // Update liquidity
        uint256 deduction;
        if (mkt.base.isNFT()) {
            o.providerIds = liq.ids;
            deduction = liq.ids.length;
            delete liq.ids;
        } else if (mkt.quote.isNFT()) {
            o.amount = liq.amount;
            deduction = o.amount;
            liq.amount = 0;
        } else if (sp.amount > 0) {
            liq.amount -= sp.amount;
            deduction = sp.amount;
        }

        // Create an order.
        uint256 timeToPay = (liq.timeToPay > 0) ? liq.timeToPay : s1.minTimeToPay;
        o.marketId = sp.addrs.marketId;
        o.provider = sp.addrs.provider;
        o.operator = sp.operator;
        o.lid = sp.lid;
        o.taker = msg.sender;
        o.recipient = sp.addrs.recipient;
        o.createdAt = block.timestamp;
        o.payDeadline = block.timestamp + timeToPay;
        o.cancelAt = block.timestamp + timeToPay + s1.orderGraceDur;
        s1.orders.push(o);
        s1.numOrders++;
        liq.openOrders += 1;
        s2.marketCore.setCounters(sp.addrs.marketId, true, false, false, mkt.numOrders + 1);
        s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - deduction);

        emit SwapOrder(
            sp.addrs.marketId,
            sp.addrs.provider,
            msg.sender,
            sp.addrs.recipient,
            [s1.numOrders - 1, sp.lid, sp.amount, o.price, sp.offerId, sp.operator, timeToPay + s1.orderGraceDur],
            o.providerIds,
            o.takerIds
        );
    }

    /// @notice Release an order by a market manager's signature
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param owner The contract owner
    /// @param oid The order ID.
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function cancelOrderByManagerSig(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        address owner,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) public {
        Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
        require(mkt.manager != address(0), "OC: NO_MGR_SET");
        require(expireAt > block.timestamp, "OC: EXPIRED_SIG");
        require(verifyMarketManagerSig(mkt.manager, oid, 2, expireAt, signature), "OC: BAD_SIG");
        cancelOrder(s1, s2, owner, oid, false, true);
        emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 2);
    }

    /// @notice Cancel an inactive order. An inactive order is one where:
    /// The order has not been marked as paid, released or disputed
    /// and has reached its cancel time.
    /// @param s1 State object
    /// @param s2 State2 object
    /// @param owner The contract owner
    /// @param id is the unique order ID
    /// @param byDispute Indicates that the method is called by the dispute manager contract.
    /// @param byManager Indicates that the method is called by the market manager.
    function cancelOrder(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        address owner,
        uint256 id,
        bool byDispute,
        bool byManager
    ) public {
        Structs.Order storage o = s1.orders[id];
        Structs.Market memory mkt = s2.marketCore.getMarket(o.marketId);
        require(
            byDispute ||
                byManager ||
                o.provider == msg.sender ||
                o.taker == msg.sender ||
                mkt.manager == msg.sender ||
                s2.minerReg.isMiner(msg.sender) ||
                owner == msg.sender,
            "OC: NOT_AUTHORIZED"
        );
        require(byDispute || (!o.paid && !o.released && !o.disputed && !o.cancelled), "OC: NOT_INACTIVE");
        require(byDispute || o.cancelAt <= block.timestamp, "OC: NOT_CANCEL_TIME");

        Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];

        // Handle ERC20/SYNTH, SYNTH/ERC20, SYNTH/NATIVE, NATIVE/SYNTH
        if (mkt.isFungible() && mkt.hasERC20OrNative()) {
            liq.amount += o.amount;
            s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);

            if (mkt.quote.isERC20()) {
                uint256 cost = (o.amount * o.price) / 10 ** 18;
                IERC20(mkt.quote.addr).transfer(o.taker, cost);
            }

            if (mkt.quote.isNative()) {
                uint256 cost = (o.amount * o.price) / 10 ** 18;
                payable(o.taker).transfer(cost);
            }
        }

        // Handle SYNTH/SYNTH
        if (mkt.base.isSynth() && mkt.quote.isSynth()) {
            liq.amount += o.amount;
            s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);
        }

        // Handle SYNTH/NFT
        if (mkt.base.isSynth() && mkt.quote.isNFT()) {
            liq.amount += o.amount;
            s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + o.amount);
            for (uint256 i = 0; i < o.takerIds.length; i++) {
                if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(address(this), o.taker, o.takerIds[i]);
                else IERC1155Strip(mkt.quote.addr).safeTransferFrom(address(this), o.taker, o.takerIds[i], 1, "");
            }
        }

        // Handle NFT/SYNTH
        if (mkt.base.isNFT() && mkt.quote.isSynth()) {
            liq.ids = o.providerIds;
            s2.marketCore.setCounters(o.marketId, false, false, true, mkt.numLiquidity + liq.ids.length);
        }

        liq.openOrders -= 1;
        o.cancelled = true;
        o.finalizer = msg.sender;

        if (s2.minerReg.isMiner(msg.sender)) {
            s2.minerReg.addReward(MinerOperation.Cancel, msg.sender);
        }

        emit OrderCancelled(id, msg.sender, byDispute);
    }

    /// @notice Mark a swap order as paid using a market manager's signature
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param oid The order ID.
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function markOrderAsPaidByManagerSig(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) public {
        Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
        require(mkt.manager != address(0), "OC: NO_MGR_SET");
        require(expireAt > block.timestamp, "OC: EXPIRED_SIG");
        require(verifyMarketManagerSig(mkt.manager, oid, 0, expireAt, signature), "OC: BAD_SIG");
        markOrderAsPaid(s1, s2.marketCore, oid, true);
        emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 0);
    }

    /// @notice Mark a swap order as paid.
    /// Order must be have been released or cancelled.
    /// When the base asset is ERC20, the sender must be the taker.
    /// When the base asset is synthetic,the sender must be the provider.
    /// @param s1 State object
    /// @param marketCore The MarketCore contract address
    /// @param id The order ID.
    /// @param byManager Indicates that the method is called by the market manager
    function markOrderAsPaid(StructsLib.State storage s1, IMarketCore marketCore, uint256 id, bool byManager) public {
        require(AuxLib.hasOrder(s1, id), "OC: UNKNOWN_ORDER");
        Structs.Order storage o = s1.orders[id];
        require(!o.released && !o.cancelled && !o.paid, "OC: NOT_ACTIVE");
        Structs.Market memory market = marketCore.getMarket(o.marketId);
        require(byManager || msg.sender == MarketAuxLib.getPayer(market, o), "OC: NOT_PAYER");
        o.paid = true;
        o.disputeFrom = block.timestamp + market.dur.disputeDelayDur;
        emit SwapOrderPaid(id, byManager ? market.manager : msg.sender, market.dur.disputeDelayDur);
    }

    /// @notice Marks an order as `disputed`
    /// @param s1 State object
    /// @param marketCore The MarketCore contract address
    /// @param id The order ID or index.
    function markAsDisputed(StructsLib.State storage s1, IMarketCore marketCore, uint256 id) public {
        Structs.Order storage o = s1.orders[id];
        o.disputed = true;
        Structs.Market memory m = marketCore.getMarket(o.marketId);
        m.numDisputes++;
        marketCore.setCounters(o.marketId, false, true, false, m.numDisputes);
    }

    /// @notice Verify a market manager's signature
    /// @param signer The market manager's address
    /// @param id The order ID
    /// @param action Indicates the manager action (0: paid, 1: release, 2: cancel)
    /// @param expireAt Indicates the timestamp when the signature is to expire.
    /// @param signature The signature created by the manager
    function verifyMarketManagerSig(
        address signer,
        uint256 id,
        uint action,
        uint256 expireAt,
        bytes memory signature
    ) internal view returns (bool) {
        if (address(0) == signer) return false;
        bytes32 messageHash = keccak256(abi.encode(block.chainid, id, action, expireAt));
        bytes32 ethSignedMessageHash = SigLib.getSignedMessageHash(messageHash);
        return SigLib.recoverSigner(ethSignedMessageHash, signature) == signer;
    }
}
合同源代码
文件 33 的 44:OrderReleaseLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "../common/SigLib.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "../market/MarketAuxLib.sol";
import "./LiquidityLib.sol";
import "./FeeLib.sol";
import {IERC721Strip, IERC1155Strip} from "../common/StrippedInterfaces.sol";

library OrderReleaseLib {
    using MarketAuxLib for Structs.Asset;
    using MarketAuxLib for Structs.Market;
    using AuxLib for address;

    event Swap(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        address releaser,
        uint256[6] numTypeInfo,
        bool byDispute,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );
    event FinalizedByManagerSig(address manager, address finalizer, uint256 orderId, uint action);

    /// @notice Release an order by a market manager's signature
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param owner The owner of the contract
    /// @param oid The order ID.
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function releaseOrderByManagerSig(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        address owner,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) public {
        Structs.Market memory mkt = s2.marketCore.getMarket(s1.orders[oid].marketId);
        require(mkt.manager != address(0), "OC: NO_MGR_SET");
        require(expireAt > block.timestamp, "OC: EXPIRED_SIG");
        require(verifyMarketManagerSig(mkt.manager, oid, 1, expireAt, signature), "OC: BAD_SIG");
        releaseOrder(s1, s2, owner, oid, false, true);
        emit FinalizedByManagerSig(mkt.manager, msg.sender, oid, 1);
    }

    /// @notice Release asset.
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param owner The owner of the contract
    /// @param oid The order ID.
    /// @param byDispute Indicates that the method is called by the dispute manager.
    /// @param byManager Indicates that the method is called by the market manager via a manager signature.
    function releaseOrder(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        address owner,
        uint256 oid,
        bool byDispute,
        bool byManager
    ) public {
        Structs.Order storage o = s1.orders[oid];
        require(!o.cancelled && !o.released, "OC: ALREADY_FINALIZED");
        Structs.Market memory mkt = s2.marketCore.getMarket(o.marketId);
        address releaser = (byDispute)
            ? msg.sender
            : (byManager)
                ? mkt.manager
                : (o.taker == MarketAuxLib.getPayer(mkt, o))
                    ? o.provider
                    : o.taker;
        require(byDispute || byManager || msg.sender == releaser || msg.sender == owner, "OC: NOT_RELEASER");
        require(o.paid, "OC: ORDER_UNPAID");

        Structs.Asset memory asset;

        // Handle ERC20/SYNTH, SYNTH/ERC20, SYNTH/NATIVE, NATIVE/SYNTH
        if (mkt.isFungible() && mkt.hasERC20OrNative()) {
            asset = releaseForFungibles(s1, s2, o, mkt);
        }

        // Handle SYNTH/SYNTH
        if (mkt.base.isSynth() && mkt.quote.isSynth()) {
            asset = releaseForSynthToSynthOrNFT(s1, s2, o, mkt);
        }

        // Handle SYNTH/NFT
        if (mkt.base.isSynth() && mkt.quote.isNFT()) {
            asset = releaseForSynthToNFT(s1, s2, o, mkt);
        }

        // Handle NFT/SYNTH
        if (mkt.base.isNFT() && mkt.quote.isSynth()) {
            asset = releaseForNFTToSynth(s1, s2, o, mkt);
        }

        // Mark as released.
        o.released = true;
        o.finalizer = releaser;

        // Decrement open order count
        s1.liquidity[o.marketId][o.provider][o.lid].openOrders -= 1;

        // Increment orderID since '0' indicates no order ID.
        emit Swap(
            o.marketId,
            o.provider,
            o.taker,
            o.recipient,
            releaser,
            [oid + 1, o.lid, o.amount, o.price, o.offerId, o.operator],
            byDispute,
            o.providerIds,
            o.takerIds
        );
    }

    /// @dev Release asset for SYNTH/NFT
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param o The order object
    /// @param mkt The market object
    function releaseForSynthToNFT(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        Structs.Order storage o,
        Structs.Market memory mkt
    ) internal returns (Structs.Asset memory asset) {
        asset = releaseForSynthToSynthOrNFT(s1, s2, o, mkt);

        // Transfer locked NFT to provider
        for (uint256 i = 0; i < o.takerIds.length; i++) {
            if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(address(this), o.provider, o.takerIds[i]);
            else IERC1155Strip(mkt.quote.addr).safeTransferFrom(address(this), o.provider, o.takerIds[i], 1, "");
        }

        return asset;
    }

    struct BoolSlot {
        bool sharable;
        bool isOperator;
    }

    /// @dev Release asset for NFT/SYNTH
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param o The order object
    /// @param mkt The market object
    function releaseForNFTToSynth(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        Structs.Order storage o,
        Structs.Market memory mkt
    ) internal returns (Structs.Asset memory asset) {
        Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                o.lid,
                o.amount,
                o.price,
                s2.nft,
                o.provider,
                o.taker
            )
        );

        // Unlock fee from fee purse
        s2.feePurse.unlock(o.provider, liq.protoFeePurseId);
        s2.feePurse.unlock(o.provider, liq.mktCreatorFeePurseId);
        liq.protoFeePurseId = 0;
        liq.mktCreatorFeePurseId = 0;

        // Transfer market creator fee to market creator
        s2.feePurse.transferFrom(o.provider, mkt.creator, feeInfo.mktCreatorFee);

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fee to sharable pool  if asset is shareable
        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(o.operator));
        if (bools.sharable) {
            s2.feePurse.transferFrom(o.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) / 10000);
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            address opAddr = s2.operatorMgr.getOperator(o.operator);
            s2.feePurse.transferFrom(o.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) / 10000);
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s2.feePurse.transferFrom(o.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) / 10000);

        // Transfer the base asset to recipient
        for (uint256 i = 0; i < o.providerIds.length; i++)
            if (mkt.base.isERC721())
                IERC721Strip(mkt.base.addr).transferFrom(address(this), o.recipient.setOrDef(o.taker), o.providerIds[i]);
            else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), o.recipient.setOrDef(o.taker), o.providerIds[i], 1, "");

        return mkt.base;
    }

    struct IntSlot {
        uint256 remaining;
        uint256 protoFeeRate;
        uint256 mktCreatorFeeRate;
        uint256 protoFee;
        uint256 mktCreatorFee;
    }

    /// @dev Release asset for SYNTH/SYNTH & SYNTH/NFT
    /// @param s1 The State object
    /// @param s2 The State2 object
    /// @param o The order object
    /// @param mkt The market object
    function releaseForSynthToSynthOrNFT(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        Structs.Order memory o,
        Structs.Market memory mkt
    ) internal returns (Structs.Asset memory asset) {
        Structs.Liquidity storage liq = s1.liquidity[o.marketId][o.provider][o.lid];

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                o.lid,
                o.amount,
                o.price,
                s2.nft,
                o.provider,
                o.taker
            )
        );

        // Unlock fee in purse
        s2.feePurse.unlock(o.provider, liq.protoFeePurseId);
        s2.feePurse.unlock(o.provider, liq.mktCreatorFeePurseId);

        // Transfer market creator order fee to market creator
        s2.feePurse.transferFrom(o.provider, mkt.creator, feeInfo.mktCreatorFee);

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fee to sharable pool  if asset is shareable
        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, asset.addr), s2.operatorMgr.isOperator(o.operator));
        if (bools.sharable) {
            s2.feePurse.transferFrom(o.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) / 10000);
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            address opAddr = s2.operatorMgr.getOperator(o.operator);
            s2.feePurse.transferFrom(o.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) / 10000);
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s2.feePurse.transferFrom(o.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) / 10000);

        // Lock fee for the remaining liquidity
        liq.protoFeePurseId = s2.feePurse.lock(o.provider, (feeInfo.protoFeeRate * feeInfo.remaining) / 10 ** 18);
        liq.mktCreatorFeePurseId = s2.feePurse.lock(o.provider, (feeInfo.mktCreatorFeeRate * feeInfo.remaining) / 10 ** 18);

        return mkt.base;
    }

    function releaseForFungibles(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        Structs.Order storage o,
        Structs.Market memory mkt
    ) internal returns (Structs.Asset memory asset) {
        asset = mkt.base.isOnchain() ? mkt.base : mkt.quote;

        // Compute fee
        Structs.Liquidity memory liq = s1.liquidity[o.marketId][o.provider][o.lid];
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                o.lid,
                o.amount,
                o.price,
                s2.nft,
                o.provider,
                o.taker
            )
        );

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fees to market creator
        s1.feeBalance[mkt.creator][asset.addr] += feeInfo.mktCreatorFee;

        // Disburse fee to sharable pool  if asset is shareable
        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, asset.addr), s2.operatorMgr.isOperator(o.operator));
        if (bools.sharable) {
            s1.shareablesBalance[asset.addr] += (feeInfo.protoFee * s1.feeBPs[1]) / 10000;
            totalFeeBps -= s1.feeBPs[1];
        }

        //  Disburse fee to operator if operator is set
        if (bools.isOperator) {
            s1.operatorsBalance[s2.operatorMgr.getOperator(o.operator)][asset.addr] += (feeInfo.protoFee * s1.feeBPs[2]) / 10000;
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s1.inventorBalance[asset.addr] += (feeInfo.protoFee * totalFeeBps) / 10000;

        // Transfer the order amount
        address receiver = (!mkt.base.isSynth()) ? o.taker : o.provider;
        if (asset.isERC20()) {
            IERC20(asset.addr).transfer(o.recipient.setOrDef(receiver), feeInfo.amtToCharge - feeInfo.totalFees);
        } else if (asset.isNative()) {
            payable(o.recipient.setOrDef(receiver)).transfer(feeInfo.amtToCharge - feeInfo.totalFees);
        }

        return asset;
    }

    /// @notice Verify a market manager's signature
    /// @param signer The market manager's address
    /// @param id The order ID
    /// @param action Indicates the manager action (0: paid, 1: release, 2: cancel)
    /// @param expireAt Indicates the timestamp when the signature is to expire.
    /// @param signature The signature created by the manager
    function verifyMarketManagerSig(
        address signer,
        uint256 id,
        uint action,
        uint256 expireAt,
        bytes memory signature
    ) internal view returns (bool) {
        if (address(0) == signer) return false;
        bytes32 messageHash = keccak256(abi.encode(block.chainid, id, action, expireAt));
        bytes32 ethSignedMessageHash = SigLib.getSignedMessageHash(messageHash);
        return SigLib.recoverSigner(ethSignedMessageHash, signature) == signer;
    }
}
合同源代码
文件 34 的 44:Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.13;

import "@openzeppelin/contracts/utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
合同源代码
文件 35 的 44:SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.4;

import "../token/interfaces/IERC20.sol";
import "@openzeppelin/contracts/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;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    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 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");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
合同源代码
文件 36 的 44:SigLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

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

library SigLib {
    function getSignedMessageHash(bytes32 _messageHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash));
    }

    function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) internal pure returns (address) {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
        (address addr, ) = ECDSA.tryRecover(_ethSignedMessageHash, v, r, s);
        return addr;
    }

    function splitSignature(bytes memory sig) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
        require(sig.length == 65, "invalid signature length");
        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            v := byte(0, mload(add(sig, 96)))
        }
    }
}
合同源代码
文件 37 的 44:Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}
合同源代码
文件 38 的 44:StrippedInterfaces.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

interface IERC721Strip {
    function balanceOf(address owner) external view returns (uint256 balance);

    function transferFrom(address from, address to, uint256 tokenId) external;

    function ownerOf(uint256 _tokenId) external view returns (address);
}

interface IERC1155Strip {
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    function balanceOf(address _owner, uint256 _id) external view returns (uint256);
}
合同源代码
文件 39 的 44:Structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

library Structs {
    enum AssetType {
        SYNTH,
        ERC20,
        ERC721,
        ERC1155,
        NATIVE
    }

    struct Asset {
        address addr;
        AssetType assetType;
    }

    struct MarketDur {
        // Time to wait before a dispute can be started
        uint256 disputeDelayDur;
    }

    // Market represents a pair of assets where one
    // asset (base) is swapped for the other (synthetic).
    struct Market {
        // The timestamp when it was created.
        uint256 createdAt;
        // The number of orders created.
        uint256 numOrders;
        // The number of disputes created.
        uint256 numDisputes;
        // The number of liquidity
        uint256 numLiquidity;
        // The percentage or fee rate to charge as commission for each trade.
        // Example:
        // - For non-synthetic pairs: 1.55% should be represented as 155 (1.55 * 100).
        // - For synthetic/nft pairs: 1 wei per 1 unit of liquidity
        uint256 commission;
        // The id of the pool that that should mediate for this market
        uint256 poolId;
        // Duration params
        MarketDur dur;
        // The creator of the market.
        address creator;
        // The market manager
        address manager;
        // The base asset of the market.
        // This can be any ERC20 token.
        Asset base;
        // The quote asset of the market.
        // This can be any ERC20 token.
        Asset quote;
        // Market permissions
        MarketPerms perms;
    }

    struct MarketPerms {
        // Indicate whether the market is permissioned for base liquidity provisioning.
        // When set to true, only specific addresses will be
        // allowed to add base liquidity.
        bool put;
        // Indicate whether the market is permissioned for swaps.
        // When set to true, only specific addresses will be allowed to add base liquidity.
        bool swap;
        // Indicates whether the market can receive only offers.
        bool offerOnly;
        // Indicates whether to allow zero price
        bool allowZeroOfferPrice;
    }

    /// @dev Liquidity represents an amount of an asset
    struct Liquidity {
        // The ids of assets (for ERC721 and ERC1155 asset types)
        uint256[] ids;
        // The amount of asset (for ERC20 and SYNTH asset types)
        uint256 amount;
        // The price to sell 1 unit of liquidity for the quote asset.
        uint256 price;
        // The minimum amount that can be swapped.
        uint256 minSwap;
        /// The maximum amount that can be swapped.
        uint256 maxSwap;
        // The number of seconds payment is expected to be made before grace period
        uint256 timeToPay;
        // The number of open orders
        uint256 openOrders;
        // The number of open disputes
        uint256 openDisputes;
        // Purse ID of locked fee for protocol
        uint256 protoFeePurseId;
        // Purse ID of locked fee for market creator
        uint256 mktCreatorFeePurseId;
        // Fee rate for protocol fee
        uint256 protoFeeRate;
        // Fee rate for market creator fee
        uint256 mktCreatorFeeRate;
        // At least one of the ID must match
        uint256[] wantOrIds;
        // All IDs must match
        uint256[] wantAndIds;
        // Indicates whether new trades can be created
        bool paused;
        // Indicate the address that is permitted to take the liquidity.
        // Used when the liquidity provider wants to limit who can take the liquidity.
        address target;
    }

    /// @dev Order represents an on-going swap order.
    struct Order {
        // The ID of the market where the order belongs to.
        address marketId;
        // The address of the liquidity provider
        address provider;
        // The address of the liquidity taker
        address taker;
        // The recipient of the ordered asset (optional)
        address recipient;
        // The releaser or canceller
        address finalizer;
        // The liquidity ID
        uint256 lid;
        // The swap amount.
        uint256 amount;
        // The swap price.
        uint256 price;
        // The start time.
        uint256 createdAt;
        // The time before which payment is expected.
        uint256 payDeadline;
        // The time an un-paid order can be cancelled by both parties.
        uint256 cancelAt;
        // The time when a dispute can be started.
        uint256 disputeFrom;
        // The ID of the offer that executed the swap
        uint256 offerId;
        // The interface operator
        uint256 operator;
        // Taker token IDs locked in order. Filled when quote is NFT
        uint256[] takerIds;
        // Provider token IDs locked in order. Filled when base is NFT
        uint256[] providerIds;
        // Indicates whether the off-chain asset has been transferred or paid.
        bool paid;
        // Indicates whether the locked asset has been released.
        bool released;
        // Indicates whether the order is disputed.
        bool disputed;
        // Indicates whether the order has been cancelled
        bool cancelled;
    }

    struct Offer {
        // creator of the offer
        address creator;
        // marketId is the unique ID of the market.
        address marketId;
        // provider is the address of the base asset liquidity provider.
        address provider;
        // the actual recipient
        address recipient;
        // liquidity ID
        uint256 lid;
        // amount is the quantity of the base asset.
        // Applies when quote asset is fungible.
        uint256 amount;
        // price is the price to pay for the offer amount.
        // Applies when base and quote asset are fungible.
        uint256 price;
        // ids is a list of token ids to be offered.
        // Applies when quote is non-fungible.
        uint256[] ids;
        // The liquidity associated to the offer.
        uint256 liq;
        // deadline is the time the offer expires
        uint256 deadline;
        // The time when the offer can be executed
        uint256 execAt;
        // The ID of the interface operator
        uint256 operator;
        // Indicate whether the offer has been accepted
        bool accepted;
        // Indicate whether the offer has been cancelled
        bool cancelled;
        // Indicate whether the offer has been executed
        bool executed;
    }

    struct Mediator {
        address addr;
        uint256 bond;
        uint256 bondReleaseAt;
        uint256 matureAt;
    }

    struct TicketStatus {
        bool drafted;
        bool joined;
        bool blocked;
        bool slashed;
        bool paused;
    }

    struct Ticket {
        uint256 idx;
        uint256 poolId;
        uint256 bond;
        uint256 matureAt;
        int256 poolIdx;
        uint256 cancelAt;
        uint256 joinBy;
        uint256 numPaused;
        address owner;
        TicketStatus status;
    }

    struct TicketPool {
        bool exists;
        bool approvedMediatorOnly;
        bool approvedMarketOnly;
        uint256 reqBond;
        uint256 draftReward;
        uint256 winReward;
        uint256 noVoteSlashBP;
        uint256 loseVoteSlashBP;
        uint256 numDraftees;
        uint256 evidenceDur;
        uint256 predictDur;
    }

    struct DelegateTicket {
        address owner;
        uint256 id;
    }

    struct PoolTicket {
        address owner;
        uint256 idx;
    }

    struct PurchaseWindow {
        uint256 endTime;
        uint256 numPurchases;
        uint256 supply;
        uint256 price;
    }
}
合同源代码
文件 40 的 44:StructsLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../assetregistry/interfaces/IAssetRegistry.sol";
import "../token/interfaces/IERC20.sol";
import "../market/interfaces/IMarketCore.sol";
import "../pools/interfaces/IFeePool.sol";
import "../feepurse/interfaces/IFeePurse.sol";
import "../badge/interfaces/IBadge.sol";
import "../operator/interfaces/IOperator.sol";
import "../minerregisty/interfaces/IMinerRegistry.sol";

library StructsLib {
    struct UserRewardLog {
        uint256 phase;
        uint256 period;
    }

    struct IntSlotState {
        // Protocol fee for erc20 -> erc20 swap
        uint256 instantSwapFeeBp;
        // Protocol fee for erc20 -> synthetic swap
        uint256 synthSwapFeeBp;
        // Protocol fee for stable coin assets
        uint256 stableSwapFeeBp;
        // Fee paid in NFT/ERC20 swap
        uint256 instantNFTSwapFeeBp;
        // Fee paid when adding liquidity to a non-ERC20 market with a synthetic base
        uint256 fixedSynthSwapFee;
        // Fee paid when adding liquidity to a non-ERC20 market with an NFT base
        uint256 fixedNFTSwapFee;
        // Maximum open orders a liquidity provider can have open
        uint256 maxOpenOrders;
        // Maximum open disputes a liquidity provider can have open
        uint256 maxOpenDisputes;
    }

    struct State {
        IntSlotState ints;
        // protocol fee split base points
        // 0: inventor, 1: shared, 2: operator
        uint256[3] feeBPs;
        // The minimum number of seconds synthetic payments must be made within.
        uint256 minTimeToPay;
        // The number of seconds after order pay deadline allowed to pass before
        // an order can be cancelled.
        uint256 orderGraceDur;
        // The number of orders created.
        uint256 numOrders;
        // Number of seconds between
        uint256 feePoolTxInterval;
        // The protocol inventor.
        address inventor;
        // The AMM swap router address
        address ammSwapRouter;
        // The list of orders.
        Structs.Order[] orders;
        // Temporary list of reward log to be skipped
        UserRewardLog[] skipped;
        // Mapping of address to fees received from assets
        // user => asset => balance
        mapping(address => mapping(address => uint256)) feeBalance;
        // Index of sharable assets and their balance
        mapping(address => uint256) shareablesBalance;
        // Mapping of protocol fee balance
        // asset => balance
        mapping(address => uint256) inventorBalance;
        // Store protocol fee earned by operators
        // operator => asset => balance
        mapping(address => mapping(address => uint256)) operatorsBalance;
        // Index of address and the amount of liquidity provided to markets.
        mapping(address => mapping(address => Structs.Liquidity[])) liquidity;
        // Maps managers of liquidity
        // Maps liquidity owner => market => manager Address
        mapping(address => mapping(address => address)) liqManagers;
        // Maps address to the amount of reward earned
        mapping(address => uint256) rewards;
        // Maps asset address to the timestamp of their last success transfer to FeePool
        mapping(address => uint256) lastFeePoolTransfer;
    }

    struct UintSlotState2 {
        // Base point representing NFT fee discount
        uint256 nftDiscountBp;
        // The fee tier of ETH pool on the native AMM DEX
        uint256 ammEthPoolFee;
    }

    struct State2 {
        UintSlotState2 ints;
        // Dispute manager contract address
        address disputeManager;
        // NFT contract address
        address nft;
        // ETH ERC20 token on native AMM
        address ammEth;
        // Registry for asset information
        IAssetRegistry assetReg;
        // App token contract
        IERC20 token;
        // Market core contract
        IMarketCore marketCore;
        // Fee pool contract
        IFeePool feePool;
        // FeePurse contract
        IFeePurse feePurse;
        // Badge contract
        IBadge badge;
        // Operator manager contract
        IOperator operatorMgr;
        // Miner registry contract
        IMinerRegistry minerReg;
        // List of offers
        Structs.Offer[] offers;
        // Liquidity-level badges required of swappers.
        // market => provider => liquidity ID => [badgeID ... badgeID
        mapping(address => mapping(address => mapping(uint256 => string[2][]))) reqSwapBadges;
        // AMM pools where an asset can be swapped
        // input address => output (address, pool fee)
        mapping(address => AmmSwapPoolInfo) ammSwapPool;
        // Used to initialize an uint256 mapping.
        mapping(uint256 => bool) intIdx;
    }

    struct AmmSwapPoolInfo {
        address outAddr;
        uint256 poolFee;
    }

    struct SwapSlotAddresses {
        // marketId is the unique ID of the market.
        address marketId;
        // provider is the address of the base asset liquidity provider.
        address provider;
        // the recipient of the output asset
        address recipient;
    }

    struct SwapSlot {
        // liquidity ID
        uint256 lid;
        // amount is the quantity of base asset to be swapped.
        // Applies when quote asset is fungible
        uint256 amount;
        // maxPrice is the maximum price to pay for amount.
        // Applies when quote asset is fungible
        uint256 maxPrice;
        // ids is a list of non-fungible asset ids to be swapped for the base liquidity
        uint256[] ids;
        // Indicate that a swap was initiated by an offer with the given ID
        uint256 offerId;
        // The ID of the interface operator
        uint256 operator;
        // Slot for variables with address type
        SwapSlotAddresses addrs;
    }

    struct UpdateLiquiditySlot {
        address marketId;
        uint256 lid;
        bool deduct;
        bool pause;
        uint256 amount;
        uint256[] ids;
        uint256[] wantOrIds;
        uint256[] wantAndIds;
        uint256 price;
        uint256 minSwap;
        uint256 maxSwap;
        uint256 timeToPay;
        address target;
    }
}
合同源代码
文件 41 的 44:SwapFungibleLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "../market/MarketAuxLib.sol";
import "./OrderLib.sol";
import {IERC1155Strip} from "../common/StrippedInterfaces.sol";
import "./LiquidityLib.sol";
import "./FeeLib.sol";


library SwapFungibleLib {
    using SafeERC20 for IERC20;
    using AuxLib for uint256[];
    using AuxLib for address;
    using MarketAuxLib for Structs.Asset;

    event Swap(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        address releaser,
        uint256[6] numTypeInfo,
        bool byDispute,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );

    /// @dev Swap quote asset for a fungible base asset.
    /// Covers:
    /// ERC20/ERC20, ERC20/SYNTH, ERC20/NATIVE, ERC20/NFT, SYNTH/ERC20,
    /// SYNTH/NATIVE, SYNTH/SYNTH, SYNTH/NFT,
    /// NATIVE/ERC20, NATIVE/SYNTH, NATIVE/NATIVE, NATIVE/NFT
    /// @param state Storage state.
    /// @param state2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param mkt The market object for the market being swapped on.
    function swap(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt
    ) public {
        require(AuxLib.hasNonZeroLiquidity(state, sp.addrs.marketId, sp.addrs.provider, sp.lid), "OC: ZERO_LIQ");
        Structs.Liquidity storage liq = state.liquidity[sp.addrs.marketId][sp.addrs.provider][sp.lid];
        require(liq.target.isZero() || liq.target == msg.sender, "OC: NOT_TARGET");
        AuxLib.checkFungibleSwapParams(sp, mkt, liq);
        require(sp.operator == 0 || state2.operatorMgr.isOperator(sp.operator), "OC: UNKNOWN_OPERATOR");

        // Handle ERC20/ERC20, ERC20/NATIVE, NATIVE/ERC20, NATIVE/NATIVE
        if (
            (mkt.base.isERC20() && (mkt.quote.isERC20() || mkt.quote.isNative())) ||
            (mkt.base.isNative() && (mkt.quote.isERC20() || mkt.quote.isNative()))
        ) {
            return swapERC20OrNativeForERC20OrNative(state, state2, sp, mkt, liq);
        }

        // Handle ERC20/NFT, NATIVE/NFT
        if ((mkt.base.isERC20() || mkt.base.isNative()) && mkt.quote.isNFT()) {
            return swapNFTForERC20OrNative(state, state2, sp, mkt, liq);
        }

        // Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH, SYNTH/NFT, NATIVE/SYNTH, SYNTH/NATIVE
        OrderLib.createOrder(state, state2, sp, mkt, liq);
    }

    struct BoolSlot {
        bool sharable;
        bool isOperator;
    }

    /// @dev Handle ERC20/ERC20, ERC20/NATIVE, NATIVE/NATIVE, NATIVE/ERC20 swap (Instant Swap)
    /// @param s1 Storage state.
    /// @param s2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param mkt The market object for the market being swapped on.
    /// @param liq The liquidity object for the liquidity being swapped on.
    function swapERC20OrNativeForERC20OrNative(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public {
        // If swap was initiated via an offer, use price from offer instead of liquidity
        uint256 price = (sp.offerId > 0) ? sp.maxPrice : liq.price;

        // Compute cost of swapping for base. Ensure swapper's quote balance is sufficient.
        // uint baseDecimals = (mkt.base.isERC20()) ? IERC20(mkt.base.addr).decimals() : 18;
        uint256 cost = (sp.amount * price) / 10 ** ((mkt.base.isERC20()) ? IERC20(mkt.base.addr).decimals() : 18);
        require(!mkt.quote.isERC20() || IERC20(mkt.quote.addr).balanceOf(msg.sender) >= cost, "OC: LOW_QUOTE_BAL");
        require(!mkt.quote.isNative() || msg.value >= cost, "OC: LOW_QUOTE_BAL");

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                sp.lid,
                sp.amount,
                price,
                s2.nft,
                sp.addrs.provider,
                msg.sender
            )
        );

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fee to market creator
        s1.feeBalance[mkt.creator][mkt.base.addr] += feeInfo.mktCreatorFee;

        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));

        // Disburse fee to sharable pool if asset is sharable
        if (bools.sharable) {
            s1.shareablesBalance[mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[1]) / 10000;
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[2]) / 10000;
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s1.inventorBalance[mkt.base.addr] += (feeInfo.protoFee * totalFeeBps) / 10000;

        // Update liquidity
        liq.amount -= sp.amount;
        s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - sp.amount);

        // Transfer quote asset (swap cost) from swapper to liquidity provider.
        if (mkt.quote.isERC20()) {
            IERC20(mkt.quote.addr).safeTransferFrom(msg.sender, sp.addrs.provider, cost);
        } else if (mkt.quote.isNative()) {
            payable(sp.addrs.provider).transfer(cost);
        }

        // Transfer base asset (swap amount) to recipient
        if (mkt.base.isERC20()) {
            IERC20(mkt.base.addr).safeTransfer(sp.addrs.recipient.setOrDef(msg.sender), sp.amount - feeInfo.totalFees);
        } else if (mkt.base.isNative()) {
            payable(sp.addrs.recipient.setOrDef(msg.sender)).transfer(sp.amount - feeInfo.totalFees);
        }

        emit Swap(
            sp.addrs.marketId,
            sp.addrs.provider,
            msg.sender,
            sp.addrs.recipient,
            address(0),
            [0, sp.lid, sp.amount, price, sp.offerId, sp.operator],
            false,
            new uint256[](0),
            new uint256[](0)
        );
    }

    /// @dev Handle ERC20/NFT, NATIVE/NFT swap (Instant Swap)
    /// @param s1 Storage state.
    /// @param s2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param mkt The market object for the market being swapped on.
    /// @param liq The liquidity object for the liquidity being swapped on.
    function swapNFTForERC20OrNative(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public {
        // Find the IDs that match the wants request.
        // If swap was initiated via offer, use offer supplied ids.
        uint256[] memory matchIds = (sp.offerId > 0)
            ? sp.ids
            : LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
        require(matchIds.length > 0, "OC: NO_ID_MATCH");

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                sp.lid,
                sp.amount,
                0,
                s2.nft,
                sp.addrs.provider,
                msg.sender
            )
        );

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fee to market creator
        s1.feeBalance[mkt.creator][mkt.base.addr] += feeInfo.mktCreatorFee;

        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));

        // Disburse fee to sharable pool if asset is sharable
        if (bools.sharable) {
            s1.shareablesBalance[mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[1]) / 10000;
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.base.addr] += (feeInfo.protoFee * s1.feeBPs[2]) / 10000;
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s1.inventorBalance[mkt.base.addr] += (feeInfo.protoFee * totalFeeBps) / 10000;

        // Transfer base asset to recipient
        if (mkt.base.isERC20()) {
            IERC20(mkt.base.addr).safeTransfer(sp.addrs.recipient.setOrDef(msg.sender), liq.amount - feeInfo.totalFees);
        } else if (mkt.base.isNative()) {
            payable(sp.addrs.recipient.setOrDef(msg.sender)).transfer(liq.amount - feeInfo.totalFees);
        }

        // Transfer quote asset to liquidity provider
        for (uint256 i = 0; i < matchIds.length; i++) {
            if (matchIds[i] == 0) continue;
            if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(msg.sender, sp.addrs.provider, matchIds[i]);
            else IERC1155Strip(mkt.quote.addr).safeTransferFrom(msg.sender, sp.addrs.provider, matchIds[i], 1, "");
        }

        // Update liquidity
        s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.amount);
        liq.amount = 0;

        emit Swap(
            sp.addrs.marketId,
            sp.addrs.provider,
            msg.sender,
            sp.addrs.recipient,
            address(0),
            [0, sp.lid, sp.amount, liq.price, sp.offerId, sp.operator],
            false,
            new uint256[](0),
            matchIds
        );
    }
}
合同源代码
文件 42 的 44:SwapLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "../market/MarketAuxLib.sol";
import "../common/StrippedInterfaces.sol";
import "./OrderLib.sol";
import "./OrderReleaseLib.sol";
import "./SwapFungibleLib.sol";
import "./SwapNonFungibleLib.sol";

library SwapLib {
    using MarketAuxLib for Structs.Asset;

    /// @dev Swap quote asset for the base asset.
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param sp The swap parameters
    function swapQuoteForBase(StructsLib.State storage state, StructsLib.State2 storage state2, StructsLib.SwapSlot memory sp) public {
        require(state2.marketCore.isPutPermitted(sp.addrs.marketId, sp.addrs.provider));
        require(state2.marketCore.isSwapPermitted(sp.addrs.marketId, msg.sender));
        Structs.Market memory market = state2.marketCore.getMarket(sp.addrs.marketId);
        require(!market.perms.offerOnly, "OC: OFFER_ONLY");

        checkMarketSwapBadges(state2, sp.addrs.marketId);
        checkProviderSwapBadges(state2, sp.addrs.marketId, sp.lid, sp.addrs.provider);
        if (market.base.isFungible()) SwapFungibleLib.swap(state, state2, sp, market);
        else SwapNonFungibleLib.swap(state, state2, sp, market);
    }

    /// @dev Create order for ERC20/SYNTH, SYNTH/ERC20, SYNTH/SYNTH & SYNTH/NFT
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param sp The swap parameters
    /// @param market The market object
    /// @param liq The liquidity object
    function createOrder(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory market,
        Structs.Liquidity storage liq
    ) public {
        OrderLib.createOrder(state, state2, sp, market, liq);
    }

    /// @dev Release ERC20 asset.
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param owner The contract owner
    /// @param oid The order ID.
    /// @param byDispute Indicates that the method is called by the dispute manager.
    function releaseOrder(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address owner,
        uint256 oid,
        bool byDispute
    ) public {
        OrderReleaseLib.releaseOrder(state, state2, owner, oid, byDispute, false);
    }

    /// @notice Release an order using a market manager's signature
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param owner The contract owner
    /// @param oid The order ID
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function releaseOrderByManagerSig(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address owner,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) public {
        OrderReleaseLib.releaseOrderByManagerSig(state, state2, owner, oid, expireAt, signature);
    }

    /// @dev Cancel an inactive order. An inactive order is one where:
    /// The order has not been marked as paid, released or disputed
    /// and has reached its cancel time.
    /// @param state State object
    /// @param state2 State2 object
    /// @param owner The contract owner
    /// @param id is the unique order ID
    /// @param byDispute Indicates that the method is called by the dispute manager.
    function cancelOrder(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address owner,
        uint256 id,
        bool byDispute
    ) public {
        OrderLib.cancelOrder(state, state2, owner, id, byDispute, false);
    }

    /// @notice Cancel an order using a market manager's signature
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param owner The contract owner
    /// @param oid The order ID
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function cancelOrderByManagerSig(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        address owner,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) external {
        OrderLib.cancelOrderByManagerSig(state, state2, owner, oid, expireAt, signature);
    }

    /// @dev Mark a swap order as paid.
    /// Order must not have been released or cancelled.
    /// When the base asset is on-chain (ERC20 or NFT), the sender must be the taker.
    /// When the base asset is synthetic, the sender must be the provider.
    /// @param state State object
    /// @param marketCore The MarketCore contract address
    /// @param id The order ID.
    function markOrderAsPaid(StructsLib.State storage state, IMarketCore marketCore, uint256 id, bool byManager) public {
        OrderLib.markOrderAsPaid(state, marketCore, id, byManager);
    }

    /// @notice Mark an order as paid using a market manager's signature
    /// @param state The State object
    /// @param state2 The State2 object
    /// @param oid The order ID
    /// @param expireAt The expire time of the signature
    /// @param signature The signature created by the manager
    function markOrderAsPaidByManagerSig(
        StructsLib.State storage state,
        StructsLib.State2 storage state2,
        uint256 oid,
        uint256 expireAt,
        bytes calldata signature
    ) external {
        OrderLib.markOrderAsPaidByManagerSig(state, state2, oid, expireAt, signature);
    }

    /// @dev Marks an order as `disputed`
    /// @param state State object
    /// @param marketCore The MarketCore contract address
    /// @param id The order ID or index.
    function markAsDisputed(StructsLib.State storage state, IMarketCore marketCore, uint256 id) public {
        OrderLib.markAsDisputed(state, marketCore, id);
    }

    /// @dev Check if sender has the required swap badges set by the market creator
    function checkMarketSwapBadges(StructsLib.State2 storage s2, address marketId) internal view {
        (, string[2][] memory reqSwapBadges) = s2.marketCore.getReqBadges(marketId);
        if (reqSwapBadges.length > 0) {
            uint256 len = reqSwapBadges.length;
            for (uint256 i = 0; i < len; i++) {
                require(s2.badge.hasBadge(msg.sender, reqSwapBadges[i][0], reqSwapBadges[i][1]), "OC: MKT_BADGE_REQ");
            }
        }
    }

    /// @dev Check if sender has the required swap badges set by the liquidity provider
    /// @param s2 A state object
    /// @param marketId The market ID
    /// @param lid The liquidity ID
    /// @param provider The provider address
    function checkProviderSwapBadges(StructsLib.State2 storage s2, address marketId, uint256 lid, address provider) internal view {
        string[2][] memory reqSwapBadges = s2.reqSwapBadges[marketId][provider][lid];
        if (reqSwapBadges.length > 0) {
            uint256 len = reqSwapBadges.length;
            for (uint256 i = 0; i < len; i++) {
                require(s2.badge.hasBadge(msg.sender, reqSwapBadges[i][0], reqSwapBadges[i][1]), "OC: LP_BADGE_REQ");
            }
        }
    }
}
合同源代码
文件 43 的 44:SwapNonFungibleLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

import "../common/Structs.sol";
import "../market/interfaces/IMarketCore.sol";
import "../common/SafeERC20.sol";
import "./AuxLib.sol";
import "./StructsLib.sol";
import "../market/MarketAuxLib.sol";
import "./OrderLib.sol";
import {IERC1155Strip} from "../common/StrippedInterfaces.sol";
import "./FeeLib.sol";

library SwapNonFungibleLib {
    using SafeERC20 for IERC20;
    using MarketAuxLib for Structs.Asset;
    using AuxLib for address;

    event Swap(
        address indexed mid,
        address indexed provider,
        address indexed swapper,
        address recipient,
        address releaser,
        uint256[6] numTypeInfo,
        bool byDispute,
        uint256[] idsFromProvider,
        uint256[] idsFromTaker
    );

    /// @dev Swap quote asset for a non-fungible base asset.
    /// Covers NFT/ERC20, NFT/NATIVE, NFT/SYNTH, NFT/NFT
    /// @param s1 Storage state.
    /// @param s2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param market The market object for the market being swapped on.
    function swap(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory market
    ) public {
        require(AuxLib.hasNonZeroLiquidity(s1, sp.addrs.marketId, sp.addrs.provider, sp.lid), "OC: ZERO_LIQ");
        Structs.Liquidity storage liq = s1.liquidity[sp.addrs.marketId][sp.addrs.provider][sp.lid];
        require(liq.target.isZero() || liq.target == msg.sender, "OC: NOT_TARGET");
        AuxLib.checkNonFungibleSwapParams(sp, market, liq);
        require(sp.operator == 0 || s2.operatorMgr.isOperator(sp.operator), "OC: UNKNOWN_OPERATOR");

        // If quote asset is ERC20 or NATIVE
        if (market.quote.isERC20() || market.quote.isNative()) {
            return swapERC20OrNativeForNFT(s1, s2, sp, market, liq);
        }

        // If quote asset is NFT
        if (market.quote.isNFT()) {
            return swapNFTForNFT(s1, s2, sp, market, liq);
        }

        // Create a swap order (NFT/SYNTH)
        OrderLib.createOrder(s1, s2, sp, market, liq);
    }

    struct BoolSlot {
        bool sharable;
        bool isOperator;
    }

    /// @dev Handle NFT/NFT swap (Instant)
    /// @param s1 Storage state.
    /// @param s2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param mkt The market object for the market being swapped on.
    /// @param liq The liquidity object.
    function swapNFTForNFT(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public {
        address sdr = msg.sender;

        // Find the IDs that match the wants request
        // If swap was initiated via offer, use offer supplied ids.
        uint256[] memory matchIds = (sp.offerId > 0)
            ? sp.ids
            : LiquidityLib.findMatchInWantIds(sp.ids, s2.intIdx, liq.wantOrIds, liq.wantAndIds);
        require(matchIds.length > 0, "OC: NO_ID_MATCH");

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                sp.lid,
                sp.amount,
                0,
                s2.nft,
                sp.addrs.provider,
                msg.sender
            )
        );

        // Unlock fee from fee purse
        s2.feePurse.unlock(sp.addrs.provider, liq.protoFeePurseId);
        s2.feePurse.unlock(sp.addrs.provider, liq.mktCreatorFeePurseId);
        liq.protoFeePurseId = 0;
        liq.mktCreatorFeePurseId = 0;

        // Transfer fee to market creator
        s2.feePurse.transferFrom(sp.addrs.provider, mkt.creator, feeInfo.mktCreatorFee);

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.base.addr), s2.operatorMgr.isOperator(sp.operator));

        // Disburse fee to sharable pool if asset is sharable
        if (bools.sharable) {
            s2.feePurse.transferFrom(sp.addrs.provider, address(this), (feeInfo.protoFee * s1.feeBPs[1]) / 10000);
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            address opAddr = s2.operatorMgr.getOperator(sp.operator);
            s2.feePurse.transferFrom(sp.addrs.provider, opAddr, (feeInfo.protoFee * s1.feeBPs[2]) / 10000);
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s2.feePurse.transferFrom(sp.addrs.provider, s1.inventor, (feeInfo.protoFee * totalFeeBps) / 10000);

        // Transfer quote asset to liquidity provider
        for (uint256 i = 0; i < matchIds.length; i++) {
            if (matchIds[i] == 0) continue;
            if (mkt.quote.isERC721()) IERC721Strip(mkt.quote.addr).transferFrom(sdr, sp.addrs.provider, matchIds[i]);
            else IERC1155Strip(mkt.quote.addr).safeTransferFrom(sdr, sp.addrs.provider, matchIds[i], 1, "");
        }

        // Transfer the base asset to recipient
        for (uint256 i = 0; i < liq.ids.length; i++)
            if (mkt.base.isERC721()) IERC721Strip(mkt.base.addr).transferFrom(address(this), sp.addrs.recipient.setOrDef(sdr), liq.ids[i]);
            else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), sp.addrs.recipient.setOrDef(sdr), liq.ids[i], 1, "");

        s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.ids.length);

        emit Swap(
            sp.addrs.marketId,
            sp.addrs.provider,
            msg.sender,
            sp.addrs.recipient,
            address(0),
            [0, sp.lid, sp.amount, 0, sp.offerId, sp.operator],
            false,
            liq.ids,
            matchIds
        );

        delete liq.ids;
    }

    /// @dev Handle NFT/ERC20, NFT/NATIVE swap (Instant)
    /// @param s1 Storage state.
    /// @param s2 Storage state 2.
    /// @param sp The parameters needed to swap.
    /// @param mkt The market object for the market being swapped on.
    /// @param liq The liquidity object.
    function swapERC20OrNativeForNFT(
        StructsLib.State storage s1,
        StructsLib.State2 storage s2,
        StructsLib.SwapSlot memory sp,
        Structs.Market memory mkt,
        Structs.Liquidity storage liq
    ) public {
        uint256 price = (sp.offerId > 0) ? sp.maxPrice : liq.price;

        // Compute fee
        FeeInfo memory feeInfo = FeeLib.computeFee(
            ComputeFeeParams(
                mkt,
                liq,
                s2.assetReg,
                s2.feePurse,
                s1.ints.synthSwapFeeBp,
                s1.ints.instantNFTSwapFeeBp,
                s1.ints.instantSwapFeeBp,
                s1.ints.stableSwapFeeBp,
                s2.ints.nftDiscountBp,
                sp.lid,
                sp.amount,
                price,
                s2.nft,
                sp.addrs.provider,
                msg.sender
            )
        );

        // Dispurse fee to market creator
        s1.feeBalance[mkt.creator][mkt.quote.addr] += feeInfo.mktCreatorFee;

        BoolSlot memory bools = BoolSlot(AuxLib.isShareable(s2, mkt.quote.addr), s2.operatorMgr.isOperator(sp.operator));

        uint256 totalFeeBps = s1.feeBPs[0] + s1.feeBPs[1] + s1.feeBPs[2];

        // Disburse fee to sharable pool if asset is sharable
        if (bools.sharable) {
            s1.shareablesBalance[mkt.quote.addr] += (feeInfo.protoFee * s1.feeBPs[1]) / 10000;
            totalFeeBps -= s1.feeBPs[1];
        }

        // Disburse fee to operator if operator is set
        if (bools.isOperator) {
            s1.operatorsBalance[s2.operatorMgr.getOperator(sp.operator)][mkt.quote.addr] += (feeInfo.protoFee * s1.feeBPs[2]) / 10000;
            totalFeeBps -= s1.feeBPs[2];
        }

        // Disburse fee to inventor
        s1.inventorBalance[mkt.quote.addr] += (feeInfo.protoFee * totalFeeBps) / 10000;

        // Transfer quote asset to contract and to the liquidity provider
        if (mkt.quote.isERC20()) {
            IERC20(mkt.quote.addr).safeTransferFrom(msg.sender, address(this), price);
            IERC20(mkt.quote.addr).safeTransfer(sp.addrs.provider, price - feeInfo.totalFees);
        } else if (mkt.quote.isNative()) {
            require(msg.value >= price, "OC: LOW_DEPOSIT");
            payable(sp.addrs.provider).transfer(price - feeInfo.totalFees);
        }

        // Transfer base assets to recipient
        for (uint256 i = 0; i < liq.ids.length; i++)
            if (mkt.base.isERC721())
                IERC721Strip(mkt.base.addr).transferFrom(address(this), sp.addrs.recipient.setOrDef(msg.sender), liq.ids[i]);
            else IERC1155Strip(mkt.base.addr).safeTransferFrom(address(this), sp.addrs.recipient.setOrDef(msg.sender), liq.ids[i], 1, "");

        s2.marketCore.setCounters(sp.addrs.marketId, false, false, true, mkt.numLiquidity - liq.ids.length);

        emit Swap(
            sp.addrs.marketId,
            sp.addrs.provider,
            msg.sender,
            sp.addrs.recipient,
            address(0),
            [0, sp.lid, sp.amount, price, sp.offerId, sp.operator],
            false,
            liq.ids,
            new uint256[](0)
        );

        delete liq.ids;
    }
}
合同源代码
文件 44 的 44:WithdrawLib.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import "./StructsLib.sol";
import "../token/interfaces/IERC20.sol";
import "../common/SafeERC20.sol";
import "../pools/interfaces/IFeePool.sol";
import "../feepurse/interfaces/IFeePurse.sol";
import "../market/interfaces/IMarketCore.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";

library WithdrawLib {
    using SafeERC20 for IERC20;

    event TransferredToFeePool(address indexed token, uint256 amount, address convAsset, uint256 convAmount);
    event TransferInventorBalance(address owner, address token, uint256 amount);
    event TransferOperatorBalance(address owner, address token, uint256 amount);

    /// @dev Withdraw fees allocated to the protocol inventor
    /// @param state State object
    /// @param asset The address of the asset the fee is denominated in.
    function withdrawInventorBalance(StructsLib.State storage state, StructsLib.State2 storage state2, address asset) public {
        uint256 bal = state.inventorBalance[asset];
        state.inventorBalance[asset] = 0;

        if (IMarketCore(state2.marketCore).getNativeAssetAddr() == asset) {
            payable(state.inventor).transfer(bal);
        } else {
            IERC20(asset).transfer(state.inventor, bal);
        }

        emit TransferInventorBalance(state.inventor, asset, bal);

        bal = state2.feePurse.balanceOf(state.inventor);
        if (bal > 0) {
            state2.feePurse.withdrawFrom(bal, state.inventor, state.inventor);
            emit TransferInventorBalance(state.inventor, asset, bal);
        }
    }

    /// @dev Withdraw fees allocated to operator (include fees in FeePurse)
    /// @param state State object
    /// @param state2 State object
    /// @param token The address of the token the fee is denominated in.
    function withdrawOperatorBalance(StructsLib.State storage state, StructsLib.State2 storage state2, address token) public {
        uint256 bal = state.operatorsBalance[msg.sender][token];

        state.operatorsBalance[msg.sender][token] = 0;

        if (IMarketCore(state2.marketCore).getNativeAssetAddr() == token) {
            payable(msg.sender).transfer(bal);
        } else {
            IERC20(token).transfer(msg.sender, bal);
        }

        emit TransferOperatorBalance(msg.sender, token, bal);

        bal = state2.feePurse.balanceOf(msg.sender);
        if (bal > 0) {
            state2.feePurse.withdrawFrom(bal, msg.sender, msg.sender);
            emit TransferOperatorBalance(msg.sender, token, bal);
        }
    }

    /// @dev Transfer fees earned on shareable assets to the fee reward contract (include fees in FeePurse)
    /// @param state State object
    /// @param state2 State object
    /// @param token The address of a shareable token
    function transferToFeePool(StructsLib.State storage state, StructsLib.State2 storage state2, address token) public {
        uint256 lastTx = state.lastFeePoolTransfer[token];
        require(lastTx == 0 || block.timestamp - lastTx > state.feePoolTxInterval, "OC: NOT_READY");

        uint256 bal = state.shareablesBalance[token];
        if (bal > 0) {
            // Clear the balance and set last transfer time
            state.shareablesBalance[token] = 0;
            state.lastFeePoolTransfer[token] = block.timestamp;

            // Convert balance to another asset (e.g. ETH, USDC)
            ISwapRouter swapRouter = ISwapRouter(state.ammSwapRouter);

            // Determine the output asset and pool
            address tokenOut = state2.ammEth;
            uint256 poolFee = state2.ints.ammEthPoolFee;
            if (state2.ammSwapPool[token].outAddr != address(0)) {
                tokenOut = state2.ammSwapPool[token].outAddr;
                poolFee = state2.ammSwapPool[token].poolFee;
            }

            require(token != tokenOut, "OC: SAME_ASSET");

            // Execute swap
            IERC20(token).safeIncreaseAllowance(address(swapRouter), bal);
            ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
                tokenIn: token,
                tokenOut: tokenOut,
                fee: uint24(poolFee),
                recipient: address(state2.feePool),
                deadline: block.timestamp,
                amountIn: bal,
                amountOutMinimum: 0,
                sqrtPriceLimitX96: 0
            });

            uint256 amountOut;
            if (token == IMarketCore(state2.marketCore).getNativeAssetAddr()) {
                amountOut = swapRouter.exactInputSingle{value: bal}(params);
            } else {
                amountOut = swapRouter.exactInputSingle(params);
            }

            // Notify fee pool of new deposit
            state2.feePool.notifyFeeDeposit(tokenOut, amountOut);
            emit TransferredToFeePool(token, bal, tokenOut, amountOut);
        }

        // Transfer shareable fees held in feepurse to feepool
        bal = state2.feePurse.balanceOf(address(this));
        if (bal > 0) {
            state2.feePurse.withdraw(bal, address(state2.feePool));
            state2.feePool.notifyFeeDeposit(address(state2.feePurse.getFeeToken()), bal);
            emit TransferredToFeePool(address(state2.feePurse.getFeeToken()), bal, address(0), 0);
        }
    }
}
设置
{
  "compilationTarget": {
    "contracts/core/Core.sol": "Core"
  },
  "evmVersion": "london",
  "libraries": {
    "contracts/core/AuxLib.sol:AuxLib": "0xe9914155cbbbd33c9db88a7134ff4aac74162a28",
    "contracts/core/FeeLib.sol:FeeLib": "0x6626dc82f9614401bfe4093bffbadf781322e203",
    "contracts/core/FungibleLiquidityLib.sol:FungibleLiquidityLib": "0xb6079afb68b4efdb30bf35aee2335ea80a191d9a",
    "contracts/core/LiquidityLib.sol:LiquidityLib": "0x85a2eef7b8cfcb6c05b9991a6063d61f1b66f122",
    "contracts/core/NonFungibleLiquidityLib.sol:NonFungibleLiquidityLib": "0x4f30ea55ada00a9ea2434962794b347f62281487",
    "contracts/core/OfferLib.sol:OfferLib": "0x4e911c672c6744d37b462b2a8534e6ed4253ac0c",
    "contracts/core/SwapLib.sol:SwapLib": "0xeb4edc8d6efe86bf39b7f493abddbd2aa9b3c236",
    "contracts/core/WithdrawLib.sol:WithdrawLib": "0x7a22af56d43a9cc78d4cbe3ae1c764377de73a9a"
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"_marketCore","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_minTimeToPay","type":"uint256"},{"internalType":"uint256","name":"_orderGraceDur","type":"uint256"},{"internalType":"uint256","name":"_maxOpenOrders","type":"uint256"},{"internalType":"uint256","name":"_maxOpenDisputes","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"address","name":"finalizer","type":"address"},{"indexed":false,"internalType":"uint256","name":"orderId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"action","type":"uint256"}],"name":"FinalizedByManagerSig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bp","type":"uint256"}],"name":"NFTDiscountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"marketId","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lid","type":"uint256"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"expireDuration","type":"uint256"}],"name":"NewOffer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":true,"internalType":"address","name":"acceptor","type":"address"},{"indexed":false,"internalType":"uint256","name":"executeAt","type":"uint256"}],"name":"OfferAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":true,"internalType":"address","name":"canceller","type":"address"}],"name":"OfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":true,"internalType":"address","name":"executor","type":"address"}],"name":"OfferExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":true,"internalType":"address","name":"unacceptor","type":"address"}],"name":"OfferUnaccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"offerId","type":"uint256"},{"indexed":true,"internalType":"address","name":"offerer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"execDelayDur","type":"uint256"}],"name":"OfferUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderID","type":"uint256"},{"indexed":true,"internalType":"address","name":"canceller","type":"address"},{"indexed":true,"internalType":"bool","name":"byDispute","type":"bool"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inventor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shared","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"operator","type":"uint256"}],"name":"ProtocolFeeSplitUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"instantSwapFeeBp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"synthSwapFeeBp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stableSwapFeeBp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fixedSynthSwapFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fixedNFTSwapFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"instantNFTSwapFeeBp","type":"uint256"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"mid","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"swapper","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"releaser","type":"address"},{"indexed":false,"internalType":"uint256[6]","name":"numTypeInfo","type":"uint256[6]"},{"indexed":false,"internalType":"bool","name":"byDispute","type":"bool"},{"indexed":false,"internalType":"uint256[]","name":"idsFromProvider","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"idsFromTaker","type":"uint256[]"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"mid","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"swapper","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256[7]","name":"numTypeInfo","type":"uint256[7]"},{"indexed":false,"internalType":"uint256[]","name":"idsFromProvider","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"idsFromTaker","type":"uint256[]"}],"name":"SwapOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderID","type":"uint256"},{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"uint256","name":"disputeDelayDur","type":"uint256"}],"name":"SwapOrderPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"marketId","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lid","type":"uint256"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"TogglePause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferInventorBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferOperatorBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"convAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"convAmount","type":"uint256"}],"name":"TransferredToFeePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapRouter","type":"address"},{"indexed":false,"internalType":"address","name":"ethErc20Addr","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolFee","type":"uint256"}],"name":"UpdatedAMMInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"input","type":"address"},{"indexed":false,"internalType":"address","name":"output","type":"address"},{"indexed":false,"internalType":"uint256","name":"poolFee","type":"uint256"}],"name":"UpdatedAMMPoolInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedAssetRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedBadge","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedDisputeManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedFeePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"interval","type":"uint256"}],"name":"UpdatedFeePoolConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedFeePurse","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"marketId","type":"address"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"deduct","type":"bool"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"},{"indexed":false,"internalType":"uint256","name":"liq","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"wantOrIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"wantAndIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"minSwap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxSwap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeToPay","type":"uint256"}],"name":"UpdatedFungibleLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedInventor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"marketId","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"address","name":"oldManager","type":"address"}],"name":"UpdatedLiquidityManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"market","type":"address"},{"indexed":false,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"liq","type":"uint256"},{"indexed":false,"internalType":"string[2][]","name":"badges","type":"string[2][]"}],"name":"UpdatedLiquiditySwapBadges","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedMarketCore","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"UpdatedMaxOpenDisputes","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"UpdatedMaxOpenOrders","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"UpdatedMinTimeToPay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedMinerRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"marketId","type":"address"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bool","name":"deduct","type":"bool"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"},{"indexed":false,"internalType":"uint256","name":"lid","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"wantOrIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"wantAndIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"timeToPay","type":"uint256"}],"name":"UpdatedNonFungibleLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"UpdatedOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"UpdatedOrderGraceDur","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"marketId","type":"address"},{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"uint256","name":"lid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"UpdatedPrice","type":"event"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"uint256","name":"delay","type":"uint256"}],"name":"acceptOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"addr","type":"address"}],"name":"canCreateDispute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"cancelOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelOrderByDispute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"cancelOrderByManagerSig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"mid","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"orderAmt","type":"uint256"},{"internalType":"uint256","name":"orderPrice","type":"uint256"}],"name":"computeSwapFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"interval","type":"uint256"}],"name":"configureFeePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"operator","type":"uint256"}],"name":"createOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"uint256","name":"txDeadline","type":"uint256"}],"name":"executeOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_asset","type":"address"}],"name":"feeBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeBPs","outputs":[{"internalType":"uint256[3]","name":"","type":"uint256[3]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_mid","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_lid","type":"uint256"}],"name":"getLiquidity","outputs":[{"components":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"minSwap","type":"uint256"},{"internalType":"uint256","name":"maxSwap","type":"uint256"},{"internalType":"uint256","name":"timeToPay","type":"uint256"},{"internalType":"uint256","name":"openOrders","type":"uint256"},{"internalType":"uint256","name":"openDisputes","type":"uint256"},{"internalType":"uint256","name":"protoFeePurseId","type":"uint256"},{"internalType":"uint256","name":"mktCreatorFeePurseId","type":"uint256"},{"internalType":"uint256","name":"protoFeeRate","type":"uint256"},{"internalType":"uint256","name":"mktCreatorFeeRate","type":"uint256"},{"internalType":"uint256[]","name":"wantOrIds","type":"uint256[]"},{"internalType":"uint256[]","name":"wantAndIds","type":"uint256[]"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"address","name":"target","type":"address"}],"internalType":"struct Structs.Liquidity","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"}],"name":"getLiquidityManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"id","type":"address"}],"name":"getMarket","outputs":[{"components":[{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"uint256","name":"numOrders","type":"uint256"},{"internalType":"uint256","name":"numDisputes","type":"uint256"},{"internalType":"uint256","name":"numLiquidity","type":"uint256"},{"internalType":"uint256","name":"commission","type":"uint256"},{"internalType":"uint256","name":"poolId","type":"uint256"},{"components":[{"internalType":"uint256","name":"disputeDelayDur","type":"uint256"}],"internalType":"struct Structs.MarketDur","name":"dur","type":"tuple"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"enum Structs.AssetType","name":"assetType","type":"uint8"}],"internalType":"struct Structs.Asset","name":"base","type":"tuple"},{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"enum Structs.AssetType","name":"assetType","type":"uint8"}],"internalType":"struct Structs.Asset","name":"quote","type":"tuple"},{"components":[{"internalType":"bool","name":"put","type":"bool"},{"internalType":"bool","name":"swap","type":"bool"},{"internalType":"bool","name":"offerOnly","type":"bool"},{"internalType":"bool","name":"allowZeroOfferPrice","type":"bool"}],"internalType":"struct Structs.MarketPerms","name":"perms","type":"tuple"}],"internalType":"struct Structs.Market","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getOffer","outputs":[{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"liq","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"execAt","type":"uint256"},{"internalType":"uint256","name":"operator","type":"uint256"},{"internalType":"bool","name":"accepted","type":"bool"},{"internalType":"bool","name":"cancelled","type":"bool"},{"internalType":"bool","name":"executed","type":"bool"}],"internalType":"struct Structs.Offer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getOrder","outputs":[{"components":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"finalizer","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"uint256","name":"payDeadline","type":"uint256"},{"internalType":"uint256","name":"cancelAt","type":"uint256"},{"internalType":"uint256","name":"disputeFrom","type":"uint256"},{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"uint256","name":"operator","type":"uint256"},{"internalType":"uint256[]","name":"takerIds","type":"uint256[]"},{"internalType":"uint256[]","name":"providerIds","type":"uint256[]"},{"internalType":"bool","name":"paid","type":"bool"},{"internalType":"bool","name":"released","type":"bool"},{"internalType":"bool","name":"disputed","type":"bool"},{"internalType":"bool","name":"cancelled","type":"bool"}],"internalType":"struct Structs.Order","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"address","name":"addr","type":"address"}],"name":"getReqSwapBadges","outputs":[{"internalType":"string[2][]","name":"","type":"string[2][]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_mid","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_lid","type":"uint256"}],"name":"hasLiquidity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"hasOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"inventorBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isVerified","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"markAsDisputed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"markOrderAsPaid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"markOrderAsPaidByManagerSig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"operatorBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"releaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"releaseOrderByDispute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"releaseOrderByManagerSig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"swapRouter","type":"address"},{"internalType":"address","name":"ethErc20Addr","type":"address"},{"internalType":"uint256","name":"poolFee","type":"uint256"}],"name":"setAmmInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"input","type":"address"},{"internalType":"address","name":"out","type":"address"},{"internalType":"uint256","name":"poolFee","type":"uint256"}],"name":"setAmmPoolInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setAssetRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"val","type":"address"}],"name":"setBadgeMgr","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bp","type":"uint256"}],"name":"setDiscountForNFTHolder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setDisputeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setFeePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"val","type":"address"}],"name":"setFeePurse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setInventor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"manager","type":"address"}],"name":"setLiquidityManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setMarketCore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMaxOpenDisputes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMaxOpenOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setMinTimeToPay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setMinerRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"setOrderGraceDur","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_instantSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"_synthSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"_stableSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"_fixedSynthSwapFee","type":"uint256"},{"internalType":"uint256","name":"_fixedNFTSwapFee","type":"uint256"},{"internalType":"uint256","name":"_instantNFTSwapFeeBp","type":"uint256"}],"name":"setProtoFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inventorBP","type":"uint256"},{"internalType":"uint256","name":"sharedBP","type":"uint256"},{"internalType":"uint256","name":"operatorBP","type":"uint256"}],"name":"setProtoFeeSplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"string[2][]","name":"badgeIds","type":"string[2][]"}],"name":"setSwapBadgeForLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"shareablesBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"state","outputs":[{"components":[{"internalType":"uint256","name":"instantSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"synthSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"stableSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"instantNFTSwapFeeBp","type":"uint256"},{"internalType":"uint256","name":"fixedSynthSwapFee","type":"uint256"},{"internalType":"uint256","name":"fixedNFTSwapFee","type":"uint256"},{"internalType":"uint256","name":"maxOpenOrders","type":"uint256"},{"internalType":"uint256","name":"maxOpenDisputes","type":"uint256"}],"internalType":"struct StructsLib.IntSlotState","name":"ints","type":"tuple"},{"internalType":"uint256","name":"minTimeToPay","type":"uint256"},{"internalType":"uint256","name":"orderGraceDur","type":"uint256"},{"internalType":"uint256","name":"numOrders","type":"uint256"},{"internalType":"uint256","name":"feePoolTxInterval","type":"uint256"},{"internalType":"address","name":"inventor","type":"address"},{"internalType":"address","name":"ammSwapRouter","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"state2","outputs":[{"components":[{"internalType":"uint256","name":"nftDiscountBp","type":"uint256"},{"internalType":"uint256","name":"ammEthPoolFee","type":"uint256"}],"internalType":"struct StructsLib.UintSlotState2","name":"ints","type":"tuple"},{"internalType":"address","name":"disputeManager","type":"address"},{"internalType":"address","name":"nft","type":"address"},{"internalType":"address","name":"ammEth","type":"address"},{"internalType":"contract IAssetRegistry","name":"assetReg","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IMarketCore","name":"marketCore","type":"address"},{"internalType":"contract IFeePool","name":"feePool","type":"address"},{"internalType":"contract IFeePurse","name":"feePurse","type":"address"},{"internalType":"contract IBadge","name":"badge","type":"address"},{"internalType":"contract IOperator","name":"operatorMgr","type":"address"},{"internalType":"contract IMinerRegistry","name":"minerReg","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"txDeadline","type":"uint256"},{"internalType":"uint256","name":"operator","type":"uint256"}],"name":"swapQuoteForBase","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"uint256","name":"expireAt","type":"uint256"}],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"transferToFeePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"unacceptOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"bool","name":"deduct","type":"bool"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256[2]","name":"amountAndPrice","type":"uint256[2]"},{"internalType":"uint256[]","name":"wantOrIds","type":"uint256[]"},{"internalType":"uint256[]","name":"wantAndIds","type":"uint256[]"},{"internalType":"uint256[5]","name":"limits","type":"uint256[5]"}],"name":"updateFungibleLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_marketId","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_lid","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"_expireAt","type":"uint256"}],"name":"updateLiquidityPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"bool","name":"deduct","type":"bool"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256[]","name":"wantOrIds","type":"uint256[]"},{"internalType":"uint256[]","name":"wantAndIds","type":"uint256[]"},{"internalType":"uint256[3]","name":"limits","type":"uint256[3]"}],"name":"updateNonFungibleLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"updateOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketId","type":"address"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"uint256","name":"lid","type":"uint256"},{"internalType":"bool","name":"incr","type":"bool"}],"name":"updateOpenDisputesCount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"withdrawFeeBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"withdrawInventorBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"withdrawOperatorBalance","outputs":[],"stateMutability":"nonpayable","type":"function"}]