POL Price: $0.09381 (-0.99%)
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 POL

POL Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
0xdd61b51fca1cda2c1cc19bde7ef81980522e6de6d45dcb554e48b0b94d30bc7c -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x27ae827a2bed1b0c66fa11604cb2a450bf749c4f1996f9e9a059fe72908d7909 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x18dc02fbb77a75cd51071bff4d0fb7e5d254779ad00d90fe2c69670e88c6125b -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xce4da38679dc1042c5f49d72fc997eb9dfe758c9f7f3d18e07662d3c112b5b22 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x6a698f2a8cef1060b70108f5e06145d07b59869bc184243f6532315b8daf70b6 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x24e6df0cda334131d54dba8dc46b5312c779102c56dd62c935f88ad89a9910e9 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x0bd8e9f87b6171e24761f68ac64ceee8211a46a1403c64940955fed20c976573 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x2c260b4a72955db4d21a5e3443c9a52c08a124f42c1ea332f58ebb75be194543 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x1a93985f6cb5147b9380c4c6d756303d065944d9de34579542f9d2e636377f28 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x71631f77365213691d012045c20191855d9e5b9081f6c720ac4d6daf1520ae87 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x3e8aa21594795e30d215b7c001c1ac2d3338b6e10635fd0564e2c7fd65cb191f -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x3d6710bcb6bb68de0bc6c4b87438f370193e51fa219dd21784b47825dcbc8e1c -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x2b4051d9bb61709182be71ef4659dd99c730410740c9f341a51b763cfb7cb47d -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x2008739559dfcbacd02d2a37ab91669227e5aae6105ad15ca5fc4287892f2f66 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xe18e51a3ca3635e0a7824c7af422fbb24eeb94b8cf7637c9c745f8992501e5da -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x60c0caa770a5ca8d9c618b18a23a765589c9a49fbff5ea2dbc936e206f0e98fb -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x5bc0b83139d1feb7aeaf11fcb9a68e389ea9ee3d1fdad8410154d34105017312 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x0891d5db1034efd9dc920a5454ae33c39d27db6ae761213d771c46a10bde8820 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xbdb7248e82508fda22bb827ec3d7a62e0e6a3cc6389495d2476201e5c1929c16 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xf08f3cc8f236384ef6b5f2dcb34bb005b7492fdee07a59b6c8051797bb60f803 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xa931ad78fca6c589d68ef8e04e2dce86750e52a5139fefc9bc67a2abdb16d086 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x0b2a446c7a82db2db0f1c58798ec869ed139cd3ce6363f81754f58004831cce1 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xf0852bd6c9916f8877ab0cf52dd9861216ca3c9f920eb525fedbc3187875e86d -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0xd42ce298ec3460076a9fb6b5bfe21a515c4141dd6c823e2ecbe1adb904e582d7 -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
0x80d417ea32ff31b5f135b253cf87ba967913605243921887b536f9c35e807c0b -(pending)2026-05-01 1:32:222 secs ago1777599142IN
Polymarket: CTF Exchange V2
0 POL(Pending)(Pending)
View all transactions

Latest 1 internal transaction

Parent Transaction Hash Block From To
849023532026-03-31 2:39:0330 days ago1774924743  Contract Creation0 POL
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CTFExchange

Compiler Version
v0.8.34+commit.80d5c536

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.34;

import { Auth } from "./mixins/Auth.sol";
import { Fees } from "./mixins/Fees.sol";
import { Assets } from "./mixins/Assets.sol";
import { Trading } from "./mixins/Trading.sol";
import { Pausable } from "./mixins/Pausable.sol";
import { Signatures } from "./mixins/Signatures.sol";
import { ERC1155TokenReceiver } from "./mixins/ERC1155TokenReceiver.sol";

import { ExchangeInitParams, Order } from "./libraries/Structs.sol";

//  ____   ___  _  __   ____  __    _    ____  _  _______ _____
// |  _ \ / _ \| | \ \ / /  \/  |  / \  |  _ \| |/ / ____|_   _|
// | |_) | | | | |  \ V /| |\/| | / _ \ | |_) | ' /|  _|   | |
// |  __/| |_| | |___| | | |  | |/ ___ \|  _ <| . \| |___  | |
// |_|    \___/|_____|_| |_|  |_/_/   \_\_| \_\_|\_\_____| |_|

/// @title CTF Exchange
/// @notice Implements logic for trading CTF assets
/// @author Polymarket
contract CTFExchange is Auth, ERC1155TokenReceiver, Pausable, Trading {
    /*--------------------------------------------------------------
                              CONSTRUCTOR
    --------------------------------------------------------------*/

    constructor(ExchangeInitParams memory params)
        Auth(params.admin)
        Assets(params.collateral, params.ctf, params.ctfCollateral, params.outcomeTokenFactory)
        Signatures(params.proxyFactory, params.safeFactory)
        Fees(params.feeReceiver)
    { }

    /*--------------------------------------------------------------
                             ONLY OPERATOR
    --------------------------------------------------------------*/

    /// @notice Matches a taker order against a list of maker orders
    /// @param conditionId          - The conditionId of the market being traded
    /// @param takerOrder           - The active order to be matched
    /// @param makerOrders          - The array of maker orders to be matched against the active order
    /// @param takerFillAmount      - The amount to fill on the taker order, always in terms of the maker amount
    /// @param makerFillAmounts     - The array of amounts to fill on the maker orders, always in terms of
    /// the maker amount
    /// @param takerFeeAmount       - The fee to be charged to the taker order
    /// @param makerFeeAmounts      - The fee to be charged to the maker orders
    function matchOrders(
        bytes32 conditionId,
        Order memory takerOrder,
        Order[] memory makerOrders,
        uint256 takerFillAmount,
        uint256[] memory makerFillAmounts,
        uint256 takerFeeAmount,
        uint256[] memory makerFeeAmounts
    ) external onlyOperator notPaused {
        uint256 makerLength = makerOrders.length;
        require(makerLength > 0, NoMakerOrders());
        require(
            makerLength == makerFillAmounts.length && makerLength == makerFeeAmounts.length, MismatchedArrayLengths()
        );

        _matchOrders(
            conditionId, takerOrder, makerOrders, takerFillAmount, makerFillAmounts, takerFeeAmount, makerFeeAmounts
        );
    }

    /// @notice Entrypoint to set an order as preapproved
    /// @param order - The order to be set as preapproved
    function preapproveOrder(Order memory order) external onlyOperator {
        bytes32 orderHash = hashOrder(order);

        _preapproveOrder(orderHash, order);
    }

    /// @notice Entrypoint to invalidate a preapproval
    /// @param orderHash - The hash of the order to invalidate
    function invalidatePreapprovedOrder(bytes32 orderHash) external onlyOperator {
        _invalidatePreapprovedOrder(orderHash);
    }

    /*--------------------------------------------------------------
                               ONLY ADMIN
    --------------------------------------------------------------*/

    /// @notice Pause trading on the Exchange
    function pauseTrading() external onlyAdmin {
        _pauseTrading();
    }

    /// @notice Unpause trading on the Exchange
    function unpauseTrading() external onlyAdmin {
        _unpauseTrading();
    }

    /// @notice Sets the user pause block interval
    /// @param _interval - The new user pause block interval
    function setUserPauseBlockInterval(uint256 _interval) external onlyAdmin {
        _setUserPauseBlockInterval(_interval);
    }

    /// @notice Sets a new fee receiver for the Exchange
    /// @param receiver - The new fee receiver address
    function setFeeReceiver(address receiver) external onlyAdmin {
        _setFeeReceiver(receiver);
    }

    /// @notice Sets the maximum fee rate for trades
    /// @param rate - The new max fee rate in basis points
    function setMaxFeeRate(uint256 rate) external onlyAdmin {
        _setMaxFeeRate(rate);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { IAuth } from "../interfaces/IAuth.sol";

/// @title Auth
/// @notice Provides admin and operator roles and access control modifiers
abstract contract Auth is IAuth {
    /// @dev The set of addresses authorized as Admins
    mapping(address => bool) internal admins;

    /// @dev The number of active admins
    uint256 internal adminCount;

    /// @dev The set of addresses authorized as Operators
    mapping(address => bool) internal operators;

    modifier onlyAdmin() {
        require(admins[msg.sender], NotAdmin());
        _;
    }

    modifier onlyOperator() {
        require(operators[msg.sender], NotOperator());
        _;
    }

    constructor(address _admin) {
        admins[_admin] = true;
        adminCount = 1;
        operators[_admin] = true;
    }

    /// @notice Returns whether an address is an admin
    /// @param _usr The address to check
    function isAdmin(address _usr) external view returns (bool) {
        return admins[_usr];
    }

    /// @notice Returns whether an address is an operator
    /// @param _usr The address to check
    function isOperator(address _usr) external view returns (bool) {
        return operators[_usr];
    }

    /// @notice Adds a new admin
    /// Can only be called by a current admin
    /// @param _admin - The new admin
    function addAdmin(address _admin) external onlyAdmin {
        require(!admins[_admin], AlreadyAdmin());
        ++adminCount;
        admins[_admin] = true;
        emit NewAdmin(_admin, msg.sender);
    }

    /// @notice Adds a new operator
    /// Can only be called by a current admin
    /// @param _operator - The new operator
    function addOperator(address _operator) external onlyAdmin {
        require(!operators[_operator], AlreadyOperator());
        operators[_operator] = true;
        emit NewOperator(_operator, msg.sender);
    }

    /// @notice Removes an existing Admin
    /// Can only be called by a current admin
    /// @param _admin - The admin to be removed
    function removeAdmin(address _admin) external onlyAdmin {
        require(admins[_admin], NotAdmin());
        require(adminCount > 1, LastAdmin());
        --adminCount;
        admins[_admin] = false;
        emit RemovedAdmin(_admin, msg.sender);
    }

    /// @notice Removes an existing operator
    /// Can only be called by a current admin
    /// @param _operator - The operator to be removed
    function removeOperator(address _operator) external onlyAdmin {
        require(operators[_operator], NotOperator());
        operators[_operator] = false;
        emit RemovedOperator(_operator, msg.sender);
    }

    /// @notice Removes the operator role for the caller
    /// @dev Can only be called by an existing operator
    function renounceOperatorRole() external onlyOperator {
        operators[msg.sender] = false;
        emit RemovedOperator(msg.sender, msg.sender);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { IFees } from "../interfaces/IFees.sol";

abstract contract Fees is IFees {
    /// @notice Denominator for basis points calculations
    uint256 internal constant BPS_DENOMINATOR = 10_000;

    /// @notice Maximum allowed fee rate in basis points
    uint256 internal constant MAX_FEE_RATE_BPS_CAP = 10_000; // 100%

    /// @notice The address that receives fees
    address internal feeReceiver;

    /// @notice The maximum fee rate allowed in basis points
    uint256 internal maxFeeRateBps = 500; // Default to 5%

    constructor(address _feeReceiver) {
        feeReceiver = _feeReceiver;
    }

    /// @notice Returns the current fee receiver address
    function getFeeReceiver() public view override returns (address) {
        return feeReceiver;
    }

    /// @notice Returns the current max fee rate in basis points
    function getMaxFeeRate() public view override returns (uint256) {
        return maxFeeRateBps;
    }

    /// @notice Validates that the fee does not exceed the maximum allowed rate
    /// @param fee       - The fee amount being charged, denominated in collateral
    /// @param cashValue - The value of the trade, denominated in collateral
    function validateFee(uint256 fee, uint256 cashValue) public view override {
        _validateFeeWithMaxFeeRate(fee, cashValue, maxFeeRateBps);
    }

    /// @notice Validates that the fee does not exceed the specified maximum fee rate
    /// @param fee           - The fee amount being charged, denominated in collateral
    /// @param cashValue     - The value of the trade, denominated in collateral
    /// @param maxFeeRate    - The maximum fee rate allowed in basis points
    function _validateFeeWithMaxFeeRate(uint256 fee, uint256 cashValue, uint256 maxFeeRate) internal pure override {
        if (fee == 0) return;

        // No limit enforced if rate is 0
        if (maxFeeRate == 0) return;
        uint256 maxAllowedFee = (cashValue * maxFeeRate) / BPS_DENOMINATOR;
        require(fee <= maxAllowedFee, FeeExceedsMaxRate());
    }

    /// @notice Sets the fee receiver address
    /// @param _feeReceiver - The new fee receiver address
    function _setFeeReceiver(address _feeReceiver) internal override {
        feeReceiver = _feeReceiver;
        emit FeeReceiverUpdated(_feeReceiver);
    }

    /// @notice Sets the maximum fee rate in basis points
    /// @param _maxFeeRateBps - The new max fee rate in bps (e.g., 500 = 5%), max (99.99%)
    function _setMaxFeeRate(uint256 _maxFeeRateBps) internal override {
        require(_maxFeeRateBps < MAX_FEE_RATE_BPS_CAP, MaxFeeRateExceedsCeiling());

        maxFeeRateBps = _maxFeeRateBps;
        emit MaxFeeRateUpdated(_maxFeeRateBps);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { ERC20 } from "@solady/src/tokens/ERC20.sol";
import { ERC1155 } from "@solady/src/tokens/ERC1155.sol";

import { IAssets } from "../interfaces/IAssets.sol";

abstract contract Assets is IAssets {
    /// @notice The Collateral token address
    address internal immutable collateral;

    /// @notice The Conditional Tokens Framework address
    address internal immutable ctf;

    /// @notice The collateral address used by the CTF for position ID derivation
    address internal immutable ctfCollateral;

    /// @notice The address that facilitates Outcome Token minting or merging
    address internal immutable outcomeTokenFactory;

    constructor(address _collateral, address _ctf, address _ctfCollateral, address _outcomeTokenFactory) {
        collateral = _collateral;
        ctf = _ctf;
        ctfCollateral = _ctfCollateral;
        outcomeTokenFactory = _outcomeTokenFactory;
        ERC20(_collateral).approve(_outcomeTokenFactory, type(uint256).max);
        ERC1155(_ctf).setApprovalForAll(_outcomeTokenFactory, true);
    }

    /// @notice Returns the collateral token address
    function getCollateral() public view override returns (address) {
        return collateral;
    }

    /// @notice Returns the Conditional Tokens Framework address
    function getCtf() public view override returns (address) {
        return ctf;
    }

    /// @notice Returns the collateral address used by the CTF for position ID derivation
    function getCtfCollateral() public view override returns (address) {
        return ctfCollateral;
    }

    /// @notice Returns the address that facilitates outcome token minting or merging
    function getOutcomeTokenFactory() public view override returns (address) {
        return outcomeTokenFactory;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { ITrading } from "../interfaces/ITrading.sol";
import { IUserPausable } from "../interfaces/IUserPausable.sol";

import { CalculatorHelper } from "../libraries/CalculatorHelper.sol";
import { Order, Side, MatchType, OrderStatus } from "../libraries/Structs.sol";
import { CTHelpers } from "@ctf-exchange-v2/src/adapters/libraries/CTHelpers.sol";

import { Hashing } from "./Hashing.sol";
import { UserPausable } from "./UserPausable.sol";
import { AssetOperations } from "./AssetOperations.sol";
import { Events } from "./Events.sol";
import { Fees } from "./Fees.sol";
import { Signatures } from "./Signatures.sol";

/// @title Trading
/// @notice Implements logic for trading CTF assets
abstract contract Trading is Hashing, AssetOperations, Events, Fees, UserPausable, Signatures, ITrading {
    /// @notice Mapping of orders to their current status
    mapping(bytes32 => OrderStatus) public orderStatus;

    /// @notice Parameters for a prepared maker order (validated and ready for settlement)
    struct PreparedMakerOrder {
        bytes32 orderHash;
        uint256 makingAmount;
        uint256 takingAmount;
        address maker;
        uint256 takerAssetId;
        Side side;
        uint256 feeAmount;
        bytes32 builder;
        bytes32 metadata;
        uint256 tokenId;
    }

    /// @notice Gets the status of an order
    /// @param orderHash    - The hash of the order
    function getOrderStatus(bytes32 orderHash) public view returns (OrderStatus memory) {
        return orderStatus[orderHash];
    }

    /// @notice Validates an order
    /// @notice order - The order to be validated
    function validateOrder(Order memory order) external view {
        bytes32 orderHash = hashOrder(order);

        require(!orderStatus[orderHash].filled, OrderAlreadyFilled());

        _validateOrder(orderHash, order);
    }

    function _validateOrder(bytes32 orderHash, Order memory order) internal view {
        // Validate order is not zero-sized
        require(order.makerAmount > 0, ZeroMakerAmount());

        // Validate signature
        validateOrderSignature(orderHash, order);

        // Validate that the user is not paused
        require(!isUserPaused(order.maker), UserIsPaused());
    }

    /// @notice Matches orders against each other
    /// @dev Transfers assets between taker and maker orders, settling fees as necessary
    /// @dev Pulls assets from the taker order to the Exchange
    /// @dev Settles maker orders against the Exchange, using the assets received from the taker order
    /// @dev Settles the taker order against the Exchange, using the assets received from the maker orders
    /// @param conditionId          - The conditionId of the market being traded
    /// @param takerOrder           - The active order to be matched
    /// @param makerOrders          - The array of maker orders to be matched against the active order
    /// @param takerFillAmount      - The amount to fill on the taker order, always in terms of the maker amount
    /// @param makerFillAmounts     - The array of amounts to fill on the maker orders, always in terms of
    /// the maker amount
    /// @param takerFeeAmount       - The fee to be charged to the taker order
    /// @param makerFeeAmounts      - The fee to be charged to the maker orders
    function _matchOrders(
        bytes32 conditionId,
        Order memory takerOrder,
        Order[] memory makerOrders,
        uint256 takerFillAmount,
        uint256[] memory makerFillAmounts,
        uint256 takerFeeAmount,
        uint256[] memory makerFeeAmounts
    ) internal {
        require(makerOrders.length > 0, NoMakerOrders());

        // Validate all tokenIds are valid positions for this conditionId
        _validateTokenIds(conditionId, takerOrder, makerOrders);
        // Check if all matches are COMPLEMENTARY (all makers have opposite side to taker)
        if (_isAllComplementary(takerOrder.side, makerOrders)) {
            _settleComplementary(
                takerOrder, makerOrders, makerFillAmounts, makerFeeAmounts, takerFillAmount, takerFeeAmount
            );
            return;
        }

        if (takerOrder.side == Side.BUY) {
            _matchBuyOrders(
                conditionId, takerOrder, makerOrders, takerFillAmount, makerFillAmounts, takerFeeAmount, makerFeeAmounts
            );
            return;
        }

        (uint256 taking, bytes32 orderHash) = _performOrderChecks(takerOrder, takerFillAmount, takerFeeAmount);

        (uint256 makerAssetId, uint256 takerAssetId) = _deriveAssetIds(takerOrder);

        _transfer(takerOrder.maker, address(this), makerAssetId, takerFillAmount);

        // Settle maker orders with delta-based surplus (prevents pre-existing balance inflation)
        {
            uint256 balanceBefore = _getBalance(takerAssetId);

            uint256 makerExchangeFees =
                _settleMakerOrders(conditionId, takerOrder, makerOrders, makerFillAmounts, makerFeeAmounts);

            // Batch transfer maker SELL fees before taker settlement (so refund logic doesn't see them as leftover)
            if (makerExchangeFees > 0) _transfer(address(this), getFeeReceiver(), 0, makerExchangeFees);

            uint256 balanceAfter = _getBalance(takerAssetId);
            require(balanceAfter >= taking + balanceBefore, TooLittleTokensReceived());
            // Actual taking amount
            taking = balanceAfter - balanceBefore;
        }

        uint256 takerExchangeFee =
            _settleTakerOrder(takerOrder.side, taking, takerOrder.maker, makerAssetId, takerAssetId, takerFeeAmount);

        _emitTakerFilledEvents(
            OrderFilledParams({
                orderHash: orderHash,
                maker: takerOrder.maker,
                taker: address(this),
                side: takerOrder.side,
                tokenId: takerOrder.tokenId,
                makerAmountFilled: takerFillAmount,
                takerAmountFilled: taking,
                fee: takerFeeAmount,
                builder: takerOrder.builder,
                metadata: takerOrder.metadata
            })
        );

        if (takerExchangeFee > 0) _transfer(address(this), getFeeReceiver(), 0, takerExchangeFee);
    }

    /// @notice Settles COMPLEMENTARY orders with direct peer-to-peer transfers
    /// @dev Eliminates exchange as intermediary, saving ~3 transfers per match
    function _settleComplementary(
        Order memory takerOrder,
        Order[] memory makerOrders,
        uint256[] memory makerFillAmounts,
        uint256[] memory makerFeeAmounts,
        uint256 takerFillAmount,
        uint256 takerFeeAmount
    ) internal {
        bytes32 takerOrderHash = hashOrder(takerOrder);
        _validateOrder(takerOrderHash, takerOrder);

        address taker = takerOrder.maker;
        bool takerIsBuy = takerOrder.side == Side.BUY;
        uint256 totalMakerFees = 0;
        uint256 executedTakerMakingAmount = 0;
        uint256 executedTakerTakingAmount = 0;

        for (uint256 i = 0; i < makerOrders.length;) {
            (uint256 makerFee, uint256 makerImpliedTakerMakingAmount) = _settleComplementaryMaker(
                takerOrder, makerOrders[i], makerFillAmounts[i], makerFeeAmounts[i], taker, takerIsBuy
            );
            unchecked {
                totalMakerFees += makerFee;
                executedTakerMakingAmount += makerImpliedTakerMakingAmount;
                executedTakerTakingAmount += makerFillAmounts[i];
                ++i;
            }
        }

        if (executedTakerMakingAmount > takerFillAmount) revert ComplementaryFillExceedsTakerFill();

        uint256 minimumTakerTakingAmount =
            _calculateTakingAndValidateFee(takerOrder, executedTakerMakingAmount, takerFeeAmount);
        if (executedTakerTakingAmount < minimumTakerTakingAmount) revert TooLittleTokensReceived();

        _updateOrderStatus(takerOrderHash, takerOrder, executedTakerMakingAmount);

        totalMakerFees += _settleComplementaryTaker(
            takerOrder,
            taker,
            takerIsBuy,
            takerOrderHash,
            executedTakerMakingAmount,
            executedTakerTakingAmount,
            takerFeeAmount
        );

        if (totalMakerFees > 0) _transfer(takerIsBuy ? taker : address(this), getFeeReceiver(), 0, totalMakerFees);
    }

    /// @notice Settles a single maker in COMPLEMENTARY match
    /// @return makerFee - Fee to batch (for maker SELL only)
    /// @return makerImpliedTakerMakingAmount - Taker-side order consumption implied by the maker's ratio
    function _settleComplementaryMaker(
        Order memory takerOrder,
        Order memory makerOrder,
        uint256 fillAmount,
        uint256 feeAmount,
        address taker,
        bool takerIsBuy
    ) internal returns (uint256 makerFee, uint256 makerImpliedTakerMakingAmount) {
        _validateOrdersMatch(takerOrder, makerOrder, MatchType.COMPLEMENTARY);

        (uint256 taking, bytes32 orderHash) = _performOrderChecks(makerOrder, fillAmount, feeAmount);
        makerImpliedTakerMakingAmount = taking;

        if (takerIsBuy) {
            // Taker BUY ↔ Maker SELL: direct transfers both ways
            _transfer(makerOrder.maker, taker, makerOrder.tokenId, fillAmount); // CTF: maker → taker
            uint256 makerReceives = taking;
            if (feeAmount > 0) {
                require(feeAmount <= taking, FeeExceedsProceeds());
                unchecked {
                    makerReceives -= feeAmount;
                }
                makerFee = feeAmount;
                _emitFeeCharged(getFeeReceiver(), feeAmount);
            }
            _transfer(taker, makerOrder.maker, 0, makerReceives); // Collateral: taker → maker
        } else {
            // Taker SELL ↔ Maker BUY: CTF direct, collateral through exchange
            _transfer(taker, makerOrder.maker, takerOrder.tokenId, taking); // CTF: taker → maker
            _transfer(makerOrder.maker, address(this), 0, fillAmount + feeAmount); // Collateral: maker → exchange
            if (feeAmount > 0) {
                makerFee = feeAmount;
                _emitFeeCharged(getFeeReceiver(), feeAmount);
            }
        }

        _emitOrderFilledEvent(
            OrderFilledParams({
                orderHash: orderHash,
                maker: makerOrder.maker,
                taker: taker,
                side: makerOrder.side,
                tokenId: makerOrder.tokenId,
                makerAmountFilled: fillAmount,
                takerAmountFilled: taking,
                fee: feeAmount,
                builder: makerOrder.builder,
                metadata: makerOrder.metadata
            })
        );
    }

    /// @notice Settles taker in COMPLEMENTARY match
    function _settleComplementaryTaker(
        Order memory takerOrder,
        address taker,
        bool takerIsBuy,
        bytes32 takerOrderHash,
        uint256 executedTakerMakingAmount,
        uint256 executedTakerTakingAmount,
        uint256 takerFeeAmount
    ) internal returns (uint256 batchedFee) {
        if (takerIsBuy) {
            if (takerFeeAmount > 0) {
                batchedFee = takerFeeAmount;
                _emitFeeCharged(getFeeReceiver(), takerFeeAmount);
            }
        } else {
            uint256 takerProceeds = executedTakerTakingAmount;
            if (takerFeeAmount > 0) {
                require(takerFeeAmount <= executedTakerTakingAmount, FeeExceedsProceeds());
                unchecked {
                    takerProceeds -= takerFeeAmount;
                }
                batchedFee = takerFeeAmount;
                _emitFeeCharged(getFeeReceiver(), takerFeeAmount);
            }
            _transfer(address(this), taker, 0, takerProceeds);
        }

        _emitTakerFilledEvents(
            OrderFilledParams({
                orderHash: takerOrderHash,
                maker: taker,
                taker: address(this),
                side: takerOrder.side,
                tokenId: takerOrder.tokenId,
                makerAmountFilled: executedTakerMakingAmount,
                takerAmountFilled: executedTakerTakingAmount,
                fee: takerFeeAmount,
                builder: takerOrder.builder,
                metadata: takerOrder.metadata
            })
        );
    }

    function _matchBuyOrders(
        bytes32 conditionId,
        Order memory takerOrder,
        Order[] memory makerOrders,
        uint256 takerFillAmount,
        uint256[] memory makerFillAmounts,
        uint256 takerFeeAmount,
        uint256[] memory makerFeeAmounts
    ) internal {
        (uint256 taking, bytes32 orderHash) = _performOrderChecks(takerOrder, takerFillAmount, takerFeeAmount);
        (uint256 makerAssetId, uint256 takerAssetId) = _deriveAssetIds(takerOrder);

        _transfer(takerOrder.maker, address(this), makerAssetId, takerFillAmount + takerFeeAmount);

        // Settle maker orders with delta-based surplus (prevents pre-existing balance inflation)
        {
            uint256 balanceBefore = _getBalance(takerAssetId);

            uint256 batchedExchangeFees =
                _settleMakerOrders(conditionId, takerOrder, makerOrders, makerFillAmounts, makerFeeAmounts);

            if (takerFeeAmount > 0) {
                batchedExchangeFees += takerFeeAmount;
                _emitFeeCharged(getFeeReceiver(), takerFeeAmount);
            }

            if (batchedExchangeFees > 0) _transfer(address(this), getFeeReceiver(), 0, batchedExchangeFees);

            uint256 balanceAfter = _getBalance(takerAssetId);
            require(balanceAfter >= taking + balanceBefore, TooLittleTokensReceived());
            taking = balanceAfter - balanceBefore;
        }

        _settleTakerOrder(takerOrder.side, taking, takerOrder.maker, makerAssetId, takerAssetId, 0);

        _emitTakerFilledEvents(
            OrderFilledParams({
                orderHash: orderHash,
                maker: takerOrder.maker,
                taker: address(this),
                side: takerOrder.side,
                tokenId: takerOrder.tokenId,
                makerAmountFilled: takerFillAmount,
                takerAmountFilled: taking,
                fee: takerFeeAmount,
                builder: takerOrder.builder,
                metadata: takerOrder.metadata
            })
        );
    }

    /// @notice Settles a Taker order
    /// @dev Transfer proceeds from Exchange to order maker
    /// @dev Charge fee on Collateral proceeds if Sell, or on order maker Collateral if Buy
    /// @return exchangeFee - Fee amount to be paid from exchange (for SELL orders), 0 for BUY orders
    function _settleTakerOrder(
        Side side,
        uint256 takingAmount,
        address maker,
        uint256 makerAssetId,
        uint256 takerAssetId,
        uint256 feeAmount
    ) internal returns (uint256 exchangeFee) {
        uint256 proceeds = takingAmount;
        if (side == Side.SELL) {
            // SELL: fee deducted from proceeds, will be batched
            require(feeAmount <= takingAmount, FeeExceedsProceeds());
            unchecked {
                proceeds = takingAmount - feeAmount; // safety: feeAmount <= takingAmount checked above
            }
            exchangeFee = feeAmount;
        }

        // Transfer order proceeds from the Exchange to the taker order maker
        _transfer(address(this), maker, takerAssetId, proceeds);

        // Charge fees (emit event, transfer batched for SELL or immediate for BUY)
        if (side == Side.SELL) {
            // SELL: emit event now, transfer will be batched later
            if (feeAmount > 0) _emitFeeCharged(getFeeReceiver(), feeAmount);
        } else {
            // BUY: fee transferred from maker directly (cannot batch)
            _chargeFee(maker, feeAmount);
        }

        // Refund any leftover tokens
        uint256 refund = _getBalance(makerAssetId);
        if (refund > 0) _transfer(address(this), maker, makerAssetId, refund);
    }

    function _settleMakerOrders(
        bytes32 conditionId,
        Order memory takerOrder,
        Order[] memory makerOrders,
        uint256[] memory makerFillAmounts,
        uint256[] memory makerFeeAmounts
    ) internal returns (uint256 totalExchangeFees) {
        uint256 length = makerOrders.length;

        // Phase 1: Prepare all maker orders (validate, transfer from makers, accumulate batch totals)
        PreparedMakerOrder[] memory prepared = new PreparedMakerOrder[](length);
        uint256 totalMintAmount = 0;
        uint256 totalMergeAmount = 0;

        for (uint256 i = 0; i < length;) {
            MatchType matchType = _deriveMatchType(takerOrder, makerOrders[i]);
            prepared[i] =
                _prepareMakerOrder(takerOrder, makerOrders[i], makerFillAmounts[i], makerFeeAmounts[i], matchType);

            // Accumulate batch totals based on match type
            if (matchType == MatchType.MINT) {
                unchecked {
                    totalMintAmount += prepared[i].takingAmount; // safety: token amounts can't realistically overflow
                    // uint256
                }
            } else if (matchType == MatchType.MERGE) {
                unchecked {
                    totalMergeAmount += prepared[i].makingAmount; // safety: token amounts can't realistically overflow
                    // uint256
                }
            }
            unchecked {
                ++i; // safety: i < length which fits in memory
            }
        }

        // Phase 2: Execute batched CTF operations (one mint and/or one merge)
        if (totalMintAmount > 0) _mint(conditionId, totalMintAmount);
        if (totalMergeAmount > 0) _merge(conditionId, totalMergeAmount);

        // Phase 3: Distribute proceeds to all makers, accumulating exchange-held fees
        for (uint256 i = 0; i < length;) {
            PreparedMakerOrder memory preparedOrder = prepared[i];
            unchecked {
                if (preparedOrder.side == Side.SELL) {
                    totalExchangeFees += _distributeSellMakerProceeds(preparedOrder, takerOrder.maker);
                } else {
                    totalExchangeFees += _distributeBuyMakerProceeds(preparedOrder, takerOrder.maker);
                }
            }
            unchecked {
                ++i; // safety: i < length which fits in memory
            }
        }
    }

    function _prepareMakerOrder(
        Order memory takerOrder,
        Order memory makerOrder,
        uint256 fillAmount,
        uint256 feeAmount,
        MatchType matchType
    ) internal returns (PreparedMakerOrder memory prepared) {
        // Ensure taker order and maker order match
        _validateOrdersMatch(takerOrder, makerOrder, matchType);

        (uint256 taking, bytes32 orderHash) = _performOrderChecks(makerOrder, fillAmount, feeAmount);

        (uint256 makerAssetId, uint256 takerAssetId) = _deriveAssetIds(makerOrder);

        _transfer(
            makerOrder.maker, address(this), makerAssetId, fillAmount + (makerOrder.side == Side.BUY ? feeAmount : 0)
        );

        prepared = PreparedMakerOrder({
            orderHash: orderHash,
            makingAmount: fillAmount,
            takingAmount: taking,
            maker: makerOrder.maker,
            takerAssetId: takerAssetId,
            side: makerOrder.side,
            feeAmount: feeAmount,
            builder: makerOrder.builder,
            metadata: makerOrder.metadata,
            tokenId: makerOrder.tokenId
        });
    }

    /// @notice Distributes proceeds to a maker after batch CTF operations
    /// @param p            - The prepared maker order data
    /// @param takerMaker   - The taker order's maker address (for event)
    /// @return exchangeFee - Fee amount already held by the exchange and ready to batch
    function _distributeBuyMakerProceeds(PreparedMakerOrder memory p, address takerMaker)
        internal
        returns (uint256 exchangeFee)
    {
        _transfer(address(this), p.maker, p.takerAssetId, p.takingAmount);

        if (p.feeAmount > 0) {
            exchangeFee = p.feeAmount;
            _emitFeeCharged(getFeeReceiver(), p.feeAmount);
        }
        _emitOrderFilledEvent(
            OrderFilledParams({
                orderHash: p.orderHash,
                maker: p.maker,
                taker: takerMaker,
                side: p.side,
                tokenId: p.tokenId,
                makerAmountFilled: p.makingAmount,
                takerAmountFilled: p.takingAmount,
                fee: p.feeAmount,
                builder: p.builder,
                metadata: p.metadata
            })
        );
    }

    function _distributeSellMakerProceeds(PreparedMakerOrder memory p, address takerMaker)
        internal
        returns (uint256 exchangeFee)
    {
        uint256 proceeds = p.takingAmount;
        require(p.feeAmount <= p.takingAmount, FeeExceedsProceeds());
        unchecked {
            proceeds = p.takingAmount - p.feeAmount;
        }
        exchangeFee = p.feeAmount;

        _transfer(address(this), p.maker, p.takerAssetId, proceeds);

        if (p.feeAmount > 0) _emitFeeCharged(getFeeReceiver(), p.feeAmount);

        _emitOrderFilledEvent(
            OrderFilledParams({
                orderHash: p.orderHash,
                maker: p.maker,
                taker: takerMaker,
                side: p.side,
                tokenId: p.tokenId,
                makerAmountFilled: p.makingAmount,
                takerAmountFilled: p.takingAmount,
                fee: p.feeAmount,
                builder: p.builder,
                metadata: p.metadata
            })
        );
    }

    /// @notice Performs common order computations and validation
    /// 1) Computes the order hash
    /// 2) Validates the order
    /// 3) Updates the order status in storage
    /// 4) Computes taking amount
    /// 5) Validates fee against max fee rate (lazy-loads maxFeeRateBps only when fee != 0)
    /// @param order        - The order being prepared
    /// @param making       - The amount of the order being filled, in terms of maker amount
    /// @param fee          - The fee charged to the order by the operator
    function _performOrderChecks(Order memory order, uint256 making, uint256 fee)
        internal
        returns (uint256 takingAmount, bytes32 orderHash)
    {
        orderHash = hashOrder(order);

        // Validate order
        _validateOrder(orderHash, order);

        // Update the order status in storage
        _updateOrderStatus(orderHash, order, making);

        takingAmount = _calculateTakingAndValidateFee(order, making, fee);
    }

    function _calculateTakingAndValidateFee(Order memory order, uint256 making, uint256 fee)
        internal
        view
        returns (uint256 takingAmount)
    {
        takingAmount = CalculatorHelper.calculateTakingAmount(making, order.makerAmount, order.takerAmount);

        // Validate fee against max fee rate (reads storage only when fee is non-zero)
        if (fee != 0) {
            uint256 cashValue = order.side == Side.BUY ? making : takingAmount;
            _validateFeeWithMaxFeeRate(fee, cashValue, maxFeeRateBps);
        }
    }

    function _deriveMatchType(Order memory takerOrder, Order memory makerOrder) internal pure returns (MatchType) {
        // Enum values: Side.Buy: 0 Side.Sell 1
        // Enum math: add one to the takerSide, and multiply it by 1 IF it's equal to the makerSide, OR
        // multiply by zero if it isn't

        MatchType matchType;
        Side takerOrderSide = takerOrder.side;
        Side makerOrderSide = makerOrder.side;

        assembly ("memory-safe") {
            matchType := mul(add(takerOrderSide, 1), eq(takerOrderSide, makerOrderSide))
        }

        return matchType;
    }

    function _isAllComplementary(Side takerSide, Order[] memory makerOrders) internal pure returns (bool result) {
        assembly {
            result := 1
            let length := mload(makerOrders)
            let ptr := add(makerOrders, 0x20)
            for { let i := 0 } lt(i, length) { i := add(i, 1) } {
                let orderPtr := mload(add(ptr, shl(5, i)))
                // side is at offset 0xC0 in Order struct
                let side := mload(add(orderPtr, 0xC0))
                if eq(side, takerSide) {
                    result := 0
                    break
                }
            }
        }
    }

    function _deriveAssetIds(Order memory order) internal pure returns (uint256 makerAssetId, uint256 takerAssetId) {
        // SIDE * tokenId, tokenId - SIDE * tokenId
        // buy: 0, tokenId
        // sell: tokenId, 0
        Side side = order.side;
        uint256 tokenId = order.tokenId;
        assembly ("memory-safe") {
            makerAssetId := mul(side, tokenId)
            takerAssetId := sub(tokenId, makerAssetId)
        }
    }

    /// @notice Validates that all order tokenIds are valid positions for the given conditionId
    function _validateTokenIds(bytes32 conditionId, Order memory takerOrder, Order[] memory makerOrders) internal view {
        address col = getCtfCollateral();
        uint256 pos1 = CTHelpers.getPositionId(col, CTHelpers.getCollectionId(bytes32(0), conditionId, 1));
        uint256 pos2 = CTHelpers.getPositionId(col, CTHelpers.getCollectionId(bytes32(0), conditionId, 2));

        uint256 takerTokenId = takerOrder.tokenId;
        require(takerTokenId == pos1 || takerTokenId == pos2, MismatchedTokenIds());

        for (uint256 i = 0; i < makerOrders.length; ++i) {
            uint256 makerTokenId = makerOrders[i].tokenId;
            require(makerTokenId == pos1 || makerTokenId == pos2, MismatchedTokenIds());
        }
    }

    /// @notice Ensures the taker and maker orders can be matched against each other
    /// @param takerOrder   - The taker order
    /// @param makerOrder   - The maker order
    function _validateOrdersMatch(Order memory takerOrder, Order memory makerOrder, MatchType matchType) internal pure {
        if (matchType == MatchType.COMPLEMENTARY) {
            require(takerOrder.tokenId == makerOrder.tokenId, MismatchedTokenIds());

            // For BUY vs SELL on the same token, the crossing condition is:
            //   buyPrice >= sellPrice
            //
            // Expressed in order terms:
            //   (makerAmount_buy / takerAmount_buy) >= (takerAmount_sell / makerAmount_sell)
            //
            // Cross-multiplying representation:
            //   makerAmount_A * makerAmount_B >= takerAmount_A * takerAmount_B
            //
            // handles the takerAmount == 0 edge case (RHS is 0, always true).
            if (takerOrder.makerAmount * makerOrder.makerAmount < takerOrder.takerAmount * makerOrder.takerAmount) {
                revert NotCrossing();
            }
        } else {
            require(takerOrder.tokenId != makerOrder.tokenId, MismatchedTokenIds());

            if (matchType == MatchType.MINT) {
                require(
                    takerOrder.takerAmount * makerOrder.makerAmount + makerOrder.takerAmount * takerOrder.makerAmount
                        >= takerOrder.takerAmount * makerOrder.takerAmount,
                    NotCrossing()
                );
            } else {
                require(
                    takerOrder.takerAmount * makerOrder.makerAmount + makerOrder.takerAmount * takerOrder.makerAmount
                        <= takerOrder.makerAmount * makerOrder.makerAmount,
                    NotCrossing()
                );
            }
        }
    }

    function _chargeFee(address payer, uint256 fee) internal {
        if (fee > 0) {
            address receiver = getFeeReceiver();
            _transfer(payer, receiver, 0, fee);
            _emitFeeCharged(receiver, fee);
        }
    }

    function _updateOrderStatus(bytes32 orderHash, Order memory order, uint256 makingAmount)
        internal
        returns (uint256 remaining)
    {
        OrderStatus storage status = orderStatus[orderHash];

        // Single SLOAD: read packed slot and extract both filled and remaining
        bool filled;
        assembly {
            let packed := sload(status.slot)
            filled := and(packed, 0xff)
            remaining := shr(8, packed)
        }

        // Validate that the order can be filled
        require(!filled, OrderAlreadyFilled());

        // Update remaining if the order is new/has not been filled
        remaining = remaining == 0 ? order.makerAmount : remaining;

        // Throw if the makingAmount(amount to be filled) is greater than the amount available
        require(makingAmount <= remaining, MakingGtRemaining());

        unchecked {
            remaining = remaining - makingAmount; // safety: makingAmount <= remaining checked above
        }

        // Single SSTORE: pack filled (1 byte) and remaining (31 bytes) into one slot
        assembly {
            let packed := or(shl(8, remaining), iszero(remaining))
            sstore(status.slot, packed)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { IPausable } from "../interfaces/IPausable.sol";

abstract contract Pausable is IPausable {
    bool public paused = false;

    modifier notPaused() {
        require(!paused, Paused());
        _;
    }

    function _pauseTrading() internal override {
        paused = true;
        emit TradingPaused(msg.sender);
    }

    function _unpauseTrading() internal override {
        paused = false;
        emit TradingUnpaused(msg.sender);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

import { ECDSA } from "@solady/src/utils/ECDSA.sol";
import { SignatureCheckerLib } from "@solady/src/utils/SignatureCheckerLib.sol";

import { SignatureType, Order } from "../libraries/Structs.sol";

import { ISignatures } from "../interfaces/ISignatures.sol";

import { PolyFactoryHelper } from "./PolyFactoryHelper.sol";

/// @title Signatures
/// @notice Maintains logic that defines the various signature types and validates them
abstract contract Signatures is ISignatures, PolyFactoryHelper {
    constructor(address _proxyFactory, address _safeFactory) PolyFactoryHelper(_proxyFactory, _safeFactory) { }

    mapping(bytes32 => bool) internal preapproved;

    /// @notice Sets an order as preapproved
    /// @param orderHash - The hash of the order
    /// @param order     - The order
    function _preapproveOrder(bytes32 orderHash, Order memory order) internal {
        require(
            _isValidSignature(order.signer, order.maker, orderHash, order.signature, order.signatureType),
            InvalidSignature()
        );

        preapproved[orderHash] = true;

        emit OrderPreapproved(orderHash);
    }

    /// @notice Invalidates a preapproval
    /// @param orderHash - The hash of the order
    function _invalidatePreapprovedOrder(bytes32 orderHash) internal {
        preapproved[orderHash] = false;

        emit OrderPreapprovalInvalidated(orderHash);
    }

    /// @notice Validates the signature of an order
    /// @dev If the signature is empty, only preapproval is checked. This allows operators to omit
    /// the signature for preapproved orders, saving calldata gas and skipping ECDSA recovery.
    /// @param orderHash - The hash of the order
    /// @param order     - The order
    function validateOrderSignature(bytes32 orderHash, Order memory order) public view override {
        if (order.signature.length == 0) {
            require(_isPreapproved(orderHash), InvalidSignature());
        } else {
            require(
                _isValidSignature(order.signer, order.maker, orderHash, order.signature, order.signatureType),
                InvalidSignature()
            );
        }
    }

    /// @notice Verifies a signature for signed Order structs
    /// @param signer           - Address of the signer
    /// @param associated       - Address associated with the signer.
    ///                           For signature type EOA, this MUST be the same as the signer address.
    ///                           For signature types POLY_PROXY and POLY_GNOSIS_SAFE, this is the address of the proxy
    ///                           or the safe
    ///                           For signature type POLY_1271, this is the address of the contract
    /// @param structHash       - The hash of the struct being verified
    /// @param signature        - The signature to be verified
    /// @param signatureType    - The signature type to be verified
    function _isValidSignature(
        address signer,
        address associated,
        bytes32 structHash,
        bytes memory signature,
        SignatureType signatureType
    ) internal view returns (bool) {
        if (signatureType == SignatureType.EOA) {
            // EOA
            return _verifyEOASignature(signer, associated, structHash, signature);
        } else if (signatureType == SignatureType.POLY_GNOSIS_SAFE) {
            // POLY_GNOSIS_SAFE
            return _verifyPolySafeSignature(signer, associated, structHash, signature);
        } else if (signatureType == SignatureType.POLY_1271) {
            // POLY_1271
            return _verifyPoly1271Signature(signer, associated, structHash, signature);
        } else {
            // POLY_PROXY
            return _verifyPolyProxySignature(signer, associated, structHash, signature);
        }
    }

    /// @notice Verifies an EOA ECDSA signature
    /// Verifies that:
    /// 1) the signature is valid
    /// 2) the signer and maker are the same
    /// @param signer      - The address of the signer
    /// @param maker       - The address of the maker
    /// @param structHash  - The hash of the struct being verified
    /// @param signature   - The signature to be verified
    function _verifyEOASignature(address signer, address maker, bytes32 structHash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        return (signer == maker) && _verifyECDSASignature(signer, structHash, signature);
    }

    /// @notice Verifies an ECDSA signature
    /// @dev Reverts if the signature length is invalid or the recovered signer is the zero address
    /// @param signer      - Address of the signer
    /// @param structHash  - The hash of the struct being verified
    /// @param signature   - The signature to be verified
    function _verifyECDSASignature(address signer, bytes32 structHash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        return ECDSA.recover(structHash, signature) == signer;
    }

    /// @notice Verifies a signature signed by a Polymarket proxy wallet
    // Verifies that:
    // 1) the ECDSA signature is valid
    // 2) the Proxy wallet is owned by the signer
    /// @param signer       - Address of the signer
    /// @param proxyWallet  - Address of the poly proxy wallet
    /// @param structHash   - Hash of the struct being verified
    /// @param signature    - Signature to be verified
    function _verifyPolyProxySignature(address signer, address proxyWallet, bytes32 structHash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        return _verifyECDSASignature(signer, structHash, signature) && getProxyWalletAddress(signer) == proxyWallet;
    }

    /// @notice Verifies a signature signed by a Polymarket Gnosis safe
    // Verifies that:
    // 1) the ECDSA signature is valid
    // 2) the Safe is owned by the signer
    /// @param signer      - Address of the signer
    /// @param safeAddress - Address of the safe
    /// @param hash        - Hash of the struct being verified
    /// @param signature   - Signature to be verified
    function _verifyPolySafeSignature(address signer, address safeAddress, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        return _verifyECDSASignature(signer, hash, signature) && getSafeWalletAddress(signer) == safeAddress;
    }

    /// @notice Verifies a signature signed by a smart contract
    /// @param signer           - Address of the 1271 smart contract
    /// @param maker            - Address of the 1271 smart contract
    /// @param hash             - Hash of the struct being verified
    /// @param signature        - Signature to be verified
    function _verifyPoly1271Signature(address signer, address maker, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool)
    {
        return (signer == maker) && maker.code.length > 0
            && SignatureCheckerLib.isValidSignatureNow(maker, hash, signature);
    }

    function _isPreapproved(bytes32 orderHash) internal view returns (bool) {
        return preapproved[orderHash];
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.15;

/// @title ERC1155TokenReceiver
/// @author Polymarket
/// @notice Default ERC1155 token receiver that accepts all transfers.
abstract contract ERC1155TokenReceiver {
    /// @notice Handles receipt of a single ERC1155 token.
    /// @return The function selector for acceptance.
    function onERC1155Received(address, address, uint256, uint256, bytes calldata) external virtual returns (bytes4) {
        return ERC1155TokenReceiver.onERC1155Received.selector;
    }

    /// @notice Handles receipt of a batch of ERC1155 tokens.
    /// @return The function selector for acceptance.
    function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
        external
        virtual
        returns (bytes4)
    {
        return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
    }

    /// @notice ERC-165 interface detection
    /// @param interfaceId - The interface identifier to check
    function supportsInterface(bytes4 interfaceId) external pure virtual returns (bool) {
        return interfaceId == 0x4e2312e0 // ERC1155TokenReceiver
            || interfaceId == 0x01ffc9a7; // ERC165
    }
}

File 9 of 36 : Structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

struct ExchangeInitParams {
    /// @notice The admin of the exchange
    address admin;
    /// @notice The collateral token address
    address collateral;
    /// @notice The Conditional Tokens Framework address
    address ctf;
    /// @notice The collateral address used by the CTF for position ID derivation
    /// @dev May differ from `collateral` when using adapters (e.g. USDC.e vs CollateralToken)
    address ctfCollateral;
    /// @notice The Outcome Token Factory
    /// @dev Must be the Conditional Tokens Framework address or the Neg Risk Adapter address
    address outcomeTokenFactory;
    /// @notice The Polymarket proxy factory address
    address proxyFactory;
    /// @notice The Polymarket Gnosis Safe factory address
    address safeFactory;
    /// @notice The address which will receive fees
    address feeReceiver;
}

bytes32 constant ORDER_TYPEHASH = 0xbb86318a2138f5fa8ae32fbe8e659f8fcf13cc6ae4014a707893055433818589;
//keccak256(
//     "Order(uint256 salt,address maker,address signer,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint8
// side,uint8 signatureType,uint256 timestamp,bytes32 metadata,bytes32 builder)" );

struct Order {
    /// @notice Unique salt to ensure entropy
    uint256 salt;
    /// @notice Maker of the order, i.e the source of funds for the order
    address maker;
    /// @notice Signer of the order
    address signer;
    /// @notice Token Id of the CTF ERC1155 asset to be bought or sold
    /// If BUY, this is the tokenId of the asset to be bought, i.e the takerAssetId
    /// If SELL, this is the tokenId of the asset to be sold, i.e the makerAssetId
    uint256 tokenId;
    /// @notice Maker amount, i.e the maximum amount of tokens to be sold
    uint256 makerAmount;
    /// @notice Taker amount, i.e the minimum amount of tokens to be received
    uint256 takerAmount;
    /// @notice The side of the order: BUY or SELL
    Side side;
    /// @notice Signature type used by the Order: EOA, POLY_PROXY, POLY_GNOSIS_SAFE or POLY_1271
    SignatureType signatureType;
    /// @notice Unix timestamp in milliseconds at which the order was created
    uint256 timestamp;
    /// @notice The metadata associated with the order, hashed
    bytes32 metadata;
    /// @notice The builder code associated with the order, indicating its origin
    bytes32 builder;
    /// @notice The order signature
    bytes signature;
}

enum SignatureType {
    // 0: ECDSA EIP712 signatures signed by EOAs
    EOA,
    // 1: EIP712 signatures signed by EOAs that own Polymarket Proxy wallets
    POLY_PROXY,
    // 2: EIP712 signatures signed by EOAs that own Polymarket Gnosis safes
    POLY_GNOSIS_SAFE,
    // 3: EIP1271 signatures signed by smart contracts. To be used by smart contract wallets or vaults
    POLY_1271
}

enum Side {
    // 0: buy
    BUY,
    // 1: sell
    SELL
}

enum MatchType {
    // 0: buy vs sell
    COMPLEMENTARY,
    // 1: both buys
    MINT,
    // 2: both sells
    MERGE
}

struct OrderStatus {
    bool filled;
    uint248 remaining;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

interface IAuthEE {
    error NotAdmin();
    error NotOperator();
    error LastAdmin();
    error AlreadyAdmin();
    error AlreadyOperator();

    /// @notice Emitted when a new admin is added
    event NewAdmin(address indexed newAdminAddress, address indexed admin);

    /// @notice Emitted when a new operator is added
    event NewOperator(address indexed newOperatorAddress, address indexed admin);

    /// @notice Emitted when an admin is removed
    event RemovedAdmin(address indexed removedAdmin, address indexed admin);

    /// @notice Emitted when an operator is removed
    event RemovedOperator(address indexed removedOperator, address indexed admin);
}

interface IAuth is IAuthEE {
    function isAdmin(address) external view returns (bool);

    function isOperator(address) external view returns (bool);

    function addAdmin(address) external;

    function addOperator(address) external;

    function removeAdmin(address) external;

    function removeOperator(address) external;

    function renounceOperatorRole() external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

interface IFeesEE {
    /// @notice Thrown when fee exceeds the maximum allowed rate
    error FeeExceedsMaxRate();

    /// @notice Thrown when setting a fee rate above 100%
    error MaxFeeRateExceedsCeiling();

    /// @notice Emitted when a fee is charged
    event FeeCharged(address indexed receiver, uint256 amount);

    /// @notice Emitted when the fee receiver is updated
    event FeeReceiverUpdated(address indexed feeReceiver);

    /// @notice Emitted when the max fee rate is updated
    event MaxFeeRateUpdated(uint256 maxFeeRate);
}

abstract contract IFees is IFeesEE {
    function getFeeReceiver() public view virtual returns (address);

    function getMaxFeeRate() public view virtual returns (uint256);

    function validateFee(uint256 fee, uint256 cashValue) public view virtual;

    function _validateFeeWithMaxFeeRate(uint256 fee, uint256 cashValue, uint256 maxFeeRate) internal pure virtual;

    function _setFeeReceiver(address receiver) internal virtual;

    function _setMaxFeeRate(uint256 rate) internal virtual;
}

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

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /// @dev The allowance of Permit2 is fixed at infinity.
    error Permit2AllowanceIsFixedAtInfinity();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    /// If you need to use a different version, override `_versionHash`.
    bytes32 private constant _DEFAULT_VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /// @dev The canonical Permit2 address.
    /// For signature-based allowance granting for single transaction ERC20 `transferFrom`.
    /// Enabled by default. To disable, override `_givePermit2InfiniteAllowance()`.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        if (_givePermit2InfiniteAllowance()) {
            if (spender == _PERMIT2) return type(uint256).max;
        }
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && amount != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        // Code duplication is for zero-cost abstraction if possible.
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                if iszero(eq(caller(), _PERMIT2)) {
                    // Compute the allowance slot and load its value.
                    mstore(0x20, caller())
                    mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                    let allowanceSlot := keccak256(0x0c, 0x34)
                    let allowance_ := sload(allowanceSlot)
                    // If the allowance is not the maximum uint256 value.
                    if not(allowance_) {
                        // Revert if the amount to be transferred exceeds the allowance.
                        if gt(amount, allowance_) {
                            mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                            revert(0x1c, 0x04)
                        }
                        // Subtract and store the updated allowance.
                        sstore(allowanceSlot, sub(allowance_, amount))
                    }
                }
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let from_ := shl(96, from)
                // Compute the allowance slot and load its value.
                mstore(0x20, caller())
                mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
                let allowanceSlot := keccak256(0x0c, 0x34)
                let allowance_ := sload(allowanceSlot)
                // If the allowance is not the maximum uint256 value.
                if not(allowance_) {
                    // Revert if the amount to be transferred exceeds the allowance.
                    if gt(amount, allowance_) {
                        mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                        revert(0x1c, 0x04)
                    }
                    // Subtract and store the updated allowance.
                    sstore(allowanceSlot, sub(allowance_, amount))
                }
                // Compute the balance slot and load its value.
                mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
                let fromBalanceSlot := keccak256(0x0c, 0x20)
                let fromBalance := sload(fromBalanceSlot)
                // Revert if insufficient balance.
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated balance.
                sstore(fromBalanceSlot, sub(fromBalance, amount))
                // Compute the balance slot of `to`.
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x20)
                // Add and store the updated balance of `to`.
                // Will not overflow because the sum of all user balances
                // cannot exceed the maximum uint256 value.
                sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
                // Emit the {Transfer} event.
                mstore(0x20, amount)
                log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
            }
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev If you need a different value, override this function.
    function _versionHash() internal view virtual returns (bytes32 result) {
        result = _DEFAULT_VERSION_HASH;
    }

    /// @dev For inheriting contracts to increment the nonce.
    function _incrementNonce(address owner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            sstore(nonceSlot, add(1, sload(nonceSlot)))
        }
    }

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && value != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(value)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        bytes32 versionHash = _versionHash();
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0x00, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        bytes32 versionHash = _versionHash();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        if (_givePermit2InfiniteAllowance()) {
            if (spender == _PERMIT2) return; // Do nothing, as allowance is infinite.
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if not(allowance_) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        if (_givePermit2InfiniteAllowance()) {
            /// @solidity memory-safe-assembly
            assembly {
                // If `spender == _PERMIT2 && amount != type(uint256).max`.
                if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) {
                    mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`.
                    revert(0x1c, 0x04)
                }
            }
        }
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          PERMIT2                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether to fix the Permit2 contract's allowance at infinity.
    ///
    /// This value should be kept constant after contract initialization,
    /// or else the actual allowance values may not match with the {Approval} events.
    /// For best performance, return a compile-time constant for zero-cost abstraction.
    function _givePermit2InfiniteAllowance() internal view virtual returns (bool) {
        return true;
    }
}

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

/// @notice Simple ERC1155 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC1155.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol)
///
/// @dev Note:
/// - The ERC1155 standard allows for self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The transfer functions use the identity precompile (0x4)
///   to copy memory internally.
///
/// If you are overriding:
/// - Make sure all variables written to storage are properly cleaned
//    (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC1155 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The lengths of the input arrays are not the same.
    error ArrayLengthsMismatch();

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Only the token owner or an approved account can manage the tokens.
    error NotOwnerNorApproved();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC1155Receiver interface.
    error TransferToNonERC1155ReceiverImplementer();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` of token `id` is transferred
    /// from `from` to `to` by `operator`.
    event TransferSingle(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 id,
        uint256 amount
    );

    /// @dev Emitted when `amounts` of token `ids` are transferred
    /// from `from` to `to` by `operator`.
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] amounts
    );

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
    /// is updated to `value`. This event is not used in the base contract.
    /// You may need to emit this event depending on your URI logic.
    ///
    /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
    event URI(string value, uint256 indexed id);

    /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
    uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
        0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;

    /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
    uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
        0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `ownerSlotSeed` of a given owner is given by.
    /// ```
    ///     let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))
    /// ```
    ///
    /// The balance slot of `owner` is given by.
    /// ```
    ///     mstore(0x20, ownerSlotSeed)
    ///     mstore(0x00, id)
    ///     let balanceSlot := keccak256(0x00, 0x40)
    /// ```
    ///
    /// The operator approval slot of `owner` is given by.
    /// ```
    ///     mstore(0x20, ownerSlotSeed)
    ///     mstore(0x00, operator)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC1155 METADATA                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the URI for token `id`.
    ///
    /// You can either return the same templated URI for all token IDs,
    /// (e.g. "https://example.com/api/{id}.json"),
    /// or return a unique URI for each `id`.
    ///
    /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
    function uri(uint256 id) public view virtual returns (string memory);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          ERC1155                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of `id` owned by `owner`.
    function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
            mstore(0x14, owner)
            mstore(0x00, id)
            result := sload(keccak256(0x00, 0x40))
        }
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
            mstore(0x14, owner)
            mstore(0x00, operator)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits a {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
            mstore(0x14, caller())
            mstore(0x00, operator)
            sstore(keccak256(0x0c, 0x34), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            // forgefmt: disable-next-line
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
        }
    }

    /// @dev Transfers `amount` of `id` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `from` must have at least `amount` of `id`.
    /// - If the caller is not `from`,
    ///   it must be approved to manage the tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferSingle} event.
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) public virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
            let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
            mstore(0x20, fromSlotSeed)
            // Clear the upper 96 bits.
            from := shr(96, fromSlotSeed)
            to := shr(96, toSlotSeed)
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // If the caller is not `from`, do the authorization check.
            if iszero(eq(caller(), from)) {
                mstore(0x00, caller())
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Subtract and store the updated balance of `from`.
            {
                mstore(0x00, id)
                let fromBalanceSlot := keccak256(0x00, 0x40)
                let fromBalance := sload(fromBalanceSlot)
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                sstore(fromBalanceSlot, sub(fromBalance, amount))
            }
            // Increase and store the updated balance of `to`.
            {
                mstore(0x20, toSlotSeed)
                let toBalanceSlot := keccak256(0x00, 0x40)
                let toBalanceBefore := sload(toBalanceSlot)
                let toBalanceAfter := add(toBalanceBefore, amount)
                if lt(toBalanceAfter, toBalanceBefore) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceAfter)
            }
            // Emit a {TransferSingle} event.
            mstore(0x20, amount)
            log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, to, _single(id), _single(amount), data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Do the {onERC1155Received} check if `to` is a smart contract.
            if extcodesize(to) {
                // Prepare the calldata.
                let m := mload(0x40)
                // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                mstore(m, 0xf23a6e61)
                mstore(add(m, 0x20), caller())
                mstore(add(m, 0x40), from)
                mstore(add(m, 0x60), id)
                mstore(add(m, 0x80), amount)
                mstore(add(m, 0xa0), 0xa0)
                mstore(add(m, 0xc0), data.length)
                calldatacopy(add(m, 0xe0), data.offset, data.length)
                // Revert if the call reverts.
                if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) {
                    if returndatasize() {
                        // Bubble up the revert if the call reverts.
                        returndatacopy(m, 0x00, returndatasize())
                        revert(m, returndatasize())
                    }
                }
                // Load the returndata and compare it with the function selector.
                if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                    mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Transfers `amounts` of `ids` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `from` must have at least `amount` of `id`.
    /// - `ids` and `amounts` must have the same length.
    /// - If the caller is not `from`,
    ///   it must be approved to manage the tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferBatch} event.
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) public virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, to, ids, amounts, data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(eq(ids.length, amounts.length)) {
                mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                revert(0x1c, 0x04)
            }
            let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
            let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
            mstore(0x20, fromSlotSeed)
            // Clear the upper 96 bits.
            from := shr(96, fromSlotSeed)
            to := shr(96, toSlotSeed)
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // If the caller is not `from`, do the authorization check.
            if iszero(eq(caller(), from)) {
                mstore(0x00, caller())
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Loop through all the `ids` and update the balances.
            {
                for { let i := shl(5, ids.length) } i {} {
                    i := sub(i, 0x20)
                    let amount := calldataload(add(amounts.offset, i))
                    // Subtract and store the updated balance of `from`.
                    {
                        mstore(0x20, fromSlotSeed)
                        mstore(0x00, calldataload(add(ids.offset, i)))
                        let fromBalanceSlot := keccak256(0x00, 0x40)
                        let fromBalance := sload(fromBalanceSlot)
                        if gt(amount, fromBalance) {
                            mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(fromBalanceSlot, sub(fromBalance, amount))
                    }
                    // Increase and store the updated balance of `to`.
                    {
                        mstore(0x20, toSlotSeed)
                        let toBalanceSlot := keccak256(0x00, 0x40)
                        let toBalanceBefore := sload(toBalanceSlot)
                        let toBalanceAfter := add(toBalanceBefore, amount)
                        if lt(toBalanceAfter, toBalanceBefore) {
                            mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(toBalanceSlot, toBalanceAfter)
                    }
                }
            }
            // Emit a {TransferBatch} event.
            {
                let m := mload(0x40)
                // Copy the `ids`.
                mstore(m, 0x40)
                let n := shl(5, ids.length)
                mstore(add(m, 0x40), ids.length)
                calldatacopy(add(m, 0x60), ids.offset, n)
                // Copy the `amounts`.
                mstore(add(m, 0x20), add(0x60, n))
                let o := add(add(m, n), 0x60)
                mstore(o, ids.length)
                calldatacopy(add(o, 0x20), amounts.offset, n)
                // Do the emit.
                log4(m, add(add(n, n), 0x80), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to)
            }
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransferCalldata(from, to, ids, amounts, data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Do the {onERC1155BatchReceived} check if `to` is a smart contract.
            if extcodesize(to) {
                mstore(0x00, to) // Cache `to` to prevent stack too deep.
                let m := mload(0x40)
                // Prepare the calldata.
                // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                mstore(m, 0xbc197c81)
                mstore(add(m, 0x20), caller())
                mstore(add(m, 0x40), from)
                // Copy the `ids`.
                mstore(add(m, 0x60), 0xa0)
                let n := shl(5, ids.length)
                mstore(add(m, 0xc0), ids.length)
                calldatacopy(add(m, 0xe0), ids.offset, n)
                // Copy the `amounts`.
                mstore(add(m, 0x80), add(0xc0, n))
                let o := add(add(m, n), 0xe0)
                mstore(o, ids.length)
                calldatacopy(add(o, 0x20), amounts.offset, n)
                // Copy the `data`.
                mstore(add(m, 0xa0), add(add(0xe0, n), n))
                o := add(add(o, n), 0x20)
                mstore(o, data.length)
                calldatacopy(add(o, 0x20), data.offset, data.length)
                let nAll := add(0x104, add(data.length, add(n, n)))
                // Revert if the call reverts.
                if iszero(call(gas(), mload(0x00), 0, add(mload(0x40), 0x1c), nAll, m, 0x20)) {
                    if returndatasize() {
                        // Bubble up the revert if the call reverts.
                        returndatacopy(m, 0x00, returndatasize())
                        revert(m, returndatasize())
                    }
                }
                // Load the returndata and compare it with the function selector.
                if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                    mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Returns the amounts of `ids` for `owners.
    ///
    /// Requirements:
    /// - `owners` and `ids` must have the same length.
    function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
        public
        view
        virtual
        returns (uint256[] memory balances)
    {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(eq(ids.length, owners.length)) {
                mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                revert(0x1c, 0x04)
            }
            balances := mload(0x40)
            mstore(balances, ids.length)
            let o := add(balances, 0x20)
            let i := shl(5, ids.length)
            mstore(0x40, add(i, o))
            // Loop through all the `ids` and load the balances.
            for {} i {} {
                i := sub(i, 0x20)
                let owner := calldataload(add(owners.offset, i))
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)))
                mstore(0x00, calldataload(add(ids.offset, i)))
                mstore(add(o, i), sload(keccak256(0x00, 0x40)))
            }
        }
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` of `id` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferSingle} event.
    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(address(0), to, _single(id), _single(amount), data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            let to_ := shl(96, to)
            // Revert if `to` is the zero address.
            if iszero(to_) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Increase and store the updated balance of `to`.
            {
                mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                mstore(0x14, to)
                mstore(0x00, id)
                let toBalanceSlot := keccak256(0x00, 0x40)
                let toBalanceBefore := sload(toBalanceSlot)
                let toBalanceAfter := add(toBalanceBefore, amount)
                if lt(toBalanceAfter, toBalanceBefore) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceAfter)
            }
            // Emit a {TransferSingle} event.
            mstore(0x20, amount)
            log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(address(0), to, _single(id), _single(amount), data);
        }
        if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data);
    }

    /// @dev Mints `amounts` of `ids` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `ids` and `amounts` must have the same length.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferBatch} event.
    function _batchMint(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(address(0), to, ids, amounts, data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(eq(mload(ids), mload(amounts))) {
                mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                revert(0x1c, 0x04)
            }
            let to_ := shl(96, to)
            // Revert if `to` is the zero address.
            if iszero(to_) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Loop through all the `ids` and update the balances.
            {
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                    let amount := mload(add(amounts, i))
                    // Increase and store the updated balance of `to`.
                    {
                        mstore(0x00, mload(add(ids, i)))
                        let toBalanceSlot := keccak256(0x00, 0x40)
                        let toBalanceBefore := sload(toBalanceSlot)
                        let toBalanceAfter := add(toBalanceBefore, amount)
                        if lt(toBalanceAfter, toBalanceBefore) {
                            mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(toBalanceSlot, toBalanceAfter)
                    }
                }
            }
            // Emit a {TransferBatch} event.
            {
                let m := mload(0x40)
                // Copy the `ids`.
                mstore(m, 0x40)
                let n := add(0x20, shl(5, mload(ids)))
                let o := add(m, 0x40)
                pop(staticcall(gas(), 4, ids, n, o, n))
                // Copy the `amounts`.
                mstore(add(m, 0x20), add(0x40, returndatasize()))
                o := add(o, returndatasize())
                n := add(0x20, shl(5, mload(amounts)))
                pop(staticcall(gas(), 4, amounts, n, o, n))
                n := sub(add(o, returndatasize()), m)
                // Do the emit.
                log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
            }
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(address(0), to, ids, amounts, data);
        }
        if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_burn(address(0), from, id, amount)`.
    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        _burn(address(0), from, id, amount);
    }

    /// @dev Destroys `amount` of `id` from `from`.
    ///
    /// Requirements:
    /// - `from` must have at least `amount` of `id`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the tokens of `from`.
    ///
    /// Emits a {TransferSingle} event.
    function _burn(address by, address from, uint256 id, uint256 amount) internal virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, address(0), _single(id), _single(amount), "");
        }
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
            // If `by` is not the zero address, and not equal to `from`,
            // check if it is approved to manage all the tokens of `from`.
            if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) {
                mstore(0x00, by)
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Decrease and store the updated balance of `from`.
            {
                mstore(0x00, id)
                let fromBalanceSlot := keccak256(0x00, 0x40)
                let fromBalance := sload(fromBalanceSlot)
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                sstore(fromBalanceSlot, sub(fromBalance, amount))
            }
            // Emit a {TransferSingle} event.
            mstore(0x20, amount)
            log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, address(0), _single(id), _single(amount), "");
        }
    }

    /// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`.
    function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts)
        internal
        virtual
    {
        _batchBurn(address(0), from, ids, amounts);
    }

    /// @dev Destroys `amounts` of `ids` from `from`.
    ///
    /// Requirements:
    /// - `ids` and `amounts` must have the same length.
    /// - `from` must have at least `amounts` of `ids`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the tokens of `from`.
    ///
    /// Emits a {TransferBatch} event.
    function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts)
        internal
        virtual
    {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, address(0), ids, amounts, "");
        }
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(eq(mload(ids), mload(amounts))) {
                mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                revert(0x1c, 0x04)
            }
            let from_ := shl(96, from)
            mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
            // If `by` is not the zero address, and not equal to `from`,
            // check if it is approved to manage all the tokens of `from`.
            let by_ := shl(96, by)
            if iszero(or(iszero(by_), eq(by_, from_))) {
                mstore(0x00, by)
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Loop through all the `ids` and update the balances.
            {
                for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                    let amount := mload(add(amounts, i))
                    // Decrease and store the updated balance of `from`.
                    {
                        mstore(0x00, mload(add(ids, i)))
                        let fromBalanceSlot := keccak256(0x00, 0x40)
                        let fromBalance := sload(fromBalanceSlot)
                        if gt(amount, fromBalance) {
                            mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(fromBalanceSlot, sub(fromBalance, amount))
                    }
                }
            }
            // Emit a {TransferBatch} event.
            {
                let m := mload(0x40)
                // Copy the `ids`.
                mstore(m, 0x40)
                let n := add(0x20, shl(5, mload(ids)))
                let o := add(m, 0x40)
                pop(staticcall(gas(), 4, ids, n, o, n))
                // Copy the `amounts`.
                mstore(add(m, 0x20), add(0x40, returndatasize()))
                o := add(o, returndatasize())
                n := add(0x20, shl(5, mload(amounts)))
                pop(staticcall(gas(), 4, amounts, n, o, n))
                n := sub(add(o, returndatasize()), m)
                // Do the emit.
                log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
            }
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, address(0), ids, amounts, "");
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL APPROVAL FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits a {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
            mstore(0x14, by)
            mstore(0x00, operator)
            sstore(keccak256(0x0c, 0x34), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            let m := shr(96, not(0))
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`.
    function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data)
        internal
        virtual
    {
        _safeTransfer(address(0), from, to, id, amount, data);
    }

    /// @dev Transfers `amount` of `id` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `from` must have at least `amount` of `id`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155Received}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferSingle} event.
    function _safeTransfer(
        address by,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            let to_ := shl(96, to)
            // Revert if `to` is the zero address.
            if iszero(to_) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
            // If `by` is not the zero address, and not equal to `from`,
            // check if it is approved to manage all the tokens of `from`.
            let by_ := shl(96, by)
            if iszero(or(iszero(by_), eq(by_, from_))) {
                mstore(0x00, by)
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Subtract and store the updated balance of `from`.
            {
                mstore(0x00, id)
                let fromBalanceSlot := keccak256(0x00, 0x40)
                let fromBalance := sload(fromBalanceSlot)
                if gt(amount, fromBalance) {
                    mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                    revert(0x1c, 0x04)
                }
                sstore(fromBalanceSlot, sub(fromBalance, amount))
            }
            // Increase and store the updated balance of `to`.
            {
                mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                let toBalanceSlot := keccak256(0x00, 0x40)
                let toBalanceBefore := sload(toBalanceSlot)
                let toBalanceAfter := add(toBalanceBefore, amount)
                if lt(toBalanceAfter, toBalanceBefore) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceAfter)
            }
            // Emit a {TransferSingle} event.
            mstore(0x20, amount)
            // forgefmt: disable-next-line
            log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, to, _single(id), _single(amount), data);
        }
        if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data);
    }

    /// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`.
    function _safeBatchTransfer(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        _safeBatchTransfer(address(0), from, to, ids, amounts, data);
    }

    /// @dev Transfers `amounts` of `ids` from `from` to `to`.
    ///
    /// Requirements:
    /// - `to` cannot be the zero address.
    /// - `ids` and `amounts` must have the same length.
    /// - `from` must have at least `amounts` of `ids`.
    /// - If `by` is not the zero address, it must be either `from`,
    ///   or approved to manage the tokens of `from`.
    /// - If `to` refers to a smart contract, it must implement
    ///   {ERC1155-onERC1155BatchReceived}, which is called upon a batch transfer.
    ///
    /// Emits a {TransferBatch} event.
    function _safeBatchTransfer(
        address by,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        if (_useBeforeTokenTransfer()) {
            _beforeTokenTransfer(from, to, ids, amounts, data);
        }
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(eq(mload(ids), mload(amounts))) {
                mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                revert(0x1c, 0x04)
            }
            let from_ := shl(96, from)
            let to_ := shl(96, to)
            // Revert if `to` is the zero address.
            if iszero(to_) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_)
            let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_)
            mstore(0x20, fromSlotSeed)
            // If `by` is not the zero address, and not equal to `from`,
            // check if it is approved to manage all the tokens of `from`.
            let by_ := shl(96, by)
            if iszero(or(iszero(by_), eq(by_, from_))) {
                mstore(0x00, by)
                if iszero(sload(keccak256(0x0c, 0x34))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Loop through all the `ids` and update the balances.
            {
                for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } {
                    let amount := mload(add(amounts, i))
                    // Subtract and store the updated balance of `from`.
                    {
                        mstore(0x20, fromSlotSeed)
                        mstore(0x00, mload(add(ids, i)))
                        let fromBalanceSlot := keccak256(0x00, 0x40)
                        let fromBalance := sload(fromBalanceSlot)
                        if gt(amount, fromBalance) {
                            mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(fromBalanceSlot, sub(fromBalance, amount))
                    }
                    // Increase and store the updated balance of `to`.
                    {
                        mstore(0x20, toSlotSeed)
                        let toBalanceSlot := keccak256(0x00, 0x40)
                        let toBalanceBefore := sload(toBalanceSlot)
                        let toBalanceAfter := add(toBalanceBefore, amount)
                        if lt(toBalanceAfter, toBalanceBefore) {
                            mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                            revert(0x1c, 0x04)
                        }
                        sstore(toBalanceSlot, toBalanceAfter)
                    }
                }
            }
            // Emit a {TransferBatch} event.
            {
                let m := mload(0x40)
                // Copy the `ids`.
                mstore(m, 0x40)
                let n := add(0x20, shl(5, mload(ids)))
                let o := add(m, 0x40)
                pop(staticcall(gas(), 4, ids, n, o, n))
                // Copy the `amounts`.
                mstore(add(m, 0x20), add(0x40, returndatasize()))
                o := add(o, returndatasize())
                n := add(0x20, shl(5, mload(amounts)))
                pop(staticcall(gas(), 4, amounts, n, o, n))
                n := sub(add(o, returndatasize()), m)
                // Do the emit.
                log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
            }
        }
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, to, ids, amounts, data);
        }
        if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
    /// This is to help the compiler avoid producing dead bytecode.
    function _useBeforeTokenTransfer() internal view virtual returns (bool) {
        return false;
    }

    /// @dev Hook that is called before any token transfer.
    /// This includes minting and burning, as well as batched variants.
    ///
    /// The same hook is called on both single and batched variants.
    /// For single transfers, the length of the `id` and `amount` arrays are 1.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /// @dev Override this function to return true if `_afterTokenTransfer` is used.
    /// This is to help the compiler avoid producing dead bytecode.
    function _useAfterTokenTransfer() internal view virtual returns (bool) {
        return false;
    }

    /// @dev Hook that is called after any token transfer.
    /// This includes minting and burning, as well as batched variants.
    ///
    /// The same hook is called on both single and batched variants.
    /// For single transfers, the length of the `id` and `amount` arrays are 1.
    function _afterTokenTransfer(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Helper for calling the `_afterTokenTransfer` hook.
    /// This is to help the compiler avoid producing dead bytecode.
    function _afterTokenTransferCalldata(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) private {
        if (_useAfterTokenTransfer()) {
            _afterTokenTransfer(from, to, ids, amounts, data);
        }
    }

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC1155Received(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            // `onERC1155Received(address,address,uint256,uint256,bytes)`.
            mstore(m, 0xf23a6e61)
            mstore(add(m, 0x20), caller())
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), amount)
            mstore(add(m, 0xa0), 0xa0)
            let n := mload(data)
            mstore(add(m, 0xc0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it with the function selector.
            if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC1155BatchReceived(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
            mstore(m, 0xbc197c81)
            mstore(add(m, 0x20), caller())
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            // Copy the `ids`.
            mstore(add(m, 0x60), 0xa0)
            let n := add(0x20, shl(5, mload(ids)))
            let o := add(m, 0xc0)
            pop(staticcall(gas(), 4, ids, n, o, n))
            // Copy the `amounts`.
            let s := add(0xa0, returndatasize())
            mstore(add(m, 0x80), s)
            o := add(o, returndatasize())
            n := add(0x20, shl(5, mload(amounts)))
            pop(staticcall(gas(), 4, amounts, n, o, n))
            // Copy the `data`.
            mstore(add(m, 0xa0), add(s, returndatasize()))
            o := add(o, returndatasize())
            n := add(0x20, mload(data))
            pop(staticcall(gas(), 4, data, n, o, n))
            n := sub(add(o, returndatasize()), add(m, 0x1c))
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(m, 0x00, returndatasize())
                    revert(m, returndatasize())
                }
            }
            // Load the returndata and compare it with the function selector.
            if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns `x` in an array with a single element.
    function _single(uint256 x) private pure returns (uint256[] memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(0x40, add(result, 0x40))
            mstore(result, 1)
            mstore(add(result, 0x20), x)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

abstract contract IAssets {
    function getCollateral() public virtual returns (address);

    function getCtf() public virtual returns (address);

    function getCtfCollateral() public virtual returns (address);

    function getOutcomeTokenFactory() public virtual returns (address);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { OrderStatus, Order, Side } from "../libraries/Structs.sol";

interface ITradingEE {
    error OrderAlreadyFilled();
    error MakingGtRemaining();
    error NotCrossing();
    error TooLittleTokensReceived();
    error ComplementaryFillExceedsTakerFill();
    error MismatchedTokenIds();
    error MismatchedArrayLengths();
    error NoMakerOrders();
    error ZeroMakerAmount();
    error FeeExceedsProceeds();

    /// @notice Emitted when an order is filled
    event OrderFilled(
        bytes32 indexed orderHash,
        address indexed maker,
        address indexed taker,
        Side side,
        uint256 tokenId,
        uint256 makerAmountFilled,
        uint256 takerAmountFilled,
        uint256 fee,
        bytes32 builder,
        bytes32 metadata
    );

    /// @notice Emitted when a set of orders is matched
    event OrdersMatched(
        bytes32 indexed takerOrderHash,
        address indexed takerOrderMaker,
        Side side,
        uint256 tokenId,
        uint256 makerAmountFilled,
        uint256 takerAmountFilled
    );
}

interface ITrading is ITradingEE { }

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.34;

interface IUserPausableEE {
    error UserIsPaused();
    error UserAlreadyPaused();
    error ExceedsMaxPauseInterval();

    /// @notice Emitted when the user pause block interval is updated
    event UserPauseBlockIntervalUpdated(uint256 oldInterval, uint256 newInterval);

    /// @notice Emitted when a user pauses their account
    event UserPaused(address indexed user, uint256 effectivePauseBlock);

    /// @notice Emitted when a user unpauses their account
    event UserUnpaused(address indexed user);
}

abstract contract IUserPausable is IUserPausableEE {
    function isUserPaused(address usr) public view virtual returns (bool);

    function pauseUser() external virtual;

    function unpauseUser() external virtual;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

library CalculatorHelper {
    function calculateTakingAmount(uint256 makingAmount, uint256 makerAmount, uint256 takerAmount)
        internal
        pure
        returns (uint256)
    {
        return (makingAmount * takerAmount) / makerAmount;
    }
}

// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity >=0.5.1;

// forked from Gnosis Condtional Tokens
library CTHelpers {
    /// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for
    /// the question.
    /// @param oracle The account assigned to report the result for the prepared condition.
    /// @param questionId An identifier for the question to be answered by the oracle.
    /// @param outcomeSlotCount The number of outcome slots which should be used for this condition.
    /// Must not exceed 256.
    function getConditionId(address oracle, bytes32 questionId, uint256 outcomeSlotCount)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount));
    }

    uint256 constant P = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
    uint256 constant B = 3;

    function sqrt(uint256 x) private pure returns (uint256 y) {
        uint256 p = P;
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            // add chain generated via https://crypto.stackexchange.com/q/27179/71252
            // and transformed to the following program:

            // x=1; y=x+x; z=y+y; z=z+z; y=y+z; x=x+y; y=y+x; z=y+y; t=z+z; t=z+t; t=t+t;
            // t=t+t; z=z+t; x=x+z; z=x+x; z=z+z; y=y+z; z=y+y; z=z+z; z=z+z; z=y+z; x=x+z;
            // z=x+x; z=z+z; z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; z=y+y; t=z+z;
            // t=t+t; t=t+t; z=z+t; x=x+z; y=y+x; z=y+y; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
            // z=x+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; t=z+z; t=t+t; t=z+t; t=y+t; t=t+t;
            // t=t+t; t=t+t; t=t+t; z=z+t; x=x+z; z=x+x; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z;
            // t=z+z; t=z+t; w=t+t; w=w+w; w=w+w; w=w+w; w=w+w; t=t+w; z=z+t; x=x+z; y=y+x;
            // z=y+y; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; z=z+z; y=y+z; z=y+y;
            // z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; x=x+z; y=y+x; x=x+y; y=y+x; z=y+y; z=z+z;
            // z=y+z; x=x+z; z=x+x; z=x+z; y=y+z; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; z=y+z;
            // z=z+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x; t=z+z; t=t+t; t=z+t;
            // t=x+t; t=t+t; t=t+t; t=t+t; t=t+t; z=z+t; y=y+z; x=x+y; y=y+x; x=x+y; z=x+x;
            // z=x+z; z=z+z; z=z+z; z=z+z; z=x+z; y=y+z; z=y+y; z=y+z; z=z+z; x=x+z; z=x+x;
            // z=x+z; y=y+z; x=x+y; z=x+x; z=z+z; y=y+z; x=x+y; z=x+x; y=y+z; x=x+y; y=y+x;
            // z=y+y; z=y+z; x=x+z; y=y+x; z=y+y; z=y+z; z=z+z; z=z+z; x=x+z; z=x+x; z=z+z;
            // z=z+z; z=x+z; y=y+z; x=x+y; z=x+x; t=x+z; t=t+t; t=t+t; z=z+t; y=y+z; z=y+y;
            // x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; y=y+x; z=y+y; t=y+z; z=y+t; z=z+z; z=z+z;
            // z=t+z; x=x+z; y=y+x; x=x+y; y=y+x; x=x+y; z=x+x; z=x+z; y=y+z; x=x+y; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x; x=x+x;
            // x=x+x; x=x+x; x=x+x; x=x+x; res=y+x
            // res == (P + 1) // 4

            y := mulmod(x, x, p)
            {
                let z := mulmod(y, y, p)
                z := mulmod(z, z, p)
                y := mulmod(y, z, p)
                x := mulmod(x, y, p)
                y := mulmod(y, x, p)
                z := mulmod(y, y, p)
                {
                    let t := mulmod(z, z, p)
                    t := mulmod(z, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    z := mulmod(z, t, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(y, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    t := mulmod(z, z, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    z := mulmod(z, t, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    z := mulmod(x, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    t := mulmod(z, z, p)
                    t := mulmod(t, t, p)
                    t := mulmod(z, t, p)
                    t := mulmod(y, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    z := mulmod(z, t, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    z := mulmod(z, z, p)
                    t := mulmod(z, z, p)
                    t := mulmod(z, t, p)
                    {
                        let w := mulmod(t, t, p)
                        w := mulmod(w, w, p)
                        w := mulmod(w, w, p)
                        w := mulmod(w, w, p)
                        w := mulmod(w, w, p)
                        t := mulmod(t, w, p)
                    }
                    z := mulmod(z, t, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    z := mulmod(x, z, p)
                    z := mulmod(z, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(z, z, p)
                    z := mulmod(y, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    t := mulmod(z, z, p)
                    t := mulmod(t, t, p)
                    t := mulmod(z, t, p)
                    t := mulmod(x, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    z := mulmod(z, t, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    z := mulmod(x, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    z := mulmod(y, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    x := mulmod(x, z, p)
                    z := mulmod(x, x, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(x, z, p)
                    y := mulmod(y, z, p)
                    x := mulmod(x, y, p)
                    z := mulmod(x, x, p)
                    t := mulmod(x, z, p)
                    t := mulmod(t, t, p)
                    t := mulmod(t, t, p)
                    z := mulmod(z, t, p)
                    y := mulmod(y, z, p)
                    z := mulmod(y, y, p)
                    x := mulmod(x, z, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    x := mulmod(x, y, p)
                    y := mulmod(y, x, p)
                    z := mulmod(y, y, p)
                    t := mulmod(y, z, p)
                    z := mulmod(y, t, p)
                    z := mulmod(z, z, p)
                    z := mulmod(z, z, p)
                    z := mulmod(t, z, p)
                }
                x := mulmod(x, z, p)
                y := mulmod(y, x, p)
                x := mulmod(x, y, p)
                y := mulmod(y, x, p)
                x := mulmod(x, y, p)
                z := mulmod(x, x, p)
                z := mulmod(x, z, p)
                y := mulmod(y, z, p)
            }
            x := mulmod(x, y, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            x := mulmod(x, x, p)
            y := mulmod(y, x, p)
        }
    }

    /// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
    /// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if
    /// there's no parent.
    /// @param conditionId Condition ID of the outcome collection to combine with the parent outcome
    /// collection.
    /// @param indexSet Index set of the outcome collection to combine with the parent outcome
    /// collection.
    function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint256 indexSet)
        internal
        view
        returns (bytes32)
    {
        uint256 x1 = uint256(keccak256(abi.encodePacked(conditionId, indexSet)));
        bool odd = x1 >> 255 != 0;
        uint256 y1;
        uint256 yy;
        do {
            x1 = addmod(x1, 1, P);
            yy = addmod(mulmod(x1, mulmod(x1, x1, P), P), B, P);
            y1 = sqrt(yy);
        } while (mulmod(y1, y1, P) != yy);
        if ((odd && y1 % 2 == 0) || (!odd && y1 % 2 == 1)) y1 = P - y1;

        uint256 x2 = uint256(parentCollectionId);
        if (x2 != 0) {
            odd = x2 >> 254 != 0;
            x2 = (x2 << 2) >> 2;
            yy = addmod(mulmod(x2, mulmod(x2, x2, P), P), B, P);
            uint256 y2 = sqrt(yy);
            if ((odd && y2 % 2 == 0) || (!odd && y2 % 2 == 1)) y2 = P - y2;
            require(mulmod(y2, y2, P) == yy, "invalid parent collection ID");

            (bool success, bytes memory ret) = address(6).staticcall(abi.encode(x1, y1, x2, y2));
            require(success, "ecadd failed");
            (x1, y1) = abi.decode(ret, (uint256, uint256));
        }

        if (y1 % 2 == 1) x1 ^= 1 << 254;

        return bytes32(x1);
    }

    /// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs
    /// are used as the ERC-1155 ID for this contract.
    /// @param collateralToken Collateral token which backs the position.
    /// @param collectionId ID of the outcome collection associated with this position.
    function getPositionId(address collateralToken, bytes32 collectionId) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(collateralToken, collectionId)));
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { EIP712 } from "@solady/src/utils/EIP712.sol";

import { IHashing } from "../interfaces/IHashing.sol";

import { Order, ORDER_TYPEHASH } from "../libraries/Structs.sol";

abstract contract Hashing is EIP712, IHashing {
    string internal constant DOMAIN_NAME = "Polymarket CTF Exchange";
    string internal constant DOMAIN_VERSION = "2";

    constructor() EIP712() { }

    function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
        return (DOMAIN_NAME, DOMAIN_VERSION);
    }

    /// @notice Computes the hash for an order
    /// @param order - The order to be hashed
    function hashOrder(Order memory order) public view override returns (bytes32) {
        return _hashTypedData(_createStructHash(order));
    }

    /// @notice Creates the struct hash for an order
    /// @dev This does not include the signature; the signature is downstream of this hash
    function _createStructHash(Order memory order) internal pure returns (bytes32) {
        bytes32 result;
        assembly {
            let prev := mload(sub(order, 0x20))
            mstore(sub(order, 0x20), ORDER_TYPEHASH)
            result := keccak256(sub(order, 0x20), 0x180)
            mstore(sub(order, 0x20), prev)
        }
        return result;
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.34;

import { IUserPausable } from "../interfaces/IUserPausable.sol";

/// @title UserPausable
/// @notice Mixin to allow users to pause and unpause their accounts
/// @author Polymarket
abstract contract UserPausable is IUserPausable {
    /// @notice Maximum allowed value for the user pause block interval
    uint256 internal constant MAX_PAUSE_BLOCK_INTERVAL = 302_400;

    /// @notice The number of blocks after which a user's pause becomes effective
    uint256 public userPauseBlockInterval = 100;

    /// @notice A mapping of users to the block number at which their pause becomes effective
    mapping(address => uint256) public userPausedBlockAt;

    /// @notice Checks if a user is currently paused
    /// @param user - The user address to check
    function isUserPaused(address user) public view override returns (bool) {
        uint256 blockPausedAt = userPausedBlockAt[user];
        return blockPausedAt > 0 && block.number >= blockPausedAt;
    }

    /// @notice Allows a user to pause their account
    function pauseUser() external override {
        require(userPausedBlockAt[msg.sender] == 0, UserAlreadyPaused());
        uint256 blockPausedAt = block.number + userPauseBlockInterval;
        userPausedBlockAt[msg.sender] = blockPausedAt;
        emit UserPaused(msg.sender, blockPausedAt);
    }

    /// @notice Allows a user to unpause their account
    function unpauseUser() external override {
        userPausedBlockAt[msg.sender] = 0;
        emit UserUnpaused(msg.sender);
    }

    /// @notice Sets the block interval after which a user's pause becomes effective
    /// @param blockInterval - The new block interval
    function _setUserPauseBlockInterval(uint256 blockInterval) internal {
        require(blockInterval <= MAX_PAUSE_BLOCK_INTERVAL, ExceedsMaxPauseInterval());
        uint256 oldInterval = userPauseBlockInterval;
        userPauseBlockInterval = blockInterval;
        emit UserPauseBlockIntervalUpdated(oldInterval, blockInterval);
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { ERC20 } from "@solady/src/tokens/ERC20.sol";
import { ERC1155 } from "@solady/src/tokens/ERC1155.sol";

import { IAssets } from "../interfaces/IAssets.sol";
import { IAssetOperations } from "../interfaces/IAssetOperations.sol";
import { IConditionalTokens } from "../interfaces/IConditionalTokens.sol";

import { TransferHelper } from "../libraries/TransferHelper.sol";

import { Assets } from "./Assets.sol";

/// @title Asset Operations
/// @notice Operations on the CTF and Collateral assets
abstract contract AssetOperations is Assets, IAssetOperations {
    bytes32 public constant PARENT_COLLECTION_ID = bytes32(0);

    function _getBalance(uint256 tokenId) internal view override returns (uint256) {
        if (tokenId == 0) return ERC20(getCollateral()).balanceOf(address(this));
        return ERC1155(getCtf()).balanceOf(address(this), tokenId);
    }

    function _transfer(address from, address to, uint256 id, uint256 value) internal override {
        if (id == 0) return _transferCollateral(from, to, value);
        return _transferCTF(from, to, id, value);
    }

    function _transferCollateral(address from, address to, uint256 value) internal {
        address token = getCollateral();
        if (from == address(this)) TransferHelper._transferERC20(token, to, value);
        else TransferHelper._transferFromERC20(token, from, to, value);
    }

    function _transferCTF(address from, address to, uint256 id, uint256 value) internal {
        TransferHelper._transferFromERC1155(getCtf(), from, to, id, value);
    }

    function _mint(bytes32 conditionId, uint256 amount) internal override {
        uint256[] memory partition = _getPartition();
        IConditionalTokens(getOutcomeTokenFactory())
            .splitPosition(getCollateral(), PARENT_COLLECTION_ID, conditionId, partition, amount);
    }

    function _merge(bytes32 conditionId, uint256 amount) internal override {
        uint256[] memory partition = _getPartition();
        IConditionalTokens(getOutcomeTokenFactory())
            .mergePositions(getCollateral(), PARENT_COLLECTION_ID, conditionId, partition, amount);
    }

    /// @dev Returns the binary partition [1, 2] for CTF operations
    function _getPartition() internal pure returns (uint256[] memory partition) {
        assembly ("memory-safe") {
            // Allocate memory for array: 32 bytes for length + 64 bytes for 2 elements
            partition := mload(0x40)
            mstore(partition, 2) // length = 2
            mstore(add(partition, 0x20), 1) // partition[0] = 1
            mstore(add(partition, 0x40), 2) // partition[1] = 2
            mstore(0x40, add(partition, 0x60)) // Update free memory pointer
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { Side } from "../libraries/Structs.sol";

/// @title Events
abstract contract Events {
    /*//////////////////////////////////////////////////////////////
                        EVENT TOPIC CONSTANTS
    //////////////////////////////////////////////////////////////*/

    /// @dev keccak256("OrderFilled(bytes32,address,address,uint8,uint256,uint256,uint256,uint256,bytes32,bytes32)")
    bytes32 private constant _ORDER_FILLED_TOPIC =
        keccak256("OrderFilled(bytes32,address,address,uint8,uint256,uint256,uint256,uint256,bytes32,bytes32)");

    /// @dev keccak256("OrdersMatched(bytes32,address,uint8,uint256,uint256,uint256)")
    bytes32 private constant _ORDERS_MATCHED_TOPIC =
        keccak256("OrdersMatched(bytes32,address,uint8,uint256,uint256,uint256)");

    /// @dev keccak256("FeeCharged(address,uint256)")
    bytes32 private constant _FEE_CHARGED_TOPIC = keccak256("FeeCharged(address,uint256)");

    /*//////////////////////////////////////////////////////////////
                            STRUCTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Parameters for the OrderFilled event
    struct OrderFilledParams {
        bytes32 orderHash; // 0x00
        address maker; // 0x20
        address taker; // 0x40
        Side side; // 0x60
        uint256 tokenId; // 0x80
        uint256 makerAmountFilled; // 0xa0
        uint256 takerAmountFilled; // 0xc0
        uint256 fee; // 0xe0
        bytes32 builder; // 0x100
        bytes32 metadata; // 0x120
    }

    /*//////////////////////////////////////////////////////////////
                        EMIT FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @dev Emits the OrderFilled event
    /// @dev OrderFilled(orderHash, maker, taker, side, tokenId, makerAmountFilled, takerAmountFilled, fee, builder,
    /// metadata)
    function _emitOrderFilledEvent(OrderFilledParams memory p) internal {
        bytes32 t = _ORDER_FILLED_TOPIC;
        assembly ("memory-safe") {
            let m := mload(0x40)
            mstore(m, mload(add(p, 0x60))) // side
            mstore(add(m, 0x20), mload(add(p, 0x80))) // tokenId
            mstore(add(m, 0x40), mload(add(p, 0xa0))) // makerAmountFilled
            mstore(add(m, 0x60), mload(add(p, 0xc0))) // takerAmountFilled
            mstore(add(m, 0x80), mload(add(p, 0xe0))) // fee
            mstore(add(m, 0xa0), mload(add(p, 0x100))) // builder
            mstore(add(m, 0xc0), mload(add(p, 0x120))) // metadata
            log4(m, 0xe0, t, mload(p), mload(add(p, 0x20)), mload(add(p, 0x40)))
        }
    }

    /// @dev Emits OrdersMatched event
    /// @dev OrdersMatched(takerOrderHash, takerOrderMaker, side, tokenId, makerAmountFilled, takerAmountFilled)
    function _emitTakerFilledEvents(OrderFilledParams memory p) internal {
        _emitOrderFilledEvent(p);
        bytes32 t = _ORDERS_MATCHED_TOPIC;
        assembly ("memory-safe") {
            let m := mload(0x40)
            mstore(m, mload(add(p, 0x60))) // side
            mstore(add(m, 0x20), mload(add(p, 0x80))) // tokenId
            mstore(add(m, 0x40), mload(add(p, 0xa0))) // makerAmountFilled
            mstore(add(m, 0x60), mload(add(p, 0xc0))) // takerAmountFilled
            log3(m, 0x80, t, mload(p), mload(add(p, 0x20)))
        }
    }

    /// @dev Emits the FeeCharged event
    /// @dev FeeCharged(receiver, amount)
    function _emitFeeCharged(address receiver, uint256 amount) internal {
        bytes32 t = _FEE_CHARGED_TOPIC;
        assembly ("memory-safe") {
            mstore(0x00, amount)
            log2(0x00, 0x20, t, receiver)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

interface IPausableEE {
    error Paused();

    event TradingPaused(address indexed pauser);

    event TradingUnpaused(address indexed pauser);
}

abstract contract IPausable is IPausableEE {
    function _pauseTrading() internal virtual;

    function _unpauseTrading() internal virtual;
}

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

/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
///   This is for more safety by default.
///   Use the `tryRecover` variants if you need to get the zero address back
///   upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The order of the secp256k1 elliptic curve.
    uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;

    /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
    uint256 private constant _HALF_N_PLUS_1 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The signature is invalid.
    error InvalidSignature();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    RECOVERY OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { continue }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function recoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            } {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { continue }
                mstore(0x00, hash)
                result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                if returndatasize() { break }
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            if iszero(returndatasize()) {
                mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   TRY-RECOVER OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // WARNING!
    // These functions will NOT revert upon recovery failure.
    // Instead, they will return the zero address upon recovery failure.
    // It is critical that the returned address is NEVER compared against
    // a zero address (e.g. an uninitialized address variable).

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecover(bytes32 hash, bytes memory signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch mload(signature)
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { break }
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
    function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let m := mload(0x40) } 1 {} {
                switch signature.length
                case 64 {
                    let vs := calldataload(add(signature.offset, 0x20))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, calldataload(signature.offset)) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                    calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                }
                default { break }
                mstore(0x00, hash)
                pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
                mstore(0x60, 0) // Restore the zero slot.
                // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                result := mload(xor(0x60, returndatasize()))
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the EIP-2098 short form signature defined by `r` and `vs`.
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, add(shr(255, vs), 27)) // `v`.
            mstore(0x40, r)
            mstore(0x60, shr(1, shl(1, vs))) // `s`.
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Recovers the signer's address from a message digest `hash`,
    /// and the signature defined by `v`, `r`, `s`.
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (address result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x00, hash)
            mstore(0x20, and(v, 0xff))
            mstore(0x40, r)
            mstore(0x60, s)
            pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
            mstore(0x60, 0) // Restore the zero slot.
            // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
            result := mload(xor(0x60, returndatasize()))
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CANONICAL HASH FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // The following functions return the hash of the signature in its canonicalized format,
    // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
    // If `s` is greater than `N / 2` then it will be converted to `N - s`
    // and the `v` value will be flipped.
    // If the signature has an invalid length, or if `v` is invalid,
    // a uniquely corrupt hash will be returned.
    // These functions are useful for "poor-mans-VRF".

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let l := mload(signature)
            for {} 1 {} {
                mstore(0x00, mload(add(signature, 0x20))) // `r`.
                let s := mload(add(signature, 0x40))
                let v := mload(add(signature, 0x41))
                if eq(l, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }

            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(l, 64), 2)) {
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHashCalldata(bytes calldata signature)
        internal
        pure
        returns (bytes32 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                mstore(0x00, calldataload(signature.offset)) // `r`.
                let s := calldataload(add(signature.offset, 0x20))
                let v := calldataload(add(signature.offset, 0x21))
                if eq(signature.length, 64) {
                    v := add(shr(255, s), 27)
                    s := shr(1, shl(1, s))
                }
                if iszero(lt(s, _HALF_N_PLUS_1)) {
                    v := xor(v, 7)
                    s := sub(N, s)
                }
                mstore(0x21, v)
                mstore(0x20, s)
                result := keccak256(0x00, 0x41)
                mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                break
            }
            // If the length is neither 64 nor 65, return a uniquely corrupted hash.
            if iszero(lt(sub(signature.length, 64), 2)) {
                calldatacopy(mload(0x40), signature.offset, signature.length)
                // `bytes4(keccak256("InvalidSignatureLength"))`.
                result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
            }
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            let v := add(shr(255, vs), 27)
            let s := shr(1, shl(1, vs))
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the canonical hash of `signature`.
    function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, r) // `r`.
            if iszero(lt(s, _HALF_N_PLUS_1)) {
                v := xor(v, 7)
                s := sub(N, s)
            }
            mstore(0x21, v)
            mstore(0x20, s)
            result := keccak256(0x00, 0x41)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

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

/// @notice Signature verification helper that supports both ECDSA signatures from EOAs
/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol)
///
/// @dev Note:
/// - The signature checking functions use the ecrecover precompile (0x1).
/// - The `bytes memory signature` variants use the identity precompile (0x4)
///   to copy memory internally.
/// - Unlike ECDSA signatures, contract signatures are revocable.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
///   See: https://eips.ethereum.org/EIPS/eip-2098
///   This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT use signatures as unique identifiers:
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
///   EIP-712 also enables readable signing of typed data for better user safety.
/// This implementation does NOT check if a signature is non-malleable.
library SignatureCheckerLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               SIGNATURE CHECKING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    switch mload(signature)
                    case 64 {
                        let vs := mload(add(signature, 0x40))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                        mstore(0x60, mload(add(signature, 0x40))) // `s`.
                    }
                    default { break }
                    mstore(0x00, hash)
                    mstore(0x40, mload(add(signature, 0x20))) // `r`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                // Copy the `signature` over.
                let n := add(0x20, mload(signature))
                let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
                isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
                isValid := and(eq(mload(d), f), and(isValid, copied))
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    switch signature.length
                    case 64 {
                        let vs := calldataload(add(signature.offset, 0x20))
                        mstore(0x20, add(shr(255, vs), 27)) // `v`.
                        mstore(0x40, calldataload(signature.offset)) // `r`.
                        mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    }
                    case 65 {
                        mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                        calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`.
                    }
                    default { break }
                    mstore(0x00, hash)
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), signature.length)
                // Copy the `signature` over.
                calldatacopy(add(m, 0x64), signature.offset, signature.length)
                isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    mstore(0x00, hash)
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x40, r) // `r`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
                mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
                isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`.
    /// If `signer.code.length == 0`, then validate with `ecrecover`, else
    /// it will validate with ERC1271 on `signer`.
    function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        if (signer == address(0)) return isValid;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            for {} 1 {} {
                if iszero(extcodesize(signer)) {
                    mstore(0x00, hash)
                    mstore(0x20, and(v, 0xff)) // `v`.
                    mstore(0x40, r) // `r`.
                    mstore(0x60, s) // `s`.
                    let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                    isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                    mstore(0x60, 0) // Restore the zero slot.
                    mstore(0x40, m) // Restore the free memory pointer.
                    break
                }
                let f := shl(224, 0x1626ba7e)
                mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m, 0x04), hash)
                let d := add(m, 0x24)
                mstore(d, 0x40) // The offset of the `signature` in the calldata.
                mstore(add(m, 0x44), 65) // Length of the signature.
                mstore(add(m, 0x64), r) // `r`.
                mstore(add(m, 0x84), s) // `s`.
                mstore8(add(m, 0xa4), v) // `v`.
                isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
                isValid := and(eq(mload(d), f), isValid)
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ERC1271 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: These ERC1271 operations do NOT have an ECDSA fallback.

    /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            // Copy the `signature` over.
            let n := add(0x20, mload(signature))
            let copied := staticcall(gas(), 4, signature, n, add(m, 0x44), n)
            isValid := staticcall(gas(), signer, m, add(returndatasize(), 0x44), d, 0x20)
            isValid := and(eq(mload(d), f), and(isValid, copied))
        }
    }

    /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNowCalldata(
        address signer,
        bytes32 hash,
        bytes calldata signature
    ) internal view returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), signature.length)
            // Copy the `signature` over.
            calldatacopy(add(m, 0x64), signature.offset, signature.length)
            isValid := staticcall(gas(), signer, m, add(signature.length, 0x64), d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

    /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`.
            mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`.
            isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

    /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash`
    /// for an ERC1271 `signer` contract.
    function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s)
        internal
        view
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let f := shl(224, 0x1626ba7e)
            mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
            mstore(add(m, 0x04), hash)
            let d := add(m, 0x24)
            mstore(d, 0x40) // The offset of the `signature` in the calldata.
            mstore(add(m, 0x44), 65) // Length of the signature.
            mstore(add(m, 0x64), r) // `r`.
            mstore(add(m, 0x84), s) // `s`.
            mstore8(add(m, 0xa4), v) // `v`.
            isValid := staticcall(gas(), signer, m, 0xa5, d, 0x20)
            isValid := and(eq(mload(d), f), isValid)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     ERC6492 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: These ERC6492 operations now include an ECDSA fallback at the very end.
    // The calldata variants are excluded for brevity.

    /// @dev Returns whether `signature` is valid for `hash`.
    /// If the signature is postfixed with the ERC6492 magic number, it will attempt to
    /// deploy / prepare the `signer` smart account before doing a regular ERC1271 check.
    /// Note: This function is NOT reentrancy safe.
    /// The verifier must be deployed.
    /// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
    /// See: https://gist.github.com/Vectorized/011d6becff6e0a73e42fe100f8d7ef04
    /// With a dedicated verifier, this function is safe to use in contracts
    /// that have been granted special permissions.
    function isValidERC6492SignatureNowAllowSideEffects(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal returns (bool isValid) {
        /// @solidity memory-safe-assembly
        assembly {
            function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
                let m_ := mload(0x40)
                let f_ := shl(224, 0x1626ba7e)
                mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m_, 0x04), hash_)
                let d_ := add(m_, 0x24)
                mstore(d_, 0x40) // The offset of the `signature` in the calldata.
                let n_ := add(0x20, mload(signature_))
                let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
                _isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
                _isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
            }
            let noCode := iszero(extcodesize(signer))
            let n := mload(signature)
            for {} 1 {} {
                if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
                    if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
                    break
                }
                if iszero(noCode) {
                    let o := add(signature, 0x20) // Signature bytes.
                    isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
                    if isValid { break }
                }
                let m := mload(0x40)
                mstore(m, signer)
                mstore(add(m, 0x20), hash)
                pop(
                    call(
                        gas(), // Remaining gas.
                        0x0000bc370E4DC924F427d84e2f4B9Ec81626ba7E, // Non-reverting verifier.
                        0, // Send zero ETH.
                        m, // Start of memory.
                        add(returndatasize(), 0x40), // Length of calldata in memory.
                        staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
                        0x00 // Length of returndata to write.
                    )
                )
                isValid := returndatasize()
                break
            }
            // Do `ecrecover` fallback if `noCode && !isValid`.
            for {} gt(noCode, isValid) {} {
                switch n
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { break }
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /// @dev Returns whether `signature` is valid for `hash`.
    /// If the signature is postfixed with the ERC6492 magic number, it will attempt
    /// to use a reverting verifier to deploy / prepare the `signer` smart account
    /// and do a `isValidSignature` check via the reverting verifier.
    /// Note: This function is reentrancy safe.
    /// The reverting verifier must be deployed.
    /// Otherwise, the function will return false if `signer` is not yet deployed / prepared.
    /// See: https://gist.github.com/Vectorized/846a474c855eee9e441506676800a9ad
    function isValidERC6492SignatureNow(address signer, bytes32 hash, bytes memory signature)
        internal
        returns (bool isValid)
    {
        /// @solidity memory-safe-assembly
        assembly {
            function callIsValidSignature(signer_, hash_, signature_) -> _isValid {
                let m_ := mload(0x40)
                let f_ := shl(224, 0x1626ba7e)
                mstore(m_, f_) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
                mstore(add(m_, 0x04), hash_)
                let d_ := add(m_, 0x24)
                mstore(d_, 0x40) // The offset of the `signature` in the calldata.
                let n_ := add(0x20, mload(signature_))
                let copied_ := staticcall(gas(), 4, signature_, n_, add(m_, 0x44), n_)
                _isValid := staticcall(gas(), signer_, m_, add(returndatasize(), 0x44), d_, 0x20)
                _isValid := and(eq(mload(d_), f_), and(_isValid, copied_))
            }
            let noCode := iszero(extcodesize(signer))
            let n := mload(signature)
            for {} 1 {} {
                if iszero(eq(mload(add(signature, n)), mul(0x6492, div(not(isValid), 0xffff)))) {
                    if iszero(noCode) { isValid := callIsValidSignature(signer, hash, signature) }
                    break
                }
                if iszero(noCode) {
                    let o := add(signature, 0x20) // Signature bytes.
                    isValid := callIsValidSignature(signer, hash, add(o, mload(add(o, 0x40))))
                    if isValid { break }
                }
                let m := mload(0x40)
                mstore(m, signer)
                mstore(add(m, 0x20), hash)
                let willBeZeroIfRevertingVerifierExists :=
                    call(
                        gas(), // Remaining gas.
                        0x00007bd799e4A591FeA53f8A8a3E9f931626Ba7e, // Reverting verifier.
                        0, // Send zero ETH.
                        m, // Start of memory.
                        add(returndatasize(), 0x40), // Length of calldata in memory.
                        staticcall(gas(), 4, add(signature, 0x20), n, add(m, 0x40), n), // 1.
                        0x00 // Length of returndata to write.
                    )
                isValid := gt(returndatasize(), willBeZeroIfRevertingVerifierExists)
                break
            }
            // Do `ecrecover` fallback if `noCode && !isValid`.
            for {} gt(noCode, isValid) {} {
                switch n
                case 64 {
                    let vs := mload(add(signature, 0x40))
                    mstore(0x20, add(shr(255, vs), 27)) // `v`.
                    mstore(0x60, shr(1, shl(1, vs))) // `s`.
                }
                case 65 {
                    mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                    mstore(0x60, mload(add(signature, 0x40))) // `s`.
                }
                default { break }
                let m := mload(0x40)
                mstore(0x00, hash)
                mstore(0x40, mload(add(signature, 0x20))) // `r`.
                let recovered := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
                isValid := gt(returndatasize(), shl(96, xor(signer, recovered)))
                mstore(0x60, 0) // Restore the zero slot.
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an Ethereum Signed Message, created from a `hash`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, hash) // Store into scratch space for keccak256.
            mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
            result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
        }
    }

    /// @dev Returns an Ethereum Signed Message, created from `s`.
    /// This produces a hash corresponding to the one signed with the
    /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
    /// JSON-RPC method as part of EIP-191.
    /// Note: Supports lengths of `s` up to 999999 bytes.
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let sLength := mload(s)
            let o := 0x20
            mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded.
            mstore(0x00, 0x00)
            // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
            for { let temp := sLength } 1 {} {
                o := sub(o, 1)
                mstore8(o, add(48, mod(temp, 10)))
                temp := div(temp, 10)
                if iszero(temp) { break }
            }
            let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
            // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
            mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
            result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
            mstore(s, sLength) // Restore the length.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   EMPTY CALLDATA HELPERS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns an empty calldata bytes.
    function emptySignature() internal pure returns (bytes calldata signature) {
        /// @solidity memory-safe-assembly
        assembly {
            signature.length := 0
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { Order } from "../libraries/Structs.sol";

interface ISignaturesEE {
    error InvalidSignature();

    /// @notice Emitted when an order is preapproved
    event OrderPreapproved(bytes32 indexed orderHash);

    /// @notice Emitted when a preapproval is invalidated
    event OrderPreapprovalInvalidated(bytes32 indexed orderHash);
}

abstract contract ISignatures is ISignaturesEE {
    function validateOrderSignature(bytes32 orderHash, Order memory order) public view virtual;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { PolySafeLib } from "../libraries/PolySafeLib.sol";
import { PolyProxyLib } from "../libraries/PolyProxyLib.sol";

interface IPolyProxyFactory {
    function getImplementation() external view returns (address);
}

interface IPolySafeFactory {
    function masterCopy() external view returns (address);
}

abstract contract PolyFactoryHelper {
    /// @notice The Polymarket Proxy Wallet Factory Contract
    address internal immutable proxyFactory;
    /// @notice The Polymarket Proxy Wallet Implementation Contract
    address internal immutable proxyImplementation;
    /// @notice The Polymarket Gnosis Safe Factory Contract
    address internal immutable safeFactory;
    /// @notice The Polymarket Gnosis Safe Implementation Contract
    address internal immutable safeImplementation;
    /// @notice Pre-computed keccak256 of the safe proxy creation code with the implementation
    bytes32 internal immutable safeBytecodeHash;

    constructor(address _proxyFactory, address _safeFactory) {
        proxyFactory = _proxyFactory;
        safeFactory = _safeFactory;

        proxyImplementation = IPolyProxyFactory(_proxyFactory).getImplementation();

        address _safeImpl = IPolySafeFactory(_safeFactory).masterCopy();
        safeImplementation = _safeImpl;
        safeBytecodeHash = PolySafeLib.computeBytecodeHash(_safeImpl);
    }

    /// @notice Gets the Proxy factory address
    function getProxyFactory() public view returns (address) {
        return proxyFactory;
    }

    /// @notice Gets the Safe factory address
    function getSafeFactory() public view returns (address) {
        return safeFactory;
    }

    /// @notice Gets the Proxy implementation address
    function getProxyImplementation() public view returns (address) {
        return proxyImplementation;
    }

    /// @notice Gets the Safe implementation address
    function getSafeImplementation() public view returns (address) {
        return safeImplementation;
    }

    /// @notice Gets the Polymarket proxy wallet address for an address
    /// @param _addr    - The address that owns the proxy wallet
    function getProxyWalletAddress(address _addr) public view returns (address) {
        return PolyProxyLib.getProxyWalletAddress(_addr, proxyImplementation, proxyFactory);
    }

    /// @notice Gets the Polymarket Gnosis Safe address for an address
    /// @param _addr    - The Safe owner/signer address used to derive the Safe address
    function getSafeWalletAddress(address _addr) public view returns (address) {
        return PolySafeLib.getSafeWalletAddress(_addr, safeBytecodeHash, safeFactory);
    }
}

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

/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  CONSTANTS AND IMMUTABLES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 internal constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
    /// This is only used in `_hashTypedDataSansChainId`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
        0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;

    /// @dev `keccak256("EIP712Domain(string name,string version)")`.
    /// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
        0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
    /// This is only used in `_hashTypedDataSansVerifyingContract`.
    bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
        0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;

    uint256 private immutable _cachedThis;
    uint256 private immutable _cachedChainId;
    bytes32 private immutable _cachedNameHash;
    bytes32 private immutable _cachedVersionHash;
    bytes32 private immutable _cachedDomainSeparator;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CONSTRUCTOR                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Cache the hashes for cheaper runtime gas costs.
    /// In the case of upgradeable contracts (i.e. proxies),
    /// or if the chain id changes due to a hard fork,
    /// the domain separator will be seamlessly calculated on-the-fly.
    constructor() {
        _cachedThis = uint256(uint160(address(this)));
        _cachedChainId = block.chainid;

        string memory name;
        string memory version;
        if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
        bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
        bytes32 versionHash =
            _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
        _cachedNameHash = nameHash;
        _cachedVersionHash = versionHash;

        bytes32 separator;
        if (!_domainNameAndVersionMayChange()) {
            /// @solidity memory-safe-assembly
            assembly {
                let m := mload(0x40) // Load the free memory pointer.
                mstore(m, _DOMAIN_TYPEHASH)
                mstore(add(m, 0x20), nameHash)
                mstore(add(m, 0x40), versionHash)
                mstore(add(m, 0x60), chainid())
                mstore(add(m, 0x80), address())
                separator := keccak256(m, 0xa0)
            }
        }
        _cachedDomainSeparator = separator;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   FUNCTIONS TO OVERRIDE                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Please override this function to return the domain name and version.
    /// ```
    ///     function _domainNameAndVersion()
    ///         internal
    ///         pure
    ///         virtual
    ///         returns (string memory name, string memory version)
    ///     {
    ///         name = "Solady";
    ///         version = "1";
    ///     }
    /// ```
    ///
    /// Note: If the returned result may change after the contract has been deployed,
    /// you must override `_domainNameAndVersionMayChange()` to return true.
    function _domainNameAndVersion()
        internal
        view
        virtual
        returns (string memory name, string memory version);

    /// @dev Returns if `_domainNameAndVersion()` may change
    /// after the contract has been deployed (i.e. after the constructor).
    /// Default: false.
    function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HASHING OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _domainSeparator() internal view virtual returns (bytes32 separator) {
        if (_domainNameAndVersionMayChange()) {
            separator = _buildDomainSeparator();
        } else {
            separator = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
        }
    }

    /// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
    /// given `structHash`, as defined in
    /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
    ///
    /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
    /// ```
    ///     bytes32 digest = _hashTypedData(keccak256(abi.encode(
    ///         keccak256("Mail(address to,string contents)"),
    ///         mailTo,
    ///         keccak256(bytes(mailContents))
    ///     )));
    ///     address signer = ECDSA.recover(digest, signature);
    /// ```
    function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
        // We will use `digest` to store the domain separator to save a bit of gas.
        if (_domainNameAndVersionMayChange()) {
            digest = _buildDomainSeparator();
        } else {
            digest = _cachedDomainSeparator;
            if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
        }
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the digest.
            mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
            mstore(0x1a, digest) // Store the domain separator.
            mstore(0x3a, structHash) // Store the struct hash.
            digest := keccak256(0x18, 0x42)
            // Restore the part of the free memory slot that was overwritten.
            mstore(0x3a, 0)
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID.
    /// Included for the niche use case of cross-chain workflows.
    function _hashTypedDataSansChainId(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, address())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of cross-chain and multi-verifier workflows.
    function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
    /// Included for the niche use case of multi-verifier workflows.
    function _hashTypedDataSansVerifyingContract(bytes32 structHash)
        internal
        view
        virtual
        returns (bytes32 digest)
    {
        (string memory name, string memory version) = _domainNameAndVersion();
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
            mstore(0x20, keccak256(add(name, 0x20), mload(name)))
            mstore(0x40, keccak256(add(version, 0x20), mload(version)))
            mstore(0x60, chainid())
            // Compute the digest.
            mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
            mstore(0x00, 0x1901) // Store "\x19\x01".
            mstore(0x40, structHash) // Store the struct hash.
            digest := keccak256(0x1e, 0x42)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    EIP-5267 OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev See: https://eips.ethereum.org/EIPS/eip-5267
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        fields = hex"0f"; // `0b01111`.
        (name, version) = _domainNameAndVersion();
        chainId = block.chainid;
        verifyingContract = address(this);
        salt = salt; // `bytes32(0)`.
        extensions = extensions; // `new uint256[](0)`.
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the EIP-712 domain separator.
    function _buildDomainSeparator() private view returns (bytes32 separator) {
        // We will use `separator` to store the name hash to save a bit of gas.
        bytes32 versionHash;
        if (_domainNameAndVersionMayChange()) {
            (string memory name, string memory version) = _domainNameAndVersion();
            separator = keccak256(bytes(name));
            versionHash = keccak256(bytes(version));
        } else {
            separator = _cachedNameHash;
            versionHash = _cachedVersionHash;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Load the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), separator) // Name hash.
            mstore(add(m, 0x40), versionHash)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            separator := keccak256(m, 0xa0)
        }
    }

    /// @dev Returns if the cached domain separator has been invalidated.
    function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
        uint256 cachedChainId = _cachedChainId;
        uint256 cachedThis = _cachedThis;
        /// @solidity memory-safe-assembly
        assembly {
            result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
        }
    }
}

File 29 of 36 : IHashing.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { Order } from "../libraries/Structs.sol";

abstract contract IHashing {
    function hashOrder(Order memory order) public view virtual returns (bytes32);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

abstract contract IAssetOperations {
    function _getBalance(uint256 tokenId) internal virtual returns (uint256);

    function _transfer(address from, address to, uint256 id, uint256 value) internal virtual;

    function _mint(bytes32 conditionId, uint256 amount) internal virtual;

    function _merge(bytes32 conditionId, uint256 amount) internal virtual;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

/// @title IConditionalTokens
/// @notice Interface for the Gnosis ConditionalTokensFramework:
/// https://github.com/gnosis/conditional-tokens-contracts/blob/master/contracts/ConditionalTokens.sol
/// @notice references to IERC20 are replaced by address
interface IConditionalTokens {
    function payoutNumerators(bytes32 conditionId, uint256 index) external view returns (uint256);

    function payoutDenominator(bytes32 conditionId) external view returns (uint256);

    function prepareCondition(address oracle, bytes32 questionId, uint256 outcomeSlotCount) external;

    function reportPayouts(bytes32 questionId, uint256[] calldata payouts) external;

    function splitPosition(
        address collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata partition,
        uint256 amount
    ) external;

    function mergePositions(
        address collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata partition,
        uint256 amount
    ) external;

    function redeemPositions(
        address collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata indexSets
    ) external;

    function getOutcomeSlotCount(bytes32 conditionId) external view returns (uint256);

    function getConditionId(address oracle, bytes32 questionId, uint256 outcomeSlotCount)
        external
        pure
        returns (bytes32);

    function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint256 indexSet)
        external
        view
        returns (bytes32);

    function getPositionId(address collateralToken, bytes32 collectionId) external pure returns (uint256);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { ERC1155 } from "@solady/src/tokens/ERC1155.sol";
import { SafeTransferLib } from "@solady/src/utils/SafeTransferLib.sol";

/// @title TransferHelper
/// @notice Helper method to transfer tokens
library TransferHelper {
    /// @notice Transfers tokens from msg.sender to a recipient
    /// @param token    - The contract address of the token which will be transferred
    /// @param to       - The recipient of the transfer
    /// @param amount   - The amount to be transferred
    function _transferERC20(address token, address to, uint256 amount) internal {
        SafeTransferLib.safeTransfer(token, to, amount);
    }

    /// @notice Transfers tokens from the targeted address to the given destination
    /// @param token    - The contract address of the token to be transferred
    /// @param from     - The originating address from which the tokens will be transferred
    /// @param to       - The destination address of the transfer
    /// @param amount   - The amount to be transferred
    function _transferFromERC20(address token, address from, address to, uint256 amount) internal {
        SafeTransferLib.safeTransferFrom(token, from, to, amount);
    }

    /// @notice Transfer an ERC1155 token
    /// @param token    - The contract address of the token to be transferred
    /// @param from     - The originating address from which the tokens will be transferred
    /// @param to       - The destination address of the transfer
    /// @param id       - The tokenId of the token to be transferred
    /// @param amount   - The amount to be transferred
    function _transferFromERC1155(address token, address from, address to, uint256 id, uint256 amount) internal {
        ERC1155(token).safeTransferFrom(from, to, id, amount, "");
    }

    /// @notice Transfers a set of ERC1155 tokens
    /// @param token    - The contract address of the token to be transferred
    /// @param from     - The originating address from which the tokens will be transferred
    /// @param to       - The destination address of the transfer
    /// @param ids      - The tokenId of the token to be transferred
    /// @param amounts  - The amount to be transferred
    function _batchTransferFromERC1155(
        address token,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal {
        ERC1155(token).safeBatchTransferFrom(from, to, ids, amounts, "");
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { Create2Lib } from "./Create2Lib.sol";

/// @title PolySafeLib
/// @notice Helper library to compute Polymarket gnosis safe addresses
library PolySafeLib {
    /// @notice Gets the Polymarket Gnosis safe address for a signer
    /// @param signer       - Address of the signer
    /// @param bytecodeHash - Pre-computed keccak256 of the safe proxy creation code
    /// @param deployer     - Address of the deployer contract
    function getSafeWalletAddress(address signer, bytes32 bytecodeHash, address deployer)
        internal
        pure
        returns (address safe)
    {
        bytes32 salt = _getSalt(signer);
        safe = Create2Lib.computeCreate2Address(deployer, bytecodeHash, salt);
    }

    /// @notice Computes the keccak256 hash of the proxy creation code with the given master copy
    /// @param implementation - Address of the Gnosis Safe master copy
    function computeBytecodeHash(address implementation) internal pure returns (bytes32) {
        return _computeCreationCodeHash(implementation);
    }

    /// @notice Computes the salt for CREATE2 from a signer address
    /// @param signer - Address of the signer
    /// @return salt - The keccak256 hash of the ABI-encoded signer address
    function _getSalt(address signer) internal pure returns (bytes32 salt) {
        assembly ("memory-safe") {
            mstore(0x00, signer)
            salt := keccak256(0x00, 0x20)
        }
    }

    /// @notice Computes the keccak256 hash of the proxy creation code with the implementation address appended
    /// @dev Writes 369 bytes of proxyCreationCode + 32 bytes abi.encode(implementation) = 401 bytes, then hashes
    function _computeCreationCodeHash(address implementation) internal pure returns (bytes32 hash) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            // Write proxyCreationCode (369 bytes) in 32-byte chunks
            mstore(ptr, 0x608060405234801561001057600080fd5b506040516101713803806101718339)
            mstore(add(ptr, 32), 0x8101604081905261002f916100b9565b6001600160a01b038116610094576040)
            mstore(add(ptr, 64), 0x5162461bcd60e51b815260206004820152602260248201527f496e76616c6964)
            mstore(add(ptr, 96), 0x2073696e676c65746f6e20616464726573732070726f76696460448201526119)
            mstore(add(ptr, 128), 0x5960f21b606482015260840160405180910390fd5b600080546001600160a01b)
            mstore(add(ptr, 160), 0x0319166001600160a01b03929092169190911790556100e7565b600060208284)
            mstore(add(ptr, 192), 0x0312156100ca578081fd5b81516001600160a01b03811681146100e0578182fd)
            mstore(add(ptr, 224), 0x5b9392505050565b607c806100f56000396000f3fe6080604052600080546001)
            mstore(add(ptr, 256), 0x600160a01b0316813563530ca43760e11b1415602857808252602082f35b3682)
            mstore(add(ptr, 288), 0x833781823684845af490503d82833e806041573d82fd5b503d81f3fea2646970)
            mstore(add(ptr, 320), 0x66735822122015938e3bf2c49f5df5c1b7f9569fa85cc5d6f3074bb258a2dc0c)
            // Last 17 bytes of proxyCreationCode (right-padded zeros get overwritten by implementation below)
            mstore(add(ptr, 352), 0x7e299bc9e33664736f6c63430008040033000000000000000000000000000000)

            // Write abi.encode(implementation) at offset 369
            mstore(add(ptr, 369), implementation)

            // Hash 401 bytes
            hash := keccak256(ptr, 401)
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

import { Create2Lib } from "./Create2Lib.sol";

/// @notice Helper library to compute polymarket proxy wallet addresses
library PolyProxyLib {
    /// @notice Gets the polymarket proxy address for a signer
    /// @param signer - Address of the signer
    function getProxyWalletAddress(address signer, address implementation, address deployer)
        internal
        pure
        returns (address proxyWallet)
    {
        return _computeCreate2Address(deployer, implementation, _getSalt(signer));
    }

    /// @notice Computes the salt for CREATE2 from a signer address
    /// @param signer - Address of the signer
    /// @return salt - The keccak256 hash of the packed signer address
    function _getSalt(address signer) internal pure returns (bytes32 salt) {
        assembly ("memory-safe") {
            // Store address at 0x20 (right-aligned: first 12 bytes are zero, last 20 bytes are the address)
            mstore(0x20, signer)
            // Hash 20 bytes starting at position 44 (32 + 12)
            salt := keccak256(44, 20)
        }
    }

    function _computeCreate2Address(address from, address target, bytes32 salt) internal pure returns (address result) {
        bytes32 bytecodeHash = _computeCreationCodeHash(from, target);
        result = Create2Lib.computeCreate2Address(from, bytecodeHash, salt);
    }

    function _computeCreationCodeHash(address deployer, address target) internal pure returns (bytes32 hash) {
        assembly ("memory-safe") {
            // Allocate from free memory pointer: only 167 bytes (no length field needed)
            let ptr := mload(0x40)

            // Write buffer section (99 bytes)
            // Bytes 0-31: first byte 0x3d, then 31 zero bytes
            mstore(ptr, 0x3d00000000000000000000000000000000000000000000000000000000000000)
            // Bytes 1-32: OR 12-byte prefix (with trailing zeros) with 20-byte deployer
            mstore(add(ptr, 1), or(0x3d606380380380913d393d730000000000000000000000000000000000000000, deployer))
            // Bytes 33-64: 19 non-zero bytes of bytecode, then 13 zero bytes
            mstore(add(ptr, 33), 0x5af4602a57600080fd5b602d8060366000396000000000000000000000000000)
            // Bytes 65-96: OR 12-byte prefix (with leading zeros) with 20-byte target
            mstore(add(ptr, 52), or(0x00f3363d3d373d3d3d363d730000000000000000000000000000000000000000, target))
            // Bytes 96-127: remaining bytes of buffer + start of consData
            mstore(add(ptr, 84), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)

            // Write consData section (68 bytes)
            mstore(add(ptr, 99), 0x52e831dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(ptr, 103), 0x0000000000000000000000000000000000000000000000000000000000000020)
            mstore(add(ptr, 135), 0x0000000000000000000000000000000000000000000000000000000000000000)

            // Hash the creation code: 167 bytes total
            hash := keccak256(ptr, 167)
        }
    }
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The ERC20 `totalSupply` query has failed.
    error TotalSupplyQueryFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /// @dev The Permit2 approve operation has failed.
    error Permit2ApproveFailed();

    /// @dev The Permit2 lockdown operation has failed.
    error Permit2LockdownFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /// @dev The canonical address of the `SELFDESTRUCT` ETH mover.
    /// See: https://gist.github.com/Vectorized/1cb8ad4cf393b1378e08f23f79bd99fa
    /// [Etherscan](https://etherscan.io/address/0x00000000000073c48c8055bD43D1A53799176f0D)
    address internal constant ETH_MOVER = 0x00000000000073c48c8055bD43D1A53799176f0D;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Force transfers ETH to `to`, without triggering the fallback (if any).
    /// This method attempts to use a separate contract to send via `SELFDESTRUCT`,
    /// and upon failure, deploys a minimal vault to accrue the ETH.
    function safeMoveETH(address to, uint256 amount) internal returns (address vault) {
        /// @solidity memory-safe-assembly
        assembly {
            to := shr(96, shl(96, to)) // Clean upper 96 bits.
            for { let mover := ETH_MOVER } iszero(eq(to, address())) {} {
                let selfBalanceBefore := selfbalance()
                if or(lt(selfBalanceBefore, amount), eq(to, mover)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                if extcodesize(mover) {
                    let balanceBefore := balance(to) // Check via delta, in case `SELFDESTRUCT` is bricked.
                    mstore(0x00, to)
                    pop(call(gas(), mover, amount, 0x00, 0x20, codesize(), 0x00))
                    // If `address(to).balance >= amount + balanceBefore`, skip vault workflow.
                    if iszero(lt(balance(to), add(amount, balanceBefore))) { break }
                    // Just in case `SELFDESTRUCT` is changed to not revert and do nothing.
                    if lt(selfBalanceBefore, selfbalance()) { invalid() }
                }
                let m := mload(0x40)
                // If the mover is missing or bricked, deploy a minimal vault
                // that withdraws all ETH to `to` when being called only by `to`.
                // forgefmt: disable-next-item
                mstore(add(m, 0x20), 0x33146025575b600160005260206000f35b3d3d3d3d47335af1601a5760003dfd)
                mstore(m, or(to, shl(160, 0x6035600b3d3960353df3fe73)))
                // Compute and store the bytecode hash.
                mstore8(0x00, 0xff) // Write the prefix.
                mstore(0x35, keccak256(m, 0x40))
                mstore(0x01, shl(96, address())) // Deployer.
                mstore(0x15, 0) // Salt.
                vault := keccak256(0x00, 0x55)
                pop(call(gas(), vault, amount, codesize(), 0x00, codesize(), 0x00))
                // The vault returns a single word on success. Failure reverts with empty data.
                if iszero(returndatasize()) {
                    if iszero(create2(0, m, 0x40, 0)) { revert(codesize(), codesize()) } // For gas estimation.
                }
                mstore(0x40, m) // Restore the free memory pointer.
                break
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                success := lt(or(iszero(extcodesize(token)), returndatasize()), success)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
            if iszero(and(eq(mload(0x00), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    mstore(0x34, 0) // Store 0 for the `amount`.
                    mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                    pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                    mstore(0x34, amount) // Store back the original `amount`.
                    // Retry the approval, reverting upon failure.
                    success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    if iszero(and(eq(mload(0x00), 1), success)) {
                        // Check the `extcodesize` again just in case the token selfdestructs lol.
                        if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                            mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                            revert(0x1c, 0x04)
                        }
                    }
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Performs a `token.balanceOf(account)` check.
    /// `implemented` denotes whether the `token` does not implement `balanceOf`.
    /// `amount` is zero if the `token` does not implement `balanceOf`.
    function checkBalanceOf(address token, address account)
        internal
        view
        returns (bool implemented, uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            implemented :=
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                )
            amount := mul(mload(0x20), implemented)
        }
    }

    /// @dev Returns the total supply of the `token`.
    /// Reverts if the token does not exist or does not implement `totalSupply()`.
    function totalSupply(address token) internal view returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x18160ddd) // `totalSupply()`.
            if iszero(
                and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20))
            ) {
                mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`.
                revert(0x1c, 0x04)
            }
            result := mload(0x00)
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(
                and(
                    call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00),
                    lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists.
                )
            ) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(
                        add(m, 0x94),
                        lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    )
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `amount != 0` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero( // Revert if token does not have code, or if the call fails.
            mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`.
    function permit2Approve(address token, address spender, uint160 amount, uint48 expiration)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let addressMask := shr(96, not(0))
            let m := mload(0x40)
            mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`.
            mstore(add(m, 0x20), and(addressMask, token))
            mstore(add(m, 0x40), and(addressMask, spender))
            mstore(add(m, 0x60), and(addressMask, amount))
            mstore(add(m, 0x80), and(0xffffffffffff, expiration))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Revokes an approval for `token` and `spender` for `address(this)`.
    function permit2Lockdown(address token, address spender) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0xcc53287f) // `Permit2.lockdown`.
            mstore(add(m, 0x20), 0x20) // Offset of the `approvals`.
            mstore(add(m, 0x40), 1) // `approvals.length`.
            mstore(add(m, 0x60), shr(96, shl(96, token)))
            mstore(add(m, 0x80), shr(96, shl(96, spender)))
            if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) {
                mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity <0.9.0;

/// @notice Shared helper for computing CREATE2 addresses
library Create2Lib {
    /// @notice Computes the CREATE2 address from deployer, bytecodeHash, and salt
    /// @param deployer - The deployer contract address
    /// @param bytecodeHash - The keccak256 hash of the creation code
    /// @param salt - The CREATE2 salt
    function computeCreate2Address(address deployer, bytes32 bytecodeHash, bytes32 salt)
        internal
        pure
        returns (address result)
    {
        assembly ("memory-safe") {
            // Get free memory pointer
            let ptr := mload(0x40)

            // Construct CREATE2 formula: 0xff ++ deployer (20 bytes) ++ salt (32 bytes) ++ bytecodeHash (32 bytes)
            // Byte 0: 0xff, Bytes 1-20: deployer address
            mstore(ptr, or(0xff00000000000000000000000000000000000000000000000000000000000000, shl(88, deployer)))
            // Bytes 21-52: salt
            mstore(add(ptr, 21), salt)
            // Bytes 53-84: bytecodeHash
            mstore(add(ptr, 53), bytecodeHash)

            // Compute keccak256 of 85 bytes and extract address
            result := and(keccak256(ptr, 85), 0xffffffffffffffffffffffffffffffffffffffff)
        }
    }
}

Settings
{
  "remappings": [
    "@solady/=lib/solady/",
    "@forge-std/=lib/forge-std/",
    "@ctf-exchange-v2/src/=src/",
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"ctf","type":"address"},{"internalType":"address","name":"ctfCollateral","type":"address"},{"internalType":"address","name":"outcomeTokenFactory","type":"address"},{"internalType":"address","name":"proxyFactory","type":"address"},{"internalType":"address","name":"safeFactory","type":"address"},{"internalType":"address","name":"feeReceiver","type":"address"}],"internalType":"struct ExchangeInitParams","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyAdmin","type":"error"},{"inputs":[],"name":"AlreadyOperator","type":"error"},{"inputs":[],"name":"ComplementaryFillExceedsTakerFill","type":"error"},{"inputs":[],"name":"ExceedsMaxPauseInterval","type":"error"},{"inputs":[],"name":"FeeExceedsMaxRate","type":"error"},{"inputs":[],"name":"FeeExceedsProceeds","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"LastAdmin","type":"error"},{"inputs":[],"name":"MakingGtRemaining","type":"error"},{"inputs":[],"name":"MaxFeeRateExceedsCeiling","type":"error"},{"inputs":[],"name":"MismatchedArrayLengths","type":"error"},{"inputs":[],"name":"MismatchedTokenIds","type":"error"},{"inputs":[],"name":"NoMakerOrders","type":"error"},{"inputs":[],"name":"NotAdmin","type":"error"},{"inputs":[],"name":"NotCrossing","type":"error"},{"inputs":[],"name":"NotOperator","type":"error"},{"inputs":[],"name":"OrderAlreadyFilled","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"TooLittleTokensReceived","type":"error"},{"inputs":[],"name":"UserAlreadyPaused","type":"error"},{"inputs":[],"name":"UserIsPaused","type":"error"},{"inputs":[],"name":"ZeroMakerAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeeCharged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeReceiver","type":"address"}],"name":"FeeReceiverUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxFeeRate","type":"uint256"}],"name":"MaxFeeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdminAddress","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperatorAddress","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"NewOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"maker","type":"address"},{"indexed":true,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"enum Side","name":"side","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"makerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"builder","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"metadata","type":"bytes32"}],"name":"OrderFilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"OrderPreapprovalInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"OrderPreapproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"takerOrderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"takerOrderMaker","type":"address"},{"indexed":false,"internalType":"enum Side","name":"side","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"makerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerAmountFilled","type":"uint256"}],"name":"OrdersMatched","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"removedAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"RemovedAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"removedOperator","type":"address"},{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"RemovedOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pauser","type":"address"}],"name":"TradingPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pauser","type":"address"}],"name":"TradingUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldInterval","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newInterval","type":"uint256"}],"name":"UserPauseBlockIntervalUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"effectivePauseBlock","type":"uint256"}],"name":"UserPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"UserUnpaused","type":"event"},{"inputs":[],"name":"PARENT_COLLECTION_ID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCtf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCtfCollateral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"getOrderStatus","outputs":[{"components":[{"internalType":"bool","name":"filled","type":"bool"},{"internalType":"uint248","name":"remaining","type":"uint248"}],"internalType":"struct OrderStatus","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOutcomeTokenFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProxyFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProxyImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getProxyWalletAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSafeFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSafeImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getSafeWalletAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"order","type":"tuple"}],"name":"hashOrder","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"invalidatePreapprovedOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_usr","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_usr","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isUserPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"conditionId","type":"bytes32"},{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"takerOrder","type":"tuple"},{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order[]","name":"makerOrders","type":"tuple[]"},{"internalType":"uint256","name":"takerFillAmount","type":"uint256"},{"internalType":"uint256[]","name":"makerFillAmounts","type":"uint256[]"},{"internalType":"uint256","name":"takerFeeAmount","type":"uint256"},{"internalType":"uint256[]","name":"makerFeeAmounts","type":"uint256[]"}],"name":"matchOrders","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":"bytes32","name":"","type":"bytes32"}],"name":"orderStatus","outputs":[{"internalType":"bool","name":"filled","type":"bool"},{"internalType":"uint248","name":"remaining","type":"uint248"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseTrading","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"order","type":"tuple"}],"name":"preapproveOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"removeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"removeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOperatorRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setMaxFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_interval","type":"uint256"}],"name":"setUserPauseBlockInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"unpauseTrading","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userPauseBlockInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPausedBlockAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"cashValue","type":"uint256"}],"name":"validateFee","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"order","type":"tuple"}],"name":"validateOrder","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"components":[{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"},{"internalType":"enum Side","name":"side","type":"uint8"},{"internalType":"enum SignatureType","name":"signatureType","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"bytes32","name":"builder","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct Order","name":"order","type":"tuple"}],"name":"validateOrderSignature","outputs":[],"stateMutability":"view","type":"function"}]

6102406040526003805460ff191690556101f46004556064600555348015610025575f5ffd5b506040516159bd3803806159bd83398101604081905261004491610598565b60a08181015160c083015160e08401516020808601516040808801516060808a01516080808c01518c516001600160a01b03165f9081528089528681208054600160ff1991821681179092558180556002909a5296902080549098169095179096553090955246909852959694958795879594919290806100c25f90565b61011e57610118604080518082018252601781527f506f6c796d61726b6574204354462045786368616e6765000000000000000000602080830191909152825180840190935260018352601960f91b9083015291565b90925090505b5f8251602084012090505f8251602084012060c083905260e081905290505f6040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815283602082015282604082015246606082015230608082015260a0812091505061010052505050506001600160a01b03848116610120819052848216610140528382166101605290821661018081905260405163095ea7b360e01b815260048101919091525f19602482015263095ea7b3906044016020604051808303815f875af11580156101f3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102179190610666565b5060405163a22cb46560e01b81526001600160a01b0382811660048301526001602483015284169063a22cb465906044015f604051808303815f87803b15801561025f575f5ffd5b505af1158015610271573d5f5f3e3d5ffd5b505060038054610100600160a81b0319166101006001600160a01b03998a1602179055505050508483166101a08190529284166101e05250506040805163557887a160e11b8152905163aaf10f42916004808201926020929091908290030181865afa1580156102e3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610307919061068c565b6001600160a01b03166101c0816001600160a01b0316815250505f816001600160a01b031663a619486e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561035e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610382919061068c565b6001600160a01b03811661020052604080517f608060405234801561001057600080fd5b50604051610171380380610171833981527f8101604081905261002f916100b9565b6001600160a01b03811661009457604060208201527f5162461bcd60e51b815260206004820152602260248201527f496e76616c6964918101919091527f2073696e676c65746f6e20616464726573732070726f7669646044820152611960608201527f5960f21b606482015260840160405180910390fd5b600080546001600160a01b60808201527f0319166001600160a01b03929092169190911790556100e7565b60006020828460a08201527f0312156100ca578081fd5b81516001600160a01b03811681146100e0578182fd60c08201527f5b9392505050565b607c806100f56000396000f3fe608060405260008054600160e08201527f600160a01b0316813563530ca43760e11b1415602857808252602082f35b36826101008201527f833781823684845af490503d82833e806041573d82fd5b503d81f3fea26469706101208201527f66735822122015938e3bf2c49f5df5c1b7f9569fa85cc5d6f3074bb258a2dc0c6101408201527f7e299bc9e33664736f6c63430008040033000000000000000000000000000000610160820152610171810191909152610191902061022052506106a5945050505050565b80516001600160a01b0381168114610593575f5ffd5b919050565b5f6101008284031280156105aa575f5ffd5b5060405161010081016001600160401b03811182821017156105da57634e487b7160e01b5f52604160045260245ffd5b6040526105e68361057d565b81526105f46020840161057d565b60208201526106056040840161057d565b60408201526106166060840161057d565b60608201526106276080840161057d565b608082015261063860a0840161057d565b60a082015261064960c0840161057d565b60c082015261065a60e0840161057d565b60e08201529392505050565b5f60208284031215610676575f5ffd5b81518015158114610685575f5ffd5b9392505050565b5f6020828403121561069c575f5ffd5b6106858261057d565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051610200516102205161522d6107905f395f61102001525f6107f301525f818161068c015261104101525f81816106e70152610ea301525f81816107330152610ec401525f81816104130152818161360a01526136f601525f81816103110152611e6f01525f81816104ba0152818161254001526134ba01525f81816105fb0152818161248d0152818161346201528181613646015261373201525f61197c01525f611a3601525f611a1001525f6119c001525f61199d015261522d5ff3fe608060405234801561000f575f5ffd5b50600436106102e3575f3560e01c80635c975abb11610187578063b28c51c0116100dd578063e3b5900011610093578063efdcd9741161006e578063efdcd97414610843578063f23a6e6114610856578063f5db3e4b1461088f575f5ffd5b8063e3b59000146107f1578063e3bf917a14610817578063e8a3539214610820575f5ffd5b8063c0c3132c116100c3578063c0c3132c146107c3578063cd4d8cb4146107cb578063e3a5ced5146107de575f5ffd5b8063b28c51c014610731578063bc197c8114610757575f5ffd5b80637afda8b81161013d57806390e4b7201161011857806390e4b720146106e55780639870d7fe1461070b578063ac8a584a1461071e575f5ffd5b80637afda8b8146106b057806384b0196e146106b75780638cda96de146106d2575f5ffd5b8063704802751161016d578063704802751461066457806370bf48e51461067757806375d7370a1461068a575f5ffd5b80635c975abb1461061f5780636d70f7ae1461062c575f5ffd5b80633b521d781161023c578063456068d2116101f25780634cce30c9116101cd5780634cce30c9146105de57806358d8b6bb146105e65780635c1548fb146105f9575f5ffd5b8063456068d21461051f57806346423aa7146105275780634a2a11f5146105d6575f5ffd5b80633d6d3598116102225780633d6d3598146104f15780633d861a4d146104f9578063437f19941461050c575f5ffd5b80633b521d78146104b85780633c2b4399146104de575f5ffd5b80631785f53c1161029c578063288721011161027757806328872101146103fe57806329cf67f2146104115780632dff692d14610437575f5ffd5b80631785f53c14610386578063234d81b91461039957806324d7806c146103c6575f5ffd5b8063088170cb116102cc578063088170cb146103565780630ffea65d1461036b5780631031e36e1461037e575f5ffd5b806301ffc9a7146102e757806303cee3df1461030f575b5f5ffd5b6102fa6102f53660046147d8565b6108a2565b60405190151581526020015b60405180910390f35b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610306565b610369610364366004614a60565b61093a565b005b610369610379366004614a92565b61099d565b6103696109aa565b610369610394366004614ab2565b6109fc565b6103b86103a7366004614ab2565b60066020525f908152604090205481565b604051908152602001610306565b6102fa6103d4366004614ab2565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090205460ff1690565b6102fa61040c366004614ab2565b610b64565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b610481610445366004614acb565b60086020525f908152604090205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805192151583527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116602083015201610306565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b6103696104ec366004614bee565b610b9f565b610369610cbc565b6103b8610507366004614a60565b610d61565b61036961051a366004614acb565b610dbf565b610369610e13565b610598610535366004614acb565b604080518082019091525f8082526020820152505f9081526008602090815260409182902082518084019093525460ff81161515835261010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169082015290565b604080518251151581526020928301517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169281019290925201610306565b6004546103b8565b610369610e63565b6103316105f4366004614ab2565b610e9c565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b6003546102fa9060ff1681565b6102fa61063a366004614ab2565b73ffffffffffffffffffffffffffffffffffffffff165f9081526002602052604090205460ff1690565b610369610672366004614ab2565b610ee8565b610331610685366004614ab2565b611019565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b6103b85f81565b6106bf611065565b6040516103069796959493929190614d48565b6103696106e0366004614acb565b61110d565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b610369610719366004614ab2565b61115e565b61036961072c366004614ab2565b61127d565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b610792610765366004614e64565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610306565b610369611398565b6103696107d9366004614acb565b61143f565b6103696107ec366004614a60565b611490565b7f0000000000000000000000000000000000000000000000000000000000000000610331565b6103b860055481565b600354610100900473ffffffffffffffffffffffffffffffffffffffff16610331565b610369610851366004614ab2565b6114ee565b610792610864366004614f23565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b61036961089d366004614f96565b61153f565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316148061093457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b5f61094482610d61565b5f8181526008602052604090205490915060ff161561098f576040517fee3b3d4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099981836115b2565b5050565b610999828260045461163d565b335f9081526020819052604090205460ff166109f2576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109fa6116af565b565b335f9081526020819052604090205460ff16610a44576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020819052604090205460ff16610aa2576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805411610add576040517fdf0e465e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015f8154610aeb90615007565b9091555073ffffffffffffffffffffffffffffffffffffffff81165f8181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055513392917f787a2e12f4a55b658b8f573c32432ee11a5e8b51677d1e1e937aaf6a0bb5776e91a350565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600660205260408120548015801590610b985750804310155b9392505050565b335f9081526002602052604090205460ff16610be7576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035460ff1615610c24576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845180610c5d576040517fdfa61d2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835181148015610c6d5750815181145b610ca3576040517f568efce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb288888888888888611706565b5050505050505050565b335f9081526002602052604090205460ff16610d04576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555182917ff7262ed0443cc211121ceb1a80d69004f319245615a7488f951f1437fd91642c91a3565b5f610934610dba837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00180517fbb86318a2138f5fa8ae32fbe8e659f8fcf13cc6ae4014a70789305543381858982526101808220915290565b61197a565b335f9081526002602052604090205460ff16610e07576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611a90565b50565b335f9081526020819052604090205460ff16610e5b576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109fa611aed565b335f81815260066020526040808220829055517f1419d4111b5c8636aecff843bf618525f4f8e1aa6898a14357021d68dde8af129190a2565b5f610934827f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611b41565b335f9081526020819052604090205460ff16610f30576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020819052604090205460ff1615610f8f576040517f386d034a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015f8154610f9d9061503b565b9091555073ffffffffffffffffffffffffffffffffffffffff81165f8181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055513392917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91a350565b5f610934827f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000611b63565b7f0f000000000000000000000000000000000000000000000000000000000000006060805f8080836110fb604080518082018252601781527f506f6c796d61726b6574204354462045786368616e67650000000000000000006020808301919091528251808401909352600183527f32000000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b335f9081526020819052604090205460ff16611155576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611bc9565b335f9081526020819052604090205460ff166111a6576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff1615611205576040517fad45d80700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055513392917ff1e04d73c4304b5ff164f9d10c7473e2a1593b740674a6107975e2a7001c1e5c91a350565b335f9081526020819052604090205460ff166112c5576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16611323576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055513392917ff7262ed0443cc211121ceb1a80d69004f319245615a7488f951f1437fd91642c91a350565b335f90815260066020526040902054156113de576040517f2b754c5700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600554436113ed9190615072565b335f818152600660205260409081902083905551919250907fa3e76126f19eb25001b29726d2a9502b6377938633d2d6a955107dd442e7a14a906114349084815260200190565b60405180910390a250565b335f9081526020819052604090205460ff16611487576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611c3f565b335f9081526002602052604090205460ff166114d8576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6114e282610d61565b90506109998183611cc1565b335f9081526020819052604090205460ff16611536576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611d76565b806101600151515f03611594575f8281526007602052604090205460ff165b610999576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61155e81604001518260200151848461016001518560e00151611deb565b5f8160800151116115ef576040517fb2f300d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115f9828261153f565b6116068160200151610b64565b15610999576040517fe72f724500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825f0361164957505050565b805f0361165557505050565b5f6127106116638385615085565b61166d91906150c9565b9050808411156116a9576040517f3c97aa5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560405133907f203c4bd3e526634f661575359ff30de3b0edaba6c2cb1eac60f730b6d2d9d536905f90a2565b5f855111611740576040517fdfa61d2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61174b878787611e6d565b6117598660c0015186611fed565b156117715761176c86868584888761202e565b611971565b5f8660c001516001811115611788576117886150dc565b0361179c5761176c878787878787876121fa565b5f5f6117a98887866123e7565b60c08a015160608b015160208c015193955091935081029190829003906117d29030848b61241f565b5f6117dc82612442565b90505f6117ec8d8d8d8c8b61256e565b90508015611824576118243061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b5f8461241f565b5f61182e84612442565b905061183a8388615072565b811015611873576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61187d8382615109565b96505050505f6118998b60c00151868d6020015186868c6127b9565b905061193c6040518061014001604052808681526020018d6020015173ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018d60c001516001811115611902576119026150dc565b81526020018d6060015181526020018b81526020018781526020018981526020018d610140015181526020018d61012001518152506128a3565b801561196b5761196b3061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b50505050505b50505050505050565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000030147f0000000000000000000000000000000000000000000000000000000000000000461416611a6d5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527f000000000000000000000000000000000000000000000000000000000000000060208201527f00000000000000000000000000000000000000000000000000000000000000009181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f8181526007602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555182917fb766aa470f20b094f26a9a14ea5bf63a60af51703c15776e2e739b6a0428adf691a250565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560405133907fa1e8a54850dbd7f520bcc09f47bff152294b77b2081da545a7adf531b7ea283b905f90a2565b5f611b5b8284611b56876020526014602c2090565b612909565b949350505050565b5f838152602081206040517fff00000000000000000000000000000000000000000000000000000000000000605885901b17815260158101829052603581018590526055902073ffffffffffffffffffffffffffffffffffffffff165b95945050505050565b6127108110611c04576040517fd1202cff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527fe380d7c3967dd06cc7c01db8b17332a1d806fd18f63206dcbd12aaef455c7ff29060200160405180910390a150565b62049d40811115611c7c576040517fb0f7fdb400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580549082905560408051828152602081018490527f8c8acf678b7cd311e3b5768c92794d63943684862fdea390856e14d9e2a9ef88910160405180910390a15050565b611cdf81604001518260200151848461016001518560e00151611deb565b611d15576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526007602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555183917fe92c22722d9c284034b6c9f5aaec018edb3e593c0e084900b6b9d390a1182a0b91a25050565b600380547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff8416908102919091179091556040517f27aae5db36d94179909d019ae0b1ac7c16d96d953148f63c0f6a0a9c8ead79ee905f90a250565b5f80826003811115611dff57611dff6150dc565b03611e1757611e1086868686612a67565b9050611bc0565b6002826003811115611e2b57611e2b6150dc565b03611e3c57611e1086868686612aa9565b6003826003811115611e5057611e506150dc565b03611e6157611e1086868686612afc565b611e1086868686612b60565b7f00000000000000000000000000000000000000000000000000000000000000005f611f1782611e9f83886001612b93565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290525f90605401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b90505f611f2a83611e9f83896002612b93565b606086015190915082811480611f3f57508181145b611f75576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8551811015610cb2575f868281518110611f9357611f9361511c565b602002602001015160600151905084811480611fae57508381145b611fe4576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600101611f77565b8051600190602083015f5b8281101561202557600581901b82015160c0015186810361201c575f945050612025565b50600101611ff8565b50505092915050565b5f61203887610d61565b905061204481886115b2565b60208701515f808960c001516001811115612061576120616150dc565b1490505f8080805b8b51811015612106575f5f6120cd8f8f858151811061208a5761208a61511c565b60200260200101518f86815181106120a4576120a461511c565b60200260200101518f87815181106120be576120be61511c565b60200260200101518c8c612fee565b91509150818601955080850194508c83815181106120ed576120ed61511c565b6020026020010151840193508260010192505050612069565b5087821115612141576040517f3fd8221600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61214d8d848a6131b6565b905080821015612189576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612194878e8561320e565b506121a48d87878a87878e6132c7565b6121ae9085615072565b935083156121eb576121eb856121c457306121c6565b865b600354610100900473ffffffffffffffffffffffffffffffffffffffff165f8761241f565b50505050505050505050505050565b5f5f6122078887866123e7565b60c08a015160608b015160208c015193955091935081029190829003906122399030846122348a8d615072565b61241f565b5f61224382612442565b90505f6122538d8d8d8c8b61256e565b90508715612295576122658882615072565b905061229561228f60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b8961342e565b80156122c4576122c43061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b5f6122ce84612442565b90506122da8388615072565b811015612313576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61231d8382615109565b96505050506123388a60c00151858c6020015185855f6127b9565b506123da6040518061014001604052808581526020018c6020015173ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018c60c0015160018111156123a0576123a06150dc565b81526020018c6060015181526020018a81526020018681526020018881526020018c610140015181526020018c61012001518152506128a3565b5050505050505050505050565b5f5f6123f285610d61565b90506123fe81866115b2565b61240981868661320e565b506124158585856131b6565b9150935093915050565b815f0361243657612431848483613460565b6116a9565b6116a9848484846134b5565b5f815f036124f7576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906370a08231906024015b602060405180830381865afa1580156124d3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109349190615149565b6040517efdd58e0000000000000000000000000000000000000000000000000000000081523060048201526024810183905273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169062fdd58e906044016124b8565b82515f90818167ffffffffffffffff81111561258c5761258c614817565b6040519080825280602002602001820160405280156125c557816020015b6125b2614768565b8152602001906001900390816125aa5790505b5090505f80805b84811015612703575f6126078b8b84815181106125eb576125eb61511c565b602002602001015160c091820151910151811460019091010290565b90506126618b8b848151811061261f5761261f61511c565b60200260200101518b85815181106126395761263961511c565b60200260200101518b86815181106126535761265361511c565b6020026020010151856134e2565b8583815181106126735761267361511c565b60209081029190910101526001816002811115612692576126926150dc565b036126be578482815181106126a9576126a961511c565b602002602001015160400151840193506126fa565b60028160028111156126d2576126d26150dc565b036126fa578482815181106126e9576126e961511c565b602002602001015160200151830192505b506001016125cc565b508115612714576127148a836135e4565b8015612724576127248a826136d0565b5f5b848110156127ab575f8482815181106127415761274161511c565b6020026020010151905060018081111561275d5761275d6150dc565b8160a001516001811115612773576127736150dc565b0361278f57612786818c60200151613756565b870196506127a2565b61279d818c602001516138b1565b870196505b50600101612726565b505050505095945050505050565b5f8560018860018111156127cf576127cf6150dc565b03612816578683111561280e576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508190508086035b6128223087868461241f565b6001886001811115612836576128366150dc565b0361286f57821561286a5760035461286a90610100900473ffffffffffffffffffffffffffffffffffffffff165b8461342e565b612879565b61287986846139b2565b5f61288386612442565b90508015612897576128973088888461241f565b50509695505050505050565b6128ac816139f1565b5f7f174b3811690657c217184f89418266767c87e4805d09680c39fc9c031c0cab7c9050604051606083015181526080830151602082015260a0830151604082015260c083015160608201526020830151835183608084a3505050565b5f5f612a0b85856040517f3d0000000000000000000000000000000000000000000000000000000000000081527f3d606380380380913d393d73000000000000000000000000000000000000000090921760018301527f5af4602a57600080fd5b602d806036600039600000000000000000000000000060218301527ef3363d3d373d3d3d363d7300000000000000000000000000000000000000001760348201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060548201527f52e831dd000000000000000000000000000000000000000000000000000000006063820152602060678201525f608782015260a7902090565b604051605887901b7fff0000000000000000000000000000000000000000000000000000000000000017815260158101859052603581018290526055902090915073ffffffffffffffffffffffffffffffffffffffff16611bc0565b5f8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148015611bc05750611bc0858484613a73565b5f612ab5858484613a73565b8015611bc057508373ffffffffffffffffffffffffffffffffffffffff16612adc86611019565b73ffffffffffffffffffffffffffffffffffffffff161495945050505050565b5f8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148015612b4e57505f8473ffffffffffffffffffffffffffffffffffffffff163b115b8015611bc05750611bc0848484613ab4565b5f612b6c858484613a73565b8015611bc057508373ffffffffffffffffffffffffffffffffffffffff16612adc86610e9c565b5f5f8383604051602001612bb1929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120905060ff81901c15155f805b7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476001850893507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47808788098709089050612c7081613bb5565b9150807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840903612bf357828015612cb15750612caf600283615160565b155b80612ccf575082158015612ccf5750612ccb600283615160565b6001145b15612d0157612cfe827f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615109565b91505b878015612fa95760fe81901c151593507f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478084850984090891505f612d8a83613bb5565b9050848015612da15750612d9f600282615160565b155b80612dbf575084158015612dbf5750612dbb600282615160565b6001145b15612df157612dee817f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615109565b90505b827f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4782830914612e82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f696e76616c696420706172656e7420636f6c6c656374696f6e2049440000000060448201526064015b60405180910390fd5b604080516020810188905290810185905260608101839052608081018290525f90819060069060a001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052612ee391615173565b5f60405180830381855afa9150503d805f8114612f1b576040519150601f19603f3d011682016040523d82523d5f602084013e612f20565b606091505b509150915081612f8c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6563616464206661696c656400000000000000000000000000000000000000006044820152606401612e79565b80806020019051810190612fa09190615189565b90985095505050505b612fb4600284615160565b600103612fe1577f4000000000000000000000000000000000000000000000000000000000000000851894505b5092979650505050505050565b5f5f612ffb88885f61436b565b5f5f6130088989896123e7565b9150915081925084156130ae576130298960200151878b606001518b61241f565b818715613098578288111561306a576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87945084900361309861228f60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b6130a8878b602001515f8461241f565b50613108565b6130c2868a602001518c606001518561241f565b60208901516130d790305f6122348b8d615072565b86156131085760035487945061310890610100900473ffffffffffffffffffffffffffffffffffffffff168861342e565b6131a96040518061014001604052808381526020018b6020015173ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1681526020018b60c00151600181111561316f5761316f6150dc565b81526020018b6060015181526020018a81526020018481526020018981526020018b610140015181526020018b61012001518152506139f1565b5050965096945050505050565b5f6131ca8385608001518660a00151614542565b90508115610b98575f808560c0015160018111156131ea576131ea6150dc565b146131f557816131f7565b835b9050613206838260045461163d565b509392505050565b5f83815260086020819052604090912080549182901c9160ff168015613260576040517fee3b3d4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b821561326c5782613272565b84608001515b9250828411156132ae576040517fe2cc6ad600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838303925082158360081b178083555050509392505050565b5f85156133095781156133045750806133046132fe60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b8361342e565b613386565b828215613378578383111561334a576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82915081900361337861286460035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b61338430895f8461241f565b505b6134236040518061014001604052808781526020018973ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018a60c0015160018111156133e9576133e96150dc565b81526020018a6060015181526020018681526020018581526020018481526020018a610140015181526020018a61012001518152506128a3565b979650505050505050565b5f7f55bb3cade9d43b798a4fe5ffdd05024b2d7870df53920673bfc7e68047cd0ab19050815f52828160205fa2505050565b7f00000000000000000000000000000000000000000000000000000000000000003073ffffffffffffffffffffffffffffffffffffffff8516036134a957612431818484614558565b6116a981858585614563565b6116a97f00000000000000000000000000000000000000000000000000000000000000008585858561456f565b6134ea614768565b6134f586868461436b565b5f5f6135028787876123e7565b60c089015160608a015160208b015193955091935081810292918390039161355291309085905f90600181111561353b5761353b6150dc565b14613546575f613548565b8a5b612234908d615072565b6040518061014001604052808481526020018981526020018581526020018a6020015173ffffffffffffffffffffffffffffffffffffffff1681526020018281526020018a60c0015160018111156135ac576135ac6150dc565b81526020018881526020018a610140015181526020018a610120015181526020018a6060015181525094505050505095945050505050565b5f61360660408051600280825260016020830152818301526060810190915290565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166372ce42757f00000000000000000000000000000000000000000000000000000000000000005b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526136a791905f908890879089906004016151ab565b5f604051808303815f87803b1580156136be575f5ffd5b505af1158015611971573d5f5f3e3d5ffd5b5f6136f260408051600280825260016020830152818301526060810190915290565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16639e7212ad7f0000000000000000000000000000000000000000000000000000000000000000613666565b604082015160c08301515f919081101561379c576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360c0015184604001510390508360c0015191506137c430856060015186608001518461241f565b60c0840151156137fa576003546137fa90610100900473ffffffffffffffffffffffffffffffffffffffff168560c0015161342e565b6138aa604051806101400160405280865f01518152602001866060015173ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018660a001516001811115613864576138646150dc565b8152602001866101200151815260200186602001518152602001866040015181526020018660c0015181526020018660e0015181526020018661010001518152506139f1565b5092915050565b5f6138ca3084606001518560800151866040015161241f565b60c083015115613902575060c082015160035461390290610100900473ffffffffffffffffffffffffffffffffffffffff168261342e565b610934604051806101400160405280855f01518152602001856060015173ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018560a00151600181111561396c5761396c6150dc565b8152602001856101200151815260200185602001518152602001856040015181526020018560c0015181526020018560e0015181526020018561010001518152506139f1565b801561099957600354610100900473ffffffffffffffffffffffffffffffffffffffff166139e283825f8561241f565b6139ec818361342e565b505050565b5f7fd543adfd945773f1a62f74f0ee55a5e3b9b1a28262980ba90b1a89f2ea84d8ee9050604051606083015181526080830151602082015260a0830151604082015260c0830151606082015260e0830151608082015261010083015160a082015261012083015160c08201526040830151602084015184518460e085a4505050565b5f8373ffffffffffffffffffffffffffffffffffffffff16613a958484614615565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b5f73ffffffffffffffffffffffffffffffffffffffff841615610b9857604051843b613b6f57825160408114613af25760418114613b2c5750613206565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606052613b3f565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5180861860601b3d119250505f60605280604052613206565b631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa905060208260443d01868b5afa9151911691141695945050505050565b5f7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47808380099150808283098181820990508181840992508183850993508184840992508183840990508181820982818309905082818209905082818209905082818309915082828609945082858609915082828309915082828509935082848509915082828309915082828309915082828509915082828609945082858609915082828309915082828309915082828609915082828509935082848609945082858609915082828309915082828509935082848509915082828309905082818209905082818209905082818309915082828609945082858509935082848509915082828309915082828309915082828609945082858609915082828309915082828609915082828309915082828309915082828609915082828509935082848509915082828309905082818209905082818309905082818509905082818209905082818209905082818209905082818209905082818309915082828609945082858609915082828609915082828509935082848509915082828509915082828309915082828309905082818309905082818209838182099050838182099050838182099050838182099050838183099150508281830991508282860994508285850993508284850991508282860994508285850993508284860994508285850993508284860994508285860991508282860991508282830991508282850993508284850991508282830991508282860994508285850993508284850991508282850991508282860994508285850993508284860994508285850993508284850991508282830991508282850991508282860994508285860991508282860991508282850993508284860994508285850993508284860994508285850993508284850991508282850991508282830991508282860994508285850993508284850991508282850991508282830991508282860994508285860991508282830990508281820990508281830990508281860990508281820990508281820990508281820990508281820990508281830991508282850993508284860994508285850993508284860994508285860991508282860991508282830991508282830991508282830991508282860991508282850993508284850991508282850991508282830991508282860994508285860991508282860991508282850993508284860994508285860991508282830991508282850993508284860994508285860991508282850993508284860994508285850993508284850991508282850991508282860994508285850993508284850991508282850991508282830991508282830991508282860994508285860991508282830991508282830991508282860991508282850993508284860994508285860991508282860990508281820990508281820990508281830991508282850993508284850991508282860994508285850993508284860994508285850993508284860994508285850993508284850991508282850990508281850991508282830991508282830991508282820991505081818509935081848409925081838509935081848409925081838509935081848509905081818509905081818409925050808284099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808383099392505050565b5f81600281111561437e5761437e6150dc565b036144245781606001518360600151146143c4576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160a001518360a001516143d89190615085565b826080015184608001516143ec9190615085565b10156139ec576040517f7f9a6f4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160600151836060015103614465576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001816002811115614479576144796150dc565b036144c4578160a001518360a001516144929190615085565b83608001518360a001516144a69190615085565b83608001518560a001516144ba9190615085565b6143ec9190615072565b816080015183608001516144d89190615085565b83608001518360a001516144ec9190615085565b83608001518560a001516145009190615085565b61450a9190615072565b11156139ec576040517f7f9a6f4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8261454e8386615085565b611b5b91906150c9565b6139ec8383836146b3565b6116a984848484614706565b6040517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528481166024830152604482018490526064820183905260a060848301525f60a483015286169063f242432a9060c4015f604051808303815f87803b1580156145f8575f5ffd5b505af115801561460a573d5f5f3e3d5ffd5b505050505050505050565b5f604051825160408114614631576041811461466b57506146a6565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660605261467e565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d6138aa575b638baa579f5f526004601cfd5b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af18060015f5114166146fc57803d853b1517106146fc576390b8ec185f526004601cfd5b505f603452505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c5260205f6064601c5f895af18060015f51141661475a57803d873b15171061475a57637939f4245f526004601cfd5b505f60605260405250505050565b6040518061014001604052805f81526020015f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f60018111156147ba576147ba6150dc565b81526020015f81526020015f81526020015f81526020015f81525090565b5f602082840312156147e8575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610b98575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610180810167ffffffffffffffff8111828210171561486857614868614817565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156148b5576148b5614817565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146148e0575f5ffd5b919050565b8035600281106148e0575f5ffd5b8035600481106148e0575f5ffd5b5f82601f830112614910575f5ffd5b813567ffffffffffffffff81111561492a5761492a614817565b61495b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161486e565b81815284602083860101111561496f575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f610180828403121561499c575f5ffd5b6149a4614844565b8235815290506149b6602083016148bd565b60208201526149c7604083016148bd565b6040820152606082810135908201526080808301359082015260a080830135908201526149f660c083016148e5565b60c0820152614a0760e083016148f3565b60e082015261010082810135908201526101208083013590820152610140808301359082015261016082013567ffffffffffffffff811115614a47575f5ffd5b614a5384828501614901565b6101608301525092915050565b5f60208284031215614a70575f5ffd5b813567ffffffffffffffff811115614a86575f5ffd5b611b5b8482850161498b565b5f5f60408385031215614aa3575f5ffd5b50508035926020909101359150565b5f60208284031215614ac2575f5ffd5b610b98826148bd565b5f60208284031215614adb575f5ffd5b5035919050565b5f67ffffffffffffffff821115614afb57614afb614817565b5060051b60200190565b5f82601f830112614b14575f5ffd5b8135614b27614b2282614ae2565b61486e565b8082825260208201915060208360051b860101925085831115614b48575f5ffd5b602085015b83811015614b8957803567ffffffffffffffff811115614b6b575f5ffd5b614b7a886020838a010161498b565b84525060209283019201614b4d565b5095945050505050565b5f82601f830112614ba2575f5ffd5b8135614bb0614b2282614ae2565b8082825260208201915060208360051b860101925085831115614bd1575f5ffd5b602085015b83811015614b89578035835260209283019201614bd6565b5f5f5f5f5f5f5f60e0888a031215614c04575f5ffd5b87359650602088013567ffffffffffffffff811115614c21575f5ffd5b614c2d8a828b0161498b565b965050604088013567ffffffffffffffff811115614c49575f5ffd5b614c558a828b01614b05565b95505060608801359350608088013567ffffffffffffffff811115614c78575f5ffd5b614c848a828b01614b93565b93505060a0880135915060c088013567ffffffffffffffff811115614ca7575f5ffd5b614cb38a828b01614b93565b91505092959891949750929550565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f8151808452602084019350602083015f5b82811015614d3e578151865260209586019590910190600101614d20565b5093949350505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f614d8260e0830189614cc2565b8281036040840152614d948189614cc2565b905086606084015273ffffffffffffffffffffffffffffffffffffffff861660808401528460a084015282810360c0840152614dd08185614d0e565b9a9950505050505050505050565b5f5f83601f840112614dee575f5ffd5b50813567ffffffffffffffff811115614e05575f5ffd5b6020830191508360208260051b8501011115614e1f575f5ffd5b9250929050565b5f5f83601f840112614e36575f5ffd5b50813567ffffffffffffffff811115614e4d575f5ffd5b602083019150836020828501011115614e1f575f5ffd5b5f5f5f5f5f5f5f5f60a0898b031215614e7b575f5ffd5b614e84896148bd565b9750614e9260208a016148bd565b9650604089013567ffffffffffffffff811115614ead575f5ffd5b614eb98b828c01614dde565b909750955050606089013567ffffffffffffffff811115614ed8575f5ffd5b614ee48b828c01614dde565b909550935050608089013567ffffffffffffffff811115614f03575f5ffd5b614f0f8b828c01614e26565b999c989b5096995094979396929594505050565b5f5f5f5f5f5f60a08789031215614f38575f5ffd5b614f41876148bd565b9550614f4f602088016148bd565b94506040870135935060608701359250608087013567ffffffffffffffff811115614f78575f5ffd5b614f8489828a01614e26565b979a9699509497509295939492505050565b5f5f60408385031215614fa7575f5ffd5b82359150602083013567ffffffffffffffff811115614fc4575f5ffd5b614fd08582860161498b565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f8161501557615015614fda565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361506b5761506b614fda565b5060010190565b8082018082111561093457610934614fda565b808202811582820484141761093457610934614fda565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826150d7576150d761509c565b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b8181038181111561093457610934614fda565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215615159575f5ffd5b5051919050565b5f8261516e5761516e61509c565b500690565b5f82518060208501845e5f920191825250919050565b5f5f6040838503121561519a575f5ffd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8616815284602082015283604082015260a060608201525f6151e560a0830185614d0e565b9050826080830152969550505050505056fea26469706673582212202aaf21d5cc0520703a08ce1ecc89fb6dc5909644172b314443b6706537069eb064736f6c634300082200330000000000000000000000003dce0a29139a851da1dfca56af8e8a6440b4d952000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea04760450000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174000000000000000000000000ada100874d00e3331d00f2007a9c336a65009718000000000000000000000000ab45c5a4b0c941a2f231c04c3f49182e1a254052000000000000000000000000aacfeea03eb1561c4e67d661e40682bd20e3541b000000000000000000000000115f48dc2a731aa16251c6d6e1befc42f92accc9

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106102e3575f3560e01c80635c975abb11610187578063b28c51c0116100dd578063e3b5900011610093578063efdcd9741161006e578063efdcd97414610843578063f23a6e6114610856578063f5db3e4b1461088f575f5ffd5b8063e3b59000146107f1578063e3bf917a14610817578063e8a3539214610820575f5ffd5b8063c0c3132c116100c3578063c0c3132c146107c3578063cd4d8cb4146107cb578063e3a5ced5146107de575f5ffd5b8063b28c51c014610731578063bc197c8114610757575f5ffd5b80637afda8b81161013d57806390e4b7201161011857806390e4b720146106e55780639870d7fe1461070b578063ac8a584a1461071e575f5ffd5b80637afda8b8146106b057806384b0196e146106b75780638cda96de146106d2575f5ffd5b8063704802751161016d578063704802751461066457806370bf48e51461067757806375d7370a1461068a575f5ffd5b80635c975abb1461061f5780636d70f7ae1461062c575f5ffd5b80633b521d781161023c578063456068d2116101f25780634cce30c9116101cd5780634cce30c9146105de57806358d8b6bb146105e65780635c1548fb146105f9575f5ffd5b8063456068d21461051f57806346423aa7146105275780634a2a11f5146105d6575f5ffd5b80633d6d3598116102225780633d6d3598146104f15780633d861a4d146104f9578063437f19941461050c575f5ffd5b80633b521d78146104b85780633c2b4399146104de575f5ffd5b80631785f53c1161029c578063288721011161027757806328872101146103fe57806329cf67f2146104115780632dff692d14610437575f5ffd5b80631785f53c14610386578063234d81b91461039957806324d7806c146103c6575f5ffd5b8063088170cb116102cc578063088170cb146103565780630ffea65d1461036b5780631031e36e1461037e575f5ffd5b806301ffc9a7146102e757806303cee3df1461030f575b5f5ffd5b6102fa6102f53660046147d8565b6108a2565b60405190151581526020015b60405180910390f35b7f0000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841745b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610306565b610369610364366004614a60565b61093a565b005b610369610379366004614a92565b61099d565b6103696109aa565b610369610394366004614ab2565b6109fc565b6103b86103a7366004614ab2565b60066020525f908152604090205481565b604051908152602001610306565b6102fa6103d4366004614ab2565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090205460ff1690565b6102fa61040c366004614ab2565b610b64565b7f000000000000000000000000ada100874d00e3331d00f2007a9c336a65009718610331565b610481610445366004614acb565b60086020525f908152604090205460ff81169061010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1682565b6040805192151583527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116602083015201610306565b7f0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea0476045610331565b6103696104ec366004614bee565b610b9f565b610369610cbc565b6103b8610507366004614a60565b610d61565b61036961051a366004614acb565b610dbf565b610369610e13565b610598610535366004614acb565b604080518082019091525f8082526020820152505f9081526008602090815260409182902082518084019093525460ff81161515835261010090047effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169082015290565b604080518251151581526020928301517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169281019290925201610306565b6004546103b8565b610369610e63565b6103316105f4366004614ab2565b610e9c565b7f000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb610331565b6003546102fa9060ff1681565b6102fa61063a366004614ab2565b73ffffffffffffffffffffffffffffffffffffffff165f9081526002602052604090205460ff1690565b610369610672366004614ab2565b610ee8565b610331610685366004614ab2565b611019565b7f000000000000000000000000aacfeea03eb1561c4e67d661e40682bd20e3541b610331565b6103b85f81565b6106bf611065565b6040516103069796959493929190614d48565b6103696106e0366004614acb565b61110d565b7f00000000000000000000000044e999d5c2f66ef0861317f9a4805ac2e90aeb4f610331565b610369610719366004614ab2565b61115e565b61036961072c366004614ab2565b61127d565b7f000000000000000000000000ab45c5a4b0c941a2f231c04c3f49182e1a254052610331565b610792610765366004614e64565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610306565b610369611398565b6103696107d9366004614acb565b61143f565b6103696107ec366004614a60565b611490565b7f000000000000000000000000e51abdf814f8854941b9fe8e3a4f65cab4e7a4a8610331565b6103b860055481565b600354610100900473ffffffffffffffffffffffffffffffffffffffff16610331565b610369610851366004614ab2565b6114ee565b610792610864366004614f23565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b61036961089d366004614f96565b61153f565b5f7f4e2312e0000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316148061093457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b5f61094482610d61565b5f8181526008602052604090205490915060ff161561098f576040517fee3b3d4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61099981836115b2565b5050565b610999828260045461163d565b335f9081526020819052604090205460ff166109f2576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109fa6116af565b565b335f9081526020819052604090205460ff16610a44576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020819052604090205460ff16610aa2576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805411610add576040517fdf0e465e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015f8154610aeb90615007565b9091555073ffffffffffffffffffffffffffffffffffffffff81165f8181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055513392917f787a2e12f4a55b658b8f573c32432ee11a5e8b51677d1e1e937aaf6a0bb5776e91a350565b73ffffffffffffffffffffffffffffffffffffffff81165f908152600660205260408120548015801590610b985750804310155b9392505050565b335f9081526002602052604090205460ff16610be7576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035460ff1615610c24576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845180610c5d576040517fdfa61d2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835181148015610c6d5750815181145b610ca3576040517f568efce200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cb288888888888888611706565b5050505050505050565b335f9081526002602052604090205460ff16610d04576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b335f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555182917ff7262ed0443cc211121ceb1a80d69004f319245615a7488f951f1437fd91642c91a3565b5f610934610dba837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00180517fbb86318a2138f5fa8ae32fbe8e659f8fcf13cc6ae4014a70789305543381858982526101808220915290565b61197a565b335f9081526002602052604090205460ff16610e07576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611a90565b50565b335f9081526020819052604090205460ff16610e5b576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109fa611aed565b335f81815260066020526040808220829055517f1419d4111b5c8636aecff843bf618525f4f8e1aa6898a14357021d68dde8af129190a2565b5f610934827f00000000000000000000000044e999d5c2f66ef0861317f9a4805ac2e90aeb4f7f000000000000000000000000ab45c5a4b0c941a2f231c04c3f49182e1a254052611b41565b335f9081526020819052604090205460ff16610f30576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526020819052604090205460ff1615610f8f576040517f386d034a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60015f8154610f9d9061503b565b9091555073ffffffffffffffffffffffffffffffffffffffff81165f8181526020819052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055513392917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91a350565b5f610934827f2bce2127ff07fb632d16c8347c4ebf501f4841168bed00d9e6ef715ddb6fcecf7f000000000000000000000000aacfeea03eb1561c4e67d661e40682bd20e3541b611b63565b7f0f000000000000000000000000000000000000000000000000000000000000006060805f8080836110fb604080518082018252601781527f506f6c796d61726b6574204354462045786368616e67650000000000000000006020808301919091528251808401909352600183527f32000000000000000000000000000000000000000000000000000000000000009083015291565b97989097965046955030945091925090565b335f9081526020819052604090205460ff16611155576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611bc9565b335f9081526020819052604090205460ff166111a6576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff1615611205576040517fad45d80700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055513392917ff1e04d73c4304b5ff164f9d10c7473e2a1593b740674a6107975e2a7001c1e5c91a350565b335f9081526020819052604090205460ff166112c5576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f9081526002602052604090205460ff16611323576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81165f8181526002602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055513392917ff7262ed0443cc211121ceb1a80d69004f319245615a7488f951f1437fd91642c91a350565b335f90815260066020526040902054156113de576040517f2b754c5700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f600554436113ed9190615072565b335f818152600660205260409081902083905551919250907fa3e76126f19eb25001b29726d2a9502b6377938633d2d6a955107dd442e7a14a906114349084815260200190565b60405180910390a250565b335f9081526020819052604090205460ff16611487576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611c3f565b335f9081526002602052604090205460ff166114d8576040517f7c214f0400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6114e282610d61565b90506109998183611cc1565b335f9081526020819052604090205460ff16611536576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e1081611d76565b806101600151515f03611594575f8281526007602052604090205460ff165b610999576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61155e81604001518260200151848461016001518560e00151611deb565b5f8160800151116115ef576040517fb2f300d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6115f9828261153f565b6116068160200151610b64565b15610999576040517fe72f724500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825f0361164957505050565b805f0361165557505050565b5f6127106116638385615085565b61166d91906150c9565b9050808411156116a9576040517f3c97aa5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560405133907f203c4bd3e526634f661575359ff30de3b0edaba6c2cb1eac60f730b6d2d9d536905f90a2565b5f855111611740576040517fdfa61d2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61174b878787611e6d565b6117598660c0015186611fed565b156117715761176c86868584888761202e565b611971565b5f8660c001516001811115611788576117886150dc565b0361179c5761176c878787878787876121fa565b5f5f6117a98887866123e7565b60c08a015160608b015160208c015193955091935081029190829003906117d29030848b61241f565b5f6117dc82612442565b90505f6117ec8d8d8d8c8b61256e565b90508015611824576118243061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b5f8461241f565b5f61182e84612442565b905061183a8388615072565b811015611873576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61187d8382615109565b96505050505f6118998b60c00151868d6020015186868c6127b9565b905061193c6040518061014001604052808681526020018d6020015173ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018d60c001516001811115611902576119026150dc565b81526020018d6060015181526020018b81526020018781526020018981526020018d610140015181526020018d61012001518152506128a3565b801561196b5761196b3061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b50505050505b50505050505050565b7f3264e159346253e26a64e00b69032db0e7d32f94628de3e6eecb50304d7af3d27f000000000000000000000000e111180000d2663c0091e4f400237545b87b996b30147f0000000000000000000000000000000000000000000000000000000000000089461416611a6d5750604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81527ff30041e9aac4c4d3a1481d2941dfb0a844a72040e9bbc79a810d1ec5b5d6c7af60208201527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a59181019190915246606082015230608082015260a090205b6719010000000000005f5280601a5281603a52604260182090505f603a52919050565b5f8181526007602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690555182917fb766aa470f20b094f26a9a14ea5bf63a60af51703c15776e2e739b6a0428adf691a250565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560405133907fa1e8a54850dbd7f520bcc09f47bff152294b77b2081da545a7adf531b7ea283b905f90a2565b5f611b5b8284611b56876020526014602c2090565b612909565b949350505050565b5f838152602081206040517fff00000000000000000000000000000000000000000000000000000000000000605885901b17815260158101829052603581018590526055902073ffffffffffffffffffffffffffffffffffffffff165b95945050505050565b6127108110611c04576040517fd1202cff00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527fe380d7c3967dd06cc7c01db8b17332a1d806fd18f63206dcbd12aaef455c7ff29060200160405180910390a150565b62049d40811115611c7c576040517fb0f7fdb400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580549082905560408051828152602081018490527f8c8acf678b7cd311e3b5768c92794d63943684862fdea390856e14d9e2a9ef88910160405180910390a15050565b611cdf81604001518260200151848461016001518560e00151611deb565b611d15576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526007602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555183917fe92c22722d9c284034b6c9f5aaec018edb3e593c0e084900b6b9d390a1182a0b91a25050565b600380547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1661010073ffffffffffffffffffffffffffffffffffffffff8416908102919091179091556040517f27aae5db36d94179909d019ae0b1ac7c16d96d953148f63c0f6a0a9c8ead79ee905f90a250565b5f80826003811115611dff57611dff6150dc565b03611e1757611e1086868686612a67565b9050611bc0565b6002826003811115611e2b57611e2b6150dc565b03611e3c57611e1086868686612aa9565b6003826003811115611e5057611e506150dc565b03611e6157611e1086868686612afc565b611e1086868686612b60565b7f0000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841745f611f1782611e9f83886001612b93565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084901b166020820152603481018290525f90605401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b90505f611f2a83611e9f83896002612b93565b606086015190915082811480611f3f57508181145b611f75576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b8551811015610cb2575f868281518110611f9357611f9361511c565b602002602001015160600151905084811480611fae57508381145b611fe4576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50600101611f77565b8051600190602083015f5b8281101561202557600581901b82015160c0015186810361201c575f945050612025565b50600101611ff8565b50505092915050565b5f61203887610d61565b905061204481886115b2565b60208701515f808960c001516001811115612061576120616150dc565b1490505f8080805b8b51811015612106575f5f6120cd8f8f858151811061208a5761208a61511c565b60200260200101518f86815181106120a4576120a461511c565b60200260200101518f87815181106120be576120be61511c565b60200260200101518c8c612fee565b91509150818601955080850194508c83815181106120ed576120ed61511c565b6020026020010151840193508260010192505050612069565b5087821115612141576040517f3fd8221600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61214d8d848a6131b6565b905080821015612189576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612194878e8561320e565b506121a48d87878a87878e6132c7565b6121ae9085615072565b935083156121eb576121eb856121c457306121c6565b865b600354610100900473ffffffffffffffffffffffffffffffffffffffff165f8761241f565b50505050505050505050505050565b5f5f6122078887866123e7565b60c08a015160608b015160208c015193955091935081029190829003906122399030846122348a8d615072565b61241f565b5f61224382612442565b90505f6122538d8d8d8c8b61256e565b90508715612295576122658882615072565b905061229561228f60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b8961342e565b80156122c4576122c43061181d60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b5f6122ce84612442565b90506122da8388615072565b811015612313576040517fdf4d808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61231d8382615109565b96505050506123388a60c00151858c6020015185855f6127b9565b506123da6040518061014001604052808581526020018c6020015173ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018c60c0015160018111156123a0576123a06150dc565b81526020018c6060015181526020018a81526020018681526020018881526020018c610140015181526020018c61012001518152506128a3565b5050505050505050505050565b5f5f6123f285610d61565b90506123fe81866115b2565b61240981868661320e565b506124158585856131b6565b9150935093915050565b815f0361243657612431848483613460565b6116a9565b6116a9848484846134b5565b5f815f036124f7576040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb16906370a08231906024015b602060405180830381865afa1580156124d3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109349190615149565b6040517efdd58e0000000000000000000000000000000000000000000000000000000081523060048201526024810183905273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea0476045169062fdd58e906044016124b8565b82515f90818167ffffffffffffffff81111561258c5761258c614817565b6040519080825280602002602001820160405280156125c557816020015b6125b2614768565b8152602001906001900390816125aa5790505b5090505f80805b84811015612703575f6126078b8b84815181106125eb576125eb61511c565b602002602001015160c091820151910151811460019091010290565b90506126618b8b848151811061261f5761261f61511c565b60200260200101518b85815181106126395761263961511c565b60200260200101518b86815181106126535761265361511c565b6020026020010151856134e2565b8583815181106126735761267361511c565b60209081029190910101526001816002811115612692576126926150dc565b036126be578482815181106126a9576126a961511c565b602002602001015160400151840193506126fa565b60028160028111156126d2576126d26150dc565b036126fa578482815181106126e9576126e961511c565b602002602001015160200151830192505b506001016125cc565b508115612714576127148a836135e4565b8015612724576127248a826136d0565b5f5b848110156127ab575f8482815181106127415761274161511c565b6020026020010151905060018081111561275d5761275d6150dc565b8160a001516001811115612773576127736150dc565b0361278f57612786818c60200151613756565b870196506127a2565b61279d818c602001516138b1565b870196505b50600101612726565b505050505095945050505050565b5f8560018860018111156127cf576127cf6150dc565b03612816578683111561280e576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b508190508086035b6128223087868461241f565b6001886001811115612836576128366150dc565b0361286f57821561286a5760035461286a90610100900473ffffffffffffffffffffffffffffffffffffffff165b8461342e565b612879565b61287986846139b2565b5f61288386612442565b90508015612897576128973088888461241f565b50509695505050505050565b6128ac816139f1565b5f7f174b3811690657c217184f89418266767c87e4805d09680c39fc9c031c0cab7c9050604051606083015181526080830151602082015260a0830151604082015260c083015160608201526020830151835183608084a3505050565b5f5f612a0b85856040517f3d0000000000000000000000000000000000000000000000000000000000000081527f3d606380380380913d393d73000000000000000000000000000000000000000090921760018301527f5af4602a57600080fd5b602d806036600039600000000000000000000000000060218301527ef3363d3d373d3d3d363d7300000000000000000000000000000000000000001760348201527f5af43d82803e903d91602b57fd5bf3000000000000000000000000000000000060548201527f52e831dd000000000000000000000000000000000000000000000000000000006063820152602060678201525f608782015260a7902090565b604051605887901b7fff0000000000000000000000000000000000000000000000000000000000000017815260158101859052603581018290526055902090915073ffffffffffffffffffffffffffffffffffffffff16611bc0565b5f8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148015611bc05750611bc0858484613a73565b5f612ab5858484613a73565b8015611bc057508373ffffffffffffffffffffffffffffffffffffffff16612adc86611019565b73ffffffffffffffffffffffffffffffffffffffff161495945050505050565b5f8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16148015612b4e57505f8473ffffffffffffffffffffffffffffffffffffffff163b115b8015611bc05750611bc0848484613ab4565b5f612b6c858484613a73565b8015611bc057508373ffffffffffffffffffffffffffffffffffffffff16612adc86610e9c565b5f5f8383604051602001612bb1929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120905060ff81901c15155f805b7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476001850893507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47808788098709089050612c7081613bb5565b9150807f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4783840903612bf357828015612cb15750612caf600283615160565b155b80612ccf575082158015612ccf5750612ccb600283615160565b6001145b15612d0157612cfe827f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615109565b91505b878015612fa95760fe81901c151593507f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478084850984090891505f612d8a83613bb5565b9050848015612da15750612d9f600282615160565b155b80612dbf575084158015612dbf5750612dbb600282615160565b6001145b15612df157612dee817f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47615109565b90505b827f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4782830914612e82576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f696e76616c696420706172656e7420636f6c6c656374696f6e2049440000000060448201526064015b60405180910390fd5b604080516020810188905290810185905260608101839052608081018290525f90819060069060a001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052612ee391615173565b5f60405180830381855afa9150503d805f8114612f1b576040519150601f19603f3d011682016040523d82523d5f602084013e612f20565b606091505b509150915081612f8c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6563616464206661696c656400000000000000000000000000000000000000006044820152606401612e79565b80806020019051810190612fa09190615189565b90985095505050505b612fb4600284615160565b600103612fe1577f4000000000000000000000000000000000000000000000000000000000000000851894505b5092979650505050505050565b5f5f612ffb88885f61436b565b5f5f6130088989896123e7565b9150915081925084156130ae576130298960200151878b606001518b61241f565b818715613098578288111561306a576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87945084900361309861228f60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b6130a8878b602001515f8461241f565b50613108565b6130c2868a602001518c606001518561241f565b60208901516130d790305f6122348b8d615072565b86156131085760035487945061310890610100900473ffffffffffffffffffffffffffffffffffffffff168861342e565b6131a96040518061014001604052808381526020018b6020015173ffffffffffffffffffffffffffffffffffffffff1681526020018873ffffffffffffffffffffffffffffffffffffffff1681526020018b60c00151600181111561316f5761316f6150dc565b81526020018b6060015181526020018a81526020018481526020018981526020018b610140015181526020018b61012001518152506139f1565b5050965096945050505050565b5f6131ca8385608001518660a00151614542565b90508115610b98575f808560c0015160018111156131ea576131ea6150dc565b146131f557816131f7565b835b9050613206838260045461163d565b509392505050565b5f83815260086020819052604090912080549182901c9160ff168015613260576040517fee3b3d4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b821561326c5782613272565b84608001515b9250828411156132ae576040517fe2cc6ad600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b838303925082158360081b178083555050509392505050565b5f85156133095781156133045750806133046132fe60035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b8361342e565b613386565b828215613378578383111561334a576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82915081900361337861286460035473ffffffffffffffffffffffffffffffffffffffff6101009091041690565b61338430895f8461241f565b505b6134236040518061014001604052808781526020018973ffffffffffffffffffffffffffffffffffffffff1681526020013073ffffffffffffffffffffffffffffffffffffffff1681526020018a60c0015160018111156133e9576133e96150dc565b81526020018a6060015181526020018681526020018581526020018481526020018a610140015181526020018a61012001518152506128a3565b979650505050505050565b5f7f55bb3cade9d43b798a4fe5ffdd05024b2d7870df53920673bfc7e68047cd0ab19050815f52828160205fa2505050565b7f000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb3073ffffffffffffffffffffffffffffffffffffffff8516036134a957612431818484614558565b6116a981858585614563565b6116a97f0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea04760458585858561456f565b6134ea614768565b6134f586868461436b565b5f5f6135028787876123e7565b60c089015160608a015160208b015193955091935081810292918390039161355291309085905f90600181111561353b5761353b6150dc565b14613546575f613548565b8a5b612234908d615072565b6040518061014001604052808481526020018981526020018581526020018a6020015173ffffffffffffffffffffffffffffffffffffffff1681526020018281526020018a60c0015160018111156135ac576135ac6150dc565b81526020018881526020018a610140015181526020018a610120015181526020018a6060015181525094505050505095945050505050565b5f61360660408051600280825260016020830152818301526060810190915290565b90507f000000000000000000000000ada100874d00e3331d00f2007a9c336a6500971873ffffffffffffffffffffffffffffffffffffffff166372ce42757f000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb5b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526136a791905f908890879089906004016151ab565b5f604051808303815f87803b1580156136be575f5ffd5b505af1158015611971573d5f5f3e3d5ffd5b5f6136f260408051600280825260016020830152818301526060810190915290565b90507f000000000000000000000000ada100874d00e3331d00f2007a9c336a6500971873ffffffffffffffffffffffffffffffffffffffff16639e7212ad7f000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb613666565b604082015160c08301515f919081101561379c576040517f5de76cda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360c0015184604001510390508360c0015191506137c430856060015186608001518461241f565b60c0840151156137fa576003546137fa90610100900473ffffffffffffffffffffffffffffffffffffffff168560c0015161342e565b6138aa604051806101400160405280865f01518152602001866060015173ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1681526020018660a001516001811115613864576138646150dc565b8152602001866101200151815260200186602001518152602001866040015181526020018660c0015181526020018660e0015181526020018661010001518152506139f1565b5092915050565b5f6138ca3084606001518560800151866040015161241f565b60c083015115613902575060c082015160035461390290610100900473ffffffffffffffffffffffffffffffffffffffff168261342e565b610934604051806101400160405280855f01518152602001856060015173ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018560a00151600181111561396c5761396c6150dc565b8152602001856101200151815260200185602001518152602001856040015181526020018560c0015181526020018560e0015181526020018561010001518152506139f1565b801561099957600354610100900473ffffffffffffffffffffffffffffffffffffffff166139e283825f8561241f565b6139ec818361342e565b505050565b5f7fd543adfd945773f1a62f74f0ee55a5e3b9b1a28262980ba90b1a89f2ea84d8ee9050604051606083015181526080830151602082015260a0830151604082015260c0830151606082015260e0830151608082015261010083015160a082015261012083015160c08201526040830151602084015184518460e085a4505050565b5f8373ffffffffffffffffffffffffffffffffffffffff16613a958484614615565b73ffffffffffffffffffffffffffffffffffffffff1614949350505050565b5f73ffffffffffffffffffffffffffffffffffffffff841615610b9857604051843b613b6f57825160408114613af25760418114613b2c5750613206565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16606052613b3f565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5180861860601b3d119250505f60605280604052613206565b631626ba7e60e01b808252846004830152602482016040815284516020018060448501828860045afa905060208260443d01868b5afa9151911691141695945050505050565b5f7f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47808380099150808283098181820990508181840992508183850993508184840992508183840990508181820982818309905082818209905082818209905082818309915082828609945082858609915082828309915082828509935082848509915082828309915082828309915082828509915082828609945082858609915082828309915082828309915082828609915082828509935082848609945082858609915082828309915082828509935082848509915082828309905082818209905082818209905082818309915082828609945082858509935082848509915082828309915082828309915082828609945082858609915082828309915082828609915082828309915082828309915082828609915082828509935082848509915082828309905082818209905082818309905082818509905082818209905082818209905082818209905082818209905082818309915082828609945082858609915082828609915082828509935082848509915082828509915082828309915082828309905082818309905082818209838182099050838182099050838182099050838182099050838183099150508281830991508282860994508285850993508284850991508282860994508285850993508284860994508285850993508284860994508285860991508282860991508282830991508282850993508284850991508282830991508282860994508285850993508284850991508282850991508282860994508285850993508284860994508285850993508284850991508282830991508282850991508282860994508285860991508282860991508282850993508284860994508285850993508284860994508285850993508284850991508282850991508282830991508282860994508285850993508284850991508282850991508282830991508282860994508285860991508282830990508281820990508281830990508281860990508281820990508281820990508281820990508281820990508281830991508282850993508284860994508285850993508284860994508285860991508282860991508282830991508282830991508282830991508282860991508282850993508284850991508282850991508282830991508282860994508285860991508282860991508282850993508284860994508285860991508282830991508282850993508284860994508285860991508282850993508284860994508285850993508284850991508282850991508282860994508285850993508284850991508282850991508282830991508282830991508282860994508285860991508282830991508282830991508282860991508282850993508284860994508285860991508282860990508281820990508281820990508281830991508282850993508284850991508282860994508285850993508284860994508285850993508284860994508285850993508284850991508282850990508281850991508282830991508282830991508282820991505081818509935081848409925081838509935081848409925081838509935081848509905081818509905081818409925050808284099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808384099250808383099392505050565b5f81600281111561437e5761437e6150dc565b036144245781606001518360600151146143c4576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160a001518360a001516143d89190615085565b826080015184608001516143ec9190615085565b10156139ec576040517f7f9a6f4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8160600151836060015103614465576040517fa0b9446500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001816002811115614479576144796150dc565b036144c4578160a001518360a001516144929190615085565b83608001518360a001516144a69190615085565b83608001518560a001516144ba9190615085565b6143ec9190615072565b816080015183608001516144d89190615085565b83608001518360a001516144ec9190615085565b83608001518560a001516145009190615085565b61450a9190615072565b11156139ec576040517f7f9a6f4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8261454e8386615085565b611b5b91906150c9565b6139ec8383836146b3565b6116a984848484614706565b6040517ff242432a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528481166024830152604482018490526064820183905260a060848301525f60a483015286169063f242432a9060c4015f604051808303815f87803b1580156145f8575f5ffd5b505af115801561460a573d5f5f3e3d5ffd5b505050505050505050565b5f604051825160408114614631576041811461466b57506146a6565b604084015160ff81901c601b016020527f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660605261467e565b60608401515f1a60205260408401516060525b50835f5260208301516040526020600160805f60015afa5191505f606052806040523d6138aa575b638baa579f5f526004601cfd5b81601452806034526fa9059cbb0000000000000000000000005f5260205f604460105f875af18060015f5114166146fc57803d853b1517106146fc576390b8ec185f526004601cfd5b505f603452505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c5260205f6064601c5f895af18060015f51141661475a57803d873b15171061475a57637939f4245f526004601cfd5b505f60605260405250505050565b6040518061014001604052805f81526020015f81526020015f81526020015f73ffffffffffffffffffffffffffffffffffffffff1681526020015f81526020015f60018111156147ba576147ba6150dc565b81526020015f81526020015f81526020015f81526020015f81525090565b5f602082840312156147e8575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610b98575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051610180810167ffffffffffffffff8111828210171561486857614868614817565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156148b5576148b5614817565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146148e0575f5ffd5b919050565b8035600281106148e0575f5ffd5b8035600481106148e0575f5ffd5b5f82601f830112614910575f5ffd5b813567ffffffffffffffff81111561492a5761492a614817565b61495b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161486e565b81815284602083860101111561496f575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f610180828403121561499c575f5ffd5b6149a4614844565b8235815290506149b6602083016148bd565b60208201526149c7604083016148bd565b6040820152606082810135908201526080808301359082015260a080830135908201526149f660c083016148e5565b60c0820152614a0760e083016148f3565b60e082015261010082810135908201526101208083013590820152610140808301359082015261016082013567ffffffffffffffff811115614a47575f5ffd5b614a5384828501614901565b6101608301525092915050565b5f60208284031215614a70575f5ffd5b813567ffffffffffffffff811115614a86575f5ffd5b611b5b8482850161498b565b5f5f60408385031215614aa3575f5ffd5b50508035926020909101359150565b5f60208284031215614ac2575f5ffd5b610b98826148bd565b5f60208284031215614adb575f5ffd5b5035919050565b5f67ffffffffffffffff821115614afb57614afb614817565b5060051b60200190565b5f82601f830112614b14575f5ffd5b8135614b27614b2282614ae2565b61486e565b8082825260208201915060208360051b860101925085831115614b48575f5ffd5b602085015b83811015614b8957803567ffffffffffffffff811115614b6b575f5ffd5b614b7a886020838a010161498b565b84525060209283019201614b4d565b5095945050505050565b5f82601f830112614ba2575f5ffd5b8135614bb0614b2282614ae2565b8082825260208201915060208360051b860101925085831115614bd1575f5ffd5b602085015b83811015614b89578035835260209283019201614bd6565b5f5f5f5f5f5f5f60e0888a031215614c04575f5ffd5b87359650602088013567ffffffffffffffff811115614c21575f5ffd5b614c2d8a828b0161498b565b965050604088013567ffffffffffffffff811115614c49575f5ffd5b614c558a828b01614b05565b95505060608801359350608088013567ffffffffffffffff811115614c78575f5ffd5b614c848a828b01614b93565b93505060a0880135915060c088013567ffffffffffffffff811115614ca7575f5ffd5b614cb38a828b01614b93565b91505092959891949750929550565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b5f8151808452602084019350602083015f5b82811015614d3e578151865260209586019590910190600101614d20565b5093949350505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f614d8260e0830189614cc2565b8281036040840152614d948189614cc2565b905086606084015273ffffffffffffffffffffffffffffffffffffffff861660808401528460a084015282810360c0840152614dd08185614d0e565b9a9950505050505050505050565b5f5f83601f840112614dee575f5ffd5b50813567ffffffffffffffff811115614e05575f5ffd5b6020830191508360208260051b8501011115614e1f575f5ffd5b9250929050565b5f5f83601f840112614e36575f5ffd5b50813567ffffffffffffffff811115614e4d575f5ffd5b602083019150836020828501011115614e1f575f5ffd5b5f5f5f5f5f5f5f5f60a0898b031215614e7b575f5ffd5b614e84896148bd565b9750614e9260208a016148bd565b9650604089013567ffffffffffffffff811115614ead575f5ffd5b614eb98b828c01614dde565b909750955050606089013567ffffffffffffffff811115614ed8575f5ffd5b614ee48b828c01614dde565b909550935050608089013567ffffffffffffffff811115614f03575f5ffd5b614f0f8b828c01614e26565b999c989b5096995094979396929594505050565b5f5f5f5f5f5f60a08789031215614f38575f5ffd5b614f41876148bd565b9550614f4f602088016148bd565b94506040870135935060608701359250608087013567ffffffffffffffff811115614f78575f5ffd5b614f8489828a01614e26565b979a9699509497509295939492505050565b5f5f60408385031215614fa7575f5ffd5b82359150602083013567ffffffffffffffff811115614fc4575f5ffd5b614fd08582860161498b565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f8161501557615015614fda565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361506b5761506b614fda565b5060010190565b8082018082111561093457610934614fda565b808202811582820484141761093457610934614fda565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f826150d7576150d761509c565b500490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b8181038181111561093457610934614fda565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f60208284031215615159575f5ffd5b5051919050565b5f8261516e5761516e61509c565b500690565b5f82518060208501845e5f920191825250919050565b5f5f6040838503121561519a575f5ffd5b505080516020909101519092909150565b73ffffffffffffffffffffffffffffffffffffffff8616815284602082015283604082015260a060608201525f6151e560a0830185614d0e565b9050826080830152969550505050505056fea26469706673582212202aaf21d5cc0520703a08ce1ecc89fb6dc5909644172b314443b6706537069eb064736f6c63430008220033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000003dce0a29139a851da1dfca56af8e8a6440b4d952000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea04760450000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174000000000000000000000000ada100874d00e3331d00f2007a9c336a65009718000000000000000000000000ab45c5a4b0c941a2f231c04c3f49182e1a254052000000000000000000000000aacfeea03eb1561c4e67d661e40682bd20e3541b000000000000000000000000115f48dc2a731aa16251c6d6e1befc42f92accc9

-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : admin (address): 0x3dcE0a29139A851Da1dFCa56Af8e8a6440b4D952
Arg [2] : collateral (address): 0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB
Arg [3] : ctf (address): 0x4D97DCd97eC945f40cF65F87097ACe5EA0476045
Arg [4] : ctfCollateral (address): 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
Arg [5] : outcomeTokenFactory (address): 0xADa100874d00e3331D00F2007a9c336a65009718
Arg [6] : proxyFactory (address): 0xaB45c5A4B0c941a2F231C04C3f49182e1A254052
Arg [7] : safeFactory (address): 0xaacFeEa03eb1561C4e67d661e40682Bd20E3541b
Arg [8] : feeReceiver (address): 0x115F48DC2A731aA16251c6d6e1BEfC42f92Accc9


-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000003dce0a29139a851da1dfca56af8e8a6440b4d952
Arg [1] : 000000000000000000000000c011a7e12a19f7b1f670d46f03b03f3342e82dfb
Arg [2] : 0000000000000000000000004d97dcd97ec945f40cf65f87097ace5ea0476045
Arg [3] : 0000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174
Arg [4] : 000000000000000000000000ada100874d00e3331d00f2007a9c336a65009718
Arg [5] : 000000000000000000000000ab45c5a4b0c941a2f231c04c3f49182e1a254052
Arg [6] : 000000000000000000000000aacfeea03eb1561c4e67d661e40682bd20e3541b
Arg [7] : 000000000000000000000000115f48dc2a731aa16251c6d6e1befc42f92accc9


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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