{"sources":{"src/PheromoneMarketplace.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SafeERC20} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {IERC721} from \"@openzeppelin/contracts/token/ERC721/IERC721.sol\";\nimport {Ownable, Ownable2Step} from \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport {ReentrancyGuard} from \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\nimport {ITradeGate} from \"./ITradeGate.sol\";\n\n/// @dev The collection exposes a per-token transfer counter the marketplace uses to detect stale listings.\ninterface IPheromoneAnts is IERC721 {\n    function transferCount(uint256 tokenId) external view returns (uint256);\n}\n\n/// @title Pheromone Marketplace\n/// @notice Fixed-price listings of Pheromone Ants in USDT, open to every wallet. A sale settles in one\n///         transaction: the buyer's USDT goes to the seller and the fee vault, and the ant moves to the\n///         buyer, with no escrow. The fee is 5% of the price, fixed in the bytecode.\n/// @dev Listings never hold the token. The seller keeps the ant and gives the marketplace a transfer\n///      approval; a listing is automatically inactive as soon as the ant moves (transferCount changes),\n///      the approval is revoked, the listing expires, or a trade gate blocks the token.\n///\n///      Revoking the approval or a gate block does NOT remove the listing. If the approval is granted\n///      again, or the gate stops blocking, the listing is live again at its original price until it\n///      expires. A seller who no longer wants to sell must call `cancel` (or `cancelMany`); the site and\n///      the MCP delist flows always call `cancel`, and the relist flow cancels before approving. A colony\n///      lock module calls `cancel` for the token it locks: the installed gate may cancel any listing it\n///      currently blocks, so the lock and the cancel can happen in the same transaction.\n///\n///      The NFT is moved with `transferFrom` (not `safeTransferFrom`) on purpose: buyers are never\n///      called back during settlement, which removes the receiver-hook reentrancy surface. Contract\n///      wallets that cannot hold ERC-721s must not buy.\n///\n///      `buyMany` sweeps several listings in one transaction through the same settlement path as `buy`\n///      (`_settle`), under one reentrancy guard, with at most `MAX_SWEEP_ORDERS` orders per call.\ncontract PheromoneMarketplace is Ownable2Step, ReentrancyGuard {\n    using SafeERC20 for IERC20;\n\n    struct Listing {\n        address seller;\n        uint96 price; // USDT, 18 decimals\n        uint64 expiry; // unix seconds; the listing is dead once block.timestamp >= expiry\n        uint64 transferCount; // the token's transfer counter when it was listed\n    }\n\n    /// @notice One item of a sweep: the same three values a `buy` takes.\n    struct BuyOrder {\n        uint256 tokenId;\n        uint256 maxPrice;\n        uint64 expectedTransferCount;\n    }\n\n    uint256 public constant FEE_BPS = 500;\n    uint256 public constant BPS = 10_000;\n    /// @notice Listings live at most 30 days, which bounds how long a forgotten listing can come back.\n    uint64 public constant MAX_LISTING_DURATION = 30 days;\n    uint256 public constant GATE_DELAY = 24 hours;\n    /// @notice A gate proposal must be applied within this window after its notice period, or re-made.\n    uint256 public constant GATE_APPLY_WINDOW = 7 days;\n    /// @notice Most orders a single `buyMany` call accepts.\n    uint256 public constant MAX_SWEEP_ORDERS = 50;\n\n    IPheromoneAnts public immutable nft;\n    IERC20 public immutable usdt;\n    address public immutable vault;\n\n    mapping(uint256 => Listing) public listings;\n\n    /// @notice Optional hook that can refuse trades (zero = no gate). Gates must be immutable contracts.\n    address public tradeGate;\n    address public pendingTradeGate;\n    uint256 public tradeGateEffectiveAt;\n    bool public tradeGateLocked;\n    bool public paused;\n\n    /// @dev `transferCount` is the value recorded in the listing, so `buy(tokenId, price, transferCount)`\n    ///      can be built from the event alone, without reading live state that may already have moved.\n    event Listed(uint256 indexed tokenId, address indexed seller, uint256 price, uint64 expiry, uint64 transferCount);\n    event Cancelled(uint256 indexed tokenId, address indexed seller);\n    event Sold(uint256 indexed tokenId, address indexed seller, address indexed buyer, uint256 price, uint256 fee);\n    /// @dev Closes a `buyMany` batch that bought at least one ant; every item also emitted `Sold`.\n    event Swept(address indexed buyer, uint256 bought, uint256 spent);\n    event Paused(address indexed by);\n    event Unpaused();\n    event TradeGateProposed(address indexed gate, uint256 effectiveAt);\n    event TradeGateApplied(address indexed gate);\n    event TradeGateProposalCancelled();\n    event TradeGateLocked();\n\n    error ZeroAddress();\n    error NotOwnerOfToken();\n    error InvalidPrice();\n    error InvalidExpiry();\n    error NotApproved();\n    error TradeBlocked();\n    error NotListed();\n    error ListingStale();\n    error PriceAboveMax(uint256 price);\n    error TransferCountMismatch(uint64 expected);\n    error SelfPurchase();\n    error TooManyOrders();\n    error DuplicateOrder(uint256 tokenId);\n    error MaxTotalExceeded(uint256 wouldSpend);\n    error IsPaused();\n    error NotPaused();\n    error NotSellerOrStale();\n    error Locked();\n    error NoProposal();\n    error TimelockActive(uint256 effectiveAt);\n    error ProposalExpired(uint256 deadline);\n    error GateNotAContract();\n    error GateMalformed();\n    error RenounceDisabled();\n\n    constructor(address owner_, address nft_, address usdt_, address vault_) Ownable(owner_) {\n        if (nft_ == address(0) || usdt_ == address(0) || vault_ == address(0)) revert ZeroAddress();\n        nft = IPheromoneAnts(nft_);\n        usdt = IERC20(usdt_);\n        vault = vault_;\n    }\n\n    modifier whenNotPaused() {\n        if (paused) revert IsPaused();\n        _;\n    }\n\n    // ---------------------------------------------------------------- trading\n\n    /// @notice List `tokenId` at `price` USDT until `expiry` (at most 30 days ahead).\n    /// @dev The marketplace must already be approved for the token (`approve` or `setApprovalForAll`).\n    ///      Listing a token that already has a listing replaces it: `Cancelled` is emitted for the old\n    ///      listing before `Listed`, so indexers can treat the two events as a plain sequence.\n    function list(uint256 tokenId, uint256 price, uint64 expiry) external {\n        if (paused) revert IsPaused();\n        if (price == 0 || price > type(uint96).max) revert InvalidPrice();\n        if (expiry <= block.timestamp || expiry > block.timestamp + MAX_LISTING_DURATION) revert InvalidExpiry();\n        if (nft.ownerOf(tokenId) != msg.sender) revert NotOwnerOfToken();\n        if (!_approvedFor(msg.sender, tokenId)) revert NotApproved();\n        if (!_gateAllows(tokenId)) revert TradeBlocked();\n\n        address previous = listings[tokenId].seller;\n        if (previous != address(0)) emit Cancelled(tokenId, previous);\n\n        uint64 count = uint64(nft.transferCount(tokenId));\n        listings[tokenId] = Listing({seller: msg.sender, price: uint96(price), expiry: expiry, transferCount: count});\n        emit Listed(tokenId, msg.sender, price, expiry, count);\n    }\n\n    /// @notice Remove a listing. The seller may always cancel. Anyone may clear a listing that is\n    ///         terminally stale: expired, the seller no longer owns the token, or the token moved since\n    ///         listing. The installed trade gate may cancel a listing it currently blocks (that is how a\n    ///         lock module removes the listing of an ant it locks). A revoked approval or a gate block is\n    ///         not terminal (the listing can come back), so nobody else can remove those.\n    function cancel(uint256 tokenId) external {\n        Listing memory l = listings[tokenId];\n        if (l.seller == address(0)) revert NotListed();\n        if (msg.sender != l.seller && !_gateMayCancel(tokenId) && !_terminallyStale(tokenId, l)) revert NotSellerOrStale();\n        delete listings[tokenId];\n        emit Cancelled(tokenId, l.seller);\n    }\n\n    /// @notice Remove several of the caller's own listings in one transaction. Entries that are not\n    ///         listed, or listed by someone else (for example sold since the batch was built), are\n    ///         skipped without an event so a single race never blocks the rest of the batch.\n    function cancelMany(uint256[] calldata tokenIds) external {\n        for (uint256 i = 0; i < tokenIds.length; i++) {\n            uint256 tokenId = tokenIds[i];\n            if (listings[tokenId].seller != msg.sender) continue;\n            delete listings[tokenId];\n            emit Cancelled(tokenId, msg.sender);\n        }\n    }\n\n    /// @notice Buy `tokenId`. Reverts if the price moved above `maxPrice` or the token moved since the\n    ///         listing (`expectedTransferCount`). Payment and the ant settle in this one transaction.\n    function buy(uint256 tokenId, uint256 maxPrice, uint64 expectedTransferCount) external nonReentrant whenNotPaused {\n        _settle(tokenId, maxPrice, expectedTransferCount, msg.sender);\n    }\n\n    /// @notice Buy several listings in one transaction (a sweep). Orders settle in calldata order, each\n    ///         exactly like `buy`, at most `MAX_SWEEP_ORDERS` per call, and a token id may appear only once\n    ///         per batch (`DuplicateOrder`, checked before anything settles).\n    ///         `allOrNothing == true`: every order must settle or the whole call reverts with the error\n    ///         `buy` would give for that order, or `MaxTotalExceeded` when an order would push the running\n    ///         total above `maxTotal`.\n    ///         `allOrNothing == false`: an order that cannot be bought right now (no listing, sold, expired,\n    ///         token moved, relisted at another count, approval revoked, gate-blocked, price above the\n    ///         order's `maxPrice`, or the caller's own listing) is skipped without an event and the loop\n    ///         goes on; the first order that would push the total above `maxTotal` stops the loop instead.\n    ///         `Swept` is emitted once at the end when at least one ant was bought. The whole batch runs\n    ///         under one reentrancy guard.\n    /// @return bought How many ants settled.\n    /// @return spent The USDT paid for them.\n    function buyMany(BuyOrder[] calldata orders, uint256 maxTotal, bool allOrNothing)\n        external\n        nonReentrant\n        whenNotPaused\n        returns (uint256 bought, uint256 spent)\n    {\n        uint256 n = orders.length;\n        if (n > MAX_SWEEP_ORDERS) revert TooManyOrders();\n        for (uint256 i = 1; i < n; i++) {\n            uint256 tokenId = orders[i].tokenId;\n            for (uint256 j = 0; j < i; j++) {\n                if (orders[j].tokenId == tokenId) revert DuplicateOrder(tokenId);\n            }\n        }\n        for (uint256 i = 0; i < n; i++) {\n            BuyOrder calldata o = orders[i];\n            (bool ok, uint256 price) = _purchasable(o.tokenId, o.maxPrice, o.expectedTransferCount, msg.sender);\n            if (!ok && !allOrNothing) continue;\n            if (ok && spent + price > maxTotal) {\n                if (!allOrNothing) break;\n                revert MaxTotalExceeded(spent + price);\n            }\n            // Not purchasable in all-or-nothing mode: `_settle` reverts with the reason `buy` would give.\n            spent += _settle(o.tokenId, o.maxPrice, o.expectedTransferCount, msg.sender);\n            bought += 1;\n        }\n        if (bought > 0) emit Swept(msg.sender, bought, spent);\n    }\n\n    // ---------------------------------------------------------------- views\n\n    /// @notice The stored listing and whether it can currently be bought. Never reverts because of a gate.\n    function listingOf(uint256 tokenId) external view returns (Listing memory listing, bool active) {\n        listing = listings[tokenId];\n        active = listing.seller != address(0) && _isActive(tokenId, listing);\n    }\n\n    function isActive(uint256 tokenId) external view returns (bool) {\n        Listing memory l = listings[tokenId];\n        return l.seller != address(0) && _isActive(tokenId, l);\n    }\n\n    /// @notice Price, fee and what the seller receives for a listed token (stale listings included;\n    ///         check `isActive` before relying on it).\n    function quote(uint256 tokenId) external view returns (uint256 price, uint256 fee, uint256 sellerProceeds) {\n        Listing memory l = listings[tokenId];\n        if (l.seller == address(0)) revert NotListed();\n        price = l.price;\n        fee = (price * FEE_BPS) / BPS;\n        sellerProceeds = price - fee;\n    }\n\n    /// @notice Prices and activity of several tokens at once, for building a sweep. `prices[i]` is the\n    ///         listed price (zero when the token has no listing) and `active[i]` whether it can be bought\n    ///         right now; `total` sums the prices of the active ones, which is what a sweep of exactly\n    ///         those tokens would spend. Never reverts.\n    function quoteMany(uint256[] calldata tokenIds)\n        external\n        view\n        returns (uint256[] memory prices, bool[] memory active, uint256 total)\n    {\n        uint256 n = tokenIds.length;\n        prices = new uint256[](n);\n        active = new bool[](n);\n        for (uint256 i = 0; i < n; i++) {\n            uint256 tokenId = tokenIds[i];\n            Listing memory l = listings[tokenId];\n            if (l.seller == address(0)) continue;\n            prices[i] = l.price;\n            if (_isActive(tokenId, l)) {\n                active[i] = true;\n                total += l.price;\n            }\n        }\n    }\n\n    // ---------------------------------------------------------------- admin\n\n    function pause() external onlyOwner {\n        if (paused) revert IsPaused();\n        paused = true;\n        emit Paused(msg.sender);\n    }\n\n    function unpause() external onlyOwner {\n        if (!paused) revert NotPaused();\n        paused = false;\n        emit Unpaused();\n    }\n\n    /// @notice Propose a new trade gate (or zero to remove it). Takes effect after `GATE_DELAY` and must be\n    ///         applied within `GATE_APPLY_WINDOW` after that. The gate is probed once here: it must answer\n    ///         `canTrade` with a well-formed boolean or the proposal is refused.\n    function proposeTradeGate(address gate) external onlyOwner {\n        if (tradeGateLocked) revert Locked();\n        if (gate != address(0)) {\n            if (gate.code.length == 0) revert GateNotAContract();\n            _probeGate(gate);\n        }\n        pendingTradeGate = gate;\n        tradeGateEffectiveAt = block.timestamp + GATE_DELAY;\n        emit TradeGateProposed(gate, tradeGateEffectiveAt);\n    }\n\n    function cancelTradeGateProposal() external onlyOwner {\n        if (tradeGateEffectiveAt == 0) revert NoProposal();\n        delete pendingTradeGate;\n        delete tradeGateEffectiveAt;\n        emit TradeGateProposalCancelled();\n    }\n\n    /// @notice Install the proposed gate once its notice period has passed. The gate is probed again\n    ///         here, so a gate whose code changed since the proposal cannot be installed fail-closed.\n    function applyTradeGate() external onlyOwner {\n        if (tradeGateLocked) revert Locked();\n        uint256 effectiveAt = tradeGateEffectiveAt;\n        if (effectiveAt == 0) revert NoProposal();\n        if (block.timestamp < effectiveAt) revert TimelockActive(effectiveAt);\n        if (block.timestamp > effectiveAt + GATE_APPLY_WINDOW) revert ProposalExpired(effectiveAt + GATE_APPLY_WINDOW);\n        address gate = pendingTradeGate;\n        if (gate != address(0)) {\n            if (gate.code.length == 0) revert GateNotAContract();\n            _probeGate(gate);\n        }\n        tradeGate = gate;\n        delete pendingTradeGate;\n        delete tradeGateEffectiveAt;\n        emit TradeGateApplied(gate);\n    }\n\n    /// @notice Freeze the trade gate forever (one way). Refused while the current gate answers badly.\n    function lockTradeGate() external onlyOwner {\n        if (tradeGateLocked) revert Locked();\n        if (tradeGate != address(0)) _probeGate(tradeGate);\n        tradeGateLocked = true;\n        delete pendingTradeGate;\n        delete tradeGateEffectiveAt;\n        emit TradeGateLocked();\n    }\n\n    /// @notice Disabled: ownership can only move through `transferOwnership` + `acceptOwnership`.\n    function renounceOwnership() public pure override {\n        revert RenounceDisabled();\n    }\n\n    // ---------------------------------------------------------------- internals\n\n    /// @dev The one settlement path, shared by `buy` and `buyMany`: the checks `buy` documents, then the\n    ///      listing is deleted before any external call, the fee goes to the vault, the rest to the seller,\n    ///      the ant moves to `buyer` and `Sold` is emitted. Returns the price paid.\n    function _settle(uint256 tokenId, uint256 maxPrice, uint64 expectedTransferCount, address buyer)\n        internal\n        returns (uint256 price)\n    {\n        Listing memory l = listings[tokenId];\n        if (l.seller == address(0)) revert NotListed();\n        if (l.transferCount != expectedTransferCount) revert TransferCountMismatch(l.transferCount);\n        if (!_isActive(tokenId, l)) revert ListingStale();\n        if (l.price > maxPrice) revert PriceAboveMax(l.price);\n        if (buyer == l.seller) revert SelfPurchase();\n\n        delete listings[tokenId];\n\n        price = l.price;\n        uint256 fee = (price * FEE_BPS) / BPS;\n        usdt.safeTransferFrom(buyer, vault, fee);\n        usdt.safeTransferFrom(buyer, l.seller, price - fee);\n        nft.transferFrom(l.seller, buyer, tokenId);\n\n        emit Sold(tokenId, l.seller, buyer, price, fee);\n    }\n\n    /// @dev The checks of `_settle` without reverting: whether `buyer` could buy `tokenId` right now\n    ///      under these constraints, and the listed price. `buyMany` uses it to skip orders and to\n    ///      check the budget before settling.\n    function _purchasable(uint256 tokenId, uint256 maxPrice, uint64 expectedTransferCount, address buyer)\n        internal\n        view\n        returns (bool ok, uint256 price)\n    {\n        Listing memory l = listings[tokenId];\n        if (l.seller == address(0) || l.seller == buyer) return (false, 0);\n        if (l.transferCount != expectedTransferCount || l.price > maxPrice) return (false, 0);\n        return (_isActive(tokenId, l), l.price);\n    }\n\n    function _isActive(uint256 tokenId, Listing memory l) internal view returns (bool) {\n        if (_terminallyStale(tokenId, l)) return false;\n        if (!_approvedFor(l.seller, tokenId)) return false;\n        return _gateAllows(tokenId);\n    }\n\n    /// @dev Staleness that cannot be undone: the listing expired or the token changed hands.\n    function _terminallyStale(uint256 tokenId, Listing memory l) internal view returns (bool) {\n        if (block.timestamp >= l.expiry) return true;\n        if (nft.ownerOf(tokenId) != l.seller) return true;\n        return nft.transferCount(tokenId) != l.transferCount;\n    }\n\n    function _approvedFor(address seller, uint256 tokenId) internal view returns (bool) {\n        return nft.getApproved(tokenId) == address(this) || nft.isApprovedForAll(seller, address(this));\n    }\n\n    /// @dev Strict gate call: only a successful call returning exactly one word equal to 1 allows the trade.\n    ///      A reverting, empty, malformed or non-boolean answer counts as \"blocked\", and never reverts here.\n    function _gateAllows(uint256 tokenId) internal view returns (bool) {\n        address gate = tradeGate;\n        if (gate == address(0)) return true;\n        (bool ok, uint256 size, uint256 word) = _callGate(gate, tokenId);\n        return ok && size == 32 && word == 1;\n    }\n\n    /// @dev The installed gate may cancel a listing it currently blocks.\n    function _gateMayCancel(uint256 tokenId) internal view returns (bool) {\n        address gate = tradeGate;\n        return gate != address(0) && msg.sender == gate && !_gateAllows(tokenId);\n    }\n\n    /// @notice The id a gate is probed with when it is proposed, applied or locked. A gate must answer\n    ///         `canTrade` for every id, including ids that do not exist, without reverting.\n    uint256 public constant GATE_PROBE_ID = type(uint256).max;\n\n    /// @dev A gate must answer `canTrade(GATE_PROBE_ID)` with a well-formed boolean before it can be\n    ///      installed or locked.\n    function _probeGate(address gate) internal view {\n        (bool ok, uint256 size, uint256 word) = _callGate(gate, GATE_PROBE_ID);\n        if (!ok || size != 32 || word > 1) revert GateMalformed();\n    }\n\n    /// @dev Staticcall `canTrade(tokenId)` copying at most one word of return data into the scratch space,\n    ///      so a hostile gate cannot inflate gas by returning a huge payload. `size` is the full return\n    ///      data size, which the callers require to be exactly 32.\n    function _callGate(address gate, uint256 tokenId) internal view returns (bool ok, uint256 size, uint256 word) {\n        bytes memory data = abi.encodeCall(ITradeGate.canTrade, (tokenId));\n        assembly (\"memory-safe\") {\n            mstore(0x00, 0)\n            ok := staticcall(gas(), gate, add(data, 0x20), mload(data), 0x00, 0x20)\n            size := returndatasize()\n            word := mload(0x00)\n        }\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-20 standard as defined in the ERC.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC1363} from \"../../../interfaces/IERC1363.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC-20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    /**\n     * @dev An operation with an ERC-20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.\n     */\n    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {\n        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.\n     */\n    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {\n        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     *\n     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the \"client\"\n     * smart contract uses ERC-7674 to set temporary allowances, then the \"client\" smart contract should avoid using\n     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract\n     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     *\n     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function\n     * only sets the \"standard\" allowance. Any temporary allowance will remain active, in addition to the value being\n     * set here.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            safeTransfer(token, to, value);\n        } else if (!token.transferAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target\n     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function transferFromAndCallRelaxed(\n        IERC1363 token,\n        address from,\n        address to,\n        uint256 value,\n        bytes memory data\n    ) internal {\n        if (to.code.length == 0) {\n            safeTransferFrom(token, from, to, value);\n        } else if (!token.transferFromAndCall(from, to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no\n     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when\n     * targeting contracts.\n     *\n     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.\n     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}\n     * once without retrying, and relies on the returned value to be true.\n     *\n     * Reverts if the returned value is other than `true`.\n     */\n    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {\n        if (to.code.length == 0) {\n            forceApprove(token, to, value);\n        } else if (!token.approveAndCall(to, value, data)) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        uint256 returnSize;\n        uint256 returnValue;\n        assembly (\"memory-safe\") {\n            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n            // bubble errors\n            if iszero(success) {\n                let ptr := mload(0x40)\n                returndatacopy(ptr, 0, returndatasize())\n                revert(ptr, returndatasize())\n            }\n            returnSize := returndatasize()\n            returnValue := mload(0)\n        }\n\n        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        bool success;\n        uint256 returnSize;\n        uint256 returnValue;\n        assembly (\"memory-safe\") {\n            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)\n            returnSize := returndatasize()\n            returnValue := mload(0)\n        }\n        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC165} from \"../../utils/introspection/IERC165.sol\";\n\n/**\n * @dev Required interface of an ERC-721 compliant contract.\n */\ninterface IERC721 is IERC165 {\n    /**\n     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n     */\n    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n    /**\n     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n     */\n    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n    /**\n     * @dev Returns the number of tokens in ``owner``'s account.\n     */\n    function balanceOf(address owner) external view returns (uint256 balance);\n\n    /**\n     * @dev Returns the owner of the `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n\n    /**\n     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must exist and be owned by `from`.\n     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or\n     *   {setApprovalForAll}.\n     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon\n     *   a safe transfer.\n     *\n     * Emits a {Transfer} event.\n     */\n    function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Transfers `tokenId` token from `from` to `to`.\n     *\n     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721\n     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must\n     * understand this adds an external call which potentially creates a reentrancy vulnerability.\n     *\n     * Requirements:\n     *\n     * - `from` cannot be the zero address.\n     * - `to` cannot be the zero address.\n     * - `tokenId` token must be owned by `from`.\n     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 tokenId) external;\n\n    /**\n     * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n     * The approval is cleared when the token is transferred.\n     *\n     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n     *\n     * Requirements:\n     *\n     * - The caller must own the token or be an approved operator.\n     * - `tokenId` must exist.\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address to, uint256 tokenId) external;\n\n    /**\n     * @dev Approve or remove `operator` as an operator for the caller.\n     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n     *\n     * Requirements:\n     *\n     * - The `operator` cannot be the address zero.\n     *\n     * Emits an {ApprovalForAll} event.\n     */\n    function setApprovalForAll(address operator, bool approved) external;\n\n    /**\n     * @dev Returns the account approved for `tokenId` token.\n     *\n     * Requirements:\n     *\n     * - `tokenId` must exist.\n     */\n    function getApproved(uint256 tokenId) external view returns (address operator);\n\n    /**\n     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n     *\n     * See {setApprovalForAll}\n     */\n    function isApprovedForAll(address owner, address operator) external view returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.20;\n\nimport {Ownable} from \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * This extension of the {Ownable} contract includes a two-step mechanism to transfer\n * ownership, where the new owner must call {acceptOwnership} in order to replace the\n * old one. This can help prevent common mistakes, such as transfers of ownership to\n * incorrect accounts, or to contracts that are unable to interact with the\n * permission system.\n *\n * The initial owner is specified at deployment time in the constructor for `Ownable`. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n    address private _pendingOwner;\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        return _pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     *\n     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        _pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        delete _pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() public virtual {\n        address sender = _msgSender();\n        if (pendingOwner() != sender) {\n            revert OwnableUnauthorizedAccount(sender);\n        }\n        _transferOwnership(sender);\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,\n * consider using {ReentrancyGuardTransient} instead.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    uint256 private _status;\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    constructor() {\n        _status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        if (_status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        _status = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _status == ENTERED;\n    }\n}\n"},"src/ITradeGate.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/// @notice Optional hook the marketplace consults before listing or selling a token.\n///         Phase 2 colony mining installs one so that ants locked in a colony cannot be traded.\n/// @dev Implementations must be immutable contracts (no proxies, no admin-switchable logic): the\n///      marketplace's 24-hour notice covers only the gate address. `canTrade` is called with a staticcall\n///      and must return exactly one boolean word; any other answer counts as \"blocked\". It must never\n///      revert, for any id, including ids that do not exist: the marketplace probes a gate with\n///      `canTrade(type(uint256).max)` (`GATE_PROBE_ID`) before installing or locking it, and refuses a\n///      gate that reverts or answers with anything but a boolean. A lock module should call\n///      `PheromoneMarketplace.cancel(tokenId)` when it locks a listed token; the marketplace accepts that\n///      call from the installed gate for any token the gate currently blocks. \"Blocks\" is judged by the\n///      same fail-closed call: a gate whose `canTrade` reverts or answers badly while it calls `cancel`\n///      (for example behind a reentrancy guard on the view) would be allowed to cancel any listing, so\n///      `canTrade` must keep answering normally during the gate's own `cancel` call.\ninterface ITradeGate {\n    function canTrade(uint256 tokenId) external view returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)\n\npragma solidity >=0.6.2;\n\nimport {IERC20} from \"./IERC20.sol\";\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @title IERC1363\n * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].\n *\n * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract\n * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.\n */\ninterface IERC1363 is IERC20, IERC165 {\n    /*\n     * Note: the ERC-165 identifier for this interface is 0xb0202a11.\n     * 0xb0202a11 ===\n     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^\n     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^\n     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))\n     */\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism\n     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.\n     * @param from The address which you want to send tokens from.\n     * @param to The address which you want to transfer to.\n     * @param value The amount of tokens to be transferred.\n     * @param data Additional data with no specified format, sent in call to `to`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.\n     * @param spender The address which will spend the funds.\n     * @param value The amount of tokens to be spent.\n     * @param data Additional data with no specified format, sent in call to `spender`.\n     * @return A boolean value indicating whether the operation succeeded unless throwing.\n     */\n    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)\n\npragma solidity >=0.4.16;\n\n/**\n * @dev Interface of the ERC-165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[ERC].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},"lib/openzeppelin-contracts/contracts/access/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC20} from \"../token/ERC20/IERC20.sol\";\n"},"lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)\n\npragma solidity >=0.4.16;\n\nimport {IERC165} from \"../utils/introspection/IERC165.sol\";\n"},"lib/openzeppelin-contracts/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"}},"matchId":"57438005","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-10-28T20:52:37Z","match":"exact_match","chainId":"56","address":"0x64D7Cf8cF8E93D864d5C8a9B685E4497487c5a1F"}