WantedCyborgs

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "WantedCyborgs.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
 //            _       __            __           __     \r
 //           | |     / /___ _____  / /____  ____/ /      \r
 //           | | /| / / __ `/ __ \/ __/ _ \/ __  /       \r
 //           | |/ |/ / /_/ / / / / /_/  __/ /_/ /        \r
 //           |__/|__/\__,_/_/ /_/\__/\___/\__,_/         \r
 //          ______      __                               \r
 //         / ____/_  __/ /_  ____  _________ ______      \r
 //        / /   / / / / __ \/ __ \/ ___/ __ `/ ___/      \r
 //       / /___/ /_/ / /_/ / /_/ / /  / /_/ (__  )       \r
 //       \____/\__, /_.___/\____/_/   \__, /____/       \r
 //            /____/                 /____/       \r
 //\r
 // Creator: MadV0x\r
 // Deployer: Syntetik Labs\r
\r
import { ERC721A } from "lib/ERC721A/contracts/ERC721A.sol";\r
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";\r
import { ReentrancyGuard } from "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";\r
import { ERC721TransferValidator } from "src/lib/ERC721TransferValidator.sol";\r
import { ICreatorToken, ILegacyCreatorToken } from "src/interfaces/ICreatorToken.sol";\r
import { ITransferValidator721 } from "src/interfaces/ITransferValidator.sol";\r
import { ERC2981 } from "@openzeppelin/contracts/token/common/ERC2981.sol";\r
import { IERC2981 } from "lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol";\r
import { IERC165 } from "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol";\r
import { WANTEDErrorsAndEvents } from "WANTED/lib/WANTEDErrorsAndEvents.sol";\r
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";\r
\r
/**\r
* @title  WANTED Cyborgs (WANTED).\r
* @notice Implements Token holding time tracking.\r
* @notice Implements marketsPaused : Override approvals to restrict \r
*           listings on marketplaces until Hunt out.\r
* @notice Implements PreHunt (HuntFor) function (onlyOwner).\r
* @notice Implements Multistages Hunt with Dutch Auctions and standard HuntListing.\r
* @notice Implements Sell/transfer Ownership2steps.\r
* @notice Implements Freeze Metadata (contractURI and BaseURI).\r
* @notice Implements Limit Break's Creator Token Standards transfer.\r
*           validation for royalty enforcement.\r
* @notice ADDED Admin role & MasterHunter Position.\r
* @notice REMOVED from ERC721A: burn() => no burn of WANTEDs !!!\r
* @notice REMOVED from ERC721A: unused mintERC2309\r
*/\r
    \r
contract WantedCyborgs is ERC721A, ERC721TransferValidator, Ownable, ReentrancyGuard, ERC2981, WANTEDErrorsAndEvents {\r
\r
    using EnumerableSet for EnumerableSet.AddressSet;\r
    EnumerableSet.AddressSet private HuntList;\r
\r
    /// INITIALIZE HUNT PARAMETERS\r
    bool public marketsPaused = true;\r
    bool public HuntPaused = true;\r
    bool public HuntListActive = true;\r
    uint256 public HuntPrice = 0.05 ether;\r
    uint256 public stageMaxSupply = 1250;\r
    uint256 public immutable maxSupply = 5000;\r
    uint256 public walletHuntLimit = 1;\r
    \r
    // Dutch Auction parameters.\r
    uint256 public auctionStartTime = 0; // Timestamp when the auction begins. Defaults to 0 (inactive).\r
    uint256 public auctionDuration; // Total duration of the auction (in seconds).\r
    uint256 public auctionStartPrice;  // Starting auction price (in wei).\r
    uint256 public auctionEndPrice;  // Final (minimum) auction price (in wei).\r
    uint256 public auctionDropInterval; // Time interval (in seconds) between each price drop.\r
    uint256 public auctionDropPerStep; // The amount (in wei) that the price drops at each interval.\r
\r
    // @notice Price set for a contract Ownership transfer.   \r
    uint256 public ownershipPrice;\r
\r
    // @notice Address currently holding the MasterHunter position.\r
    address public MasterHunter;\r
\r
    // @notice ADMIN address.\r
    address public Admin;\r
\r
    // @notice Pending Owner address (if ownership transfer).\r
    address public pendingOwner;\r
\r
    // @notice Track the URI for contract metadata.\r
    string public contractURI;\r
\r
    // @notice Track if contract metadata is frozen (baseURI + contractURI).\r
    // @dev emit an event when frozen.\r
    bool public isMetadataFrozen = false;\r
\r
    // @dev Mapping from tokenId to tokenData.\r
    mapping(uint256 => uint96) private tokenHoldingStart;\r
\r
    constructor(\r
        string memory _name,\r
        string memory _symbol,\r
        string memory _baseURI\r
    ) ERC721A(_name, _symbol, _baseURI) ERC2981() Ownable(msg.sender) {\r
    }\r
\r
    /**\r
     * @notice Returns whether the interface is supported.\r
     *\r
     * @param interfaceId The interface id to check against.\r
     */\r
    function supportsInterface(bytes4 interfaceId)\r
        public\r
        view\r
        override(ERC721A, ERC2981)\r
        returns (bool)\r
    {\r
        return\r
            interfaceId == type(IERC165).interfaceId ||\r
            interfaceId == type(IERC2981).interfaceId ||\r
            interfaceId == type(ICreatorToken).interfaceId ||\r
            interfaceId == type(ILegacyCreatorToken).interfaceId ||\r
            interfaceId == 0x49064906 || // ERC-4906\r
            super.supportsInterface(interfaceId);\r
    }\r
\r
    /**\r
    * @notice Public hunting function supporting multiple types of sale stages.\r
    * @dev Handles simple HuntList, Dutch Auction, and public Hunt phases.\r
    *      Keeps `HuntListActive` enabled until the public Hunt stage(s) starts.\r
    *      Reverts if quantity is zero, Hunting is paused, or limits are exceeded.\r
    *      Refunds excess payment if overpaid.\r
    *\r
    * @param quantity Number of WANTED tokens to Hunt.\r
    */\r
    function hunt(uint256 quantity) nonReentrant external payable {\r
        // Perform initial verifications before proceeding.\r
        _checkBeforeHunt(quantity);\r
        \r
        uint256 totalPrice = HuntPrice * quantity;\r
\r
        // Adjust price if a Dutch Auction is active. \r
        if (auctionStartTime != 0) {\r
            totalPrice = getDutchAuctionCurrentPrice() * quantity;\r
        } \r
\r
        // Revert if insufficient payment. \r
        if (msg.value < totalPrice) {\r
            revert InvalidPaidPrice();\r
        }\r
        // Revert if quantity Hunted exceeds maxSupply:\r
        if (totalSupply() + quantity > maxSupply) {\r
            revert QuantityExceedMaxSupply();\r
        }\r
\r
        _safeMint(msg.sender, quantity);\r
        emit Hunted(msg.sender, quantity);\r
\r
        // Refund any excess payment.\r
        if (msg.value > totalPrice) {\r
            payable(msg.sender).transfer(msg.value - totalPrice);\r
        }\r
\r
        // Trigger Hunt out logic if max supply reached.\r
        if(totalSupply() == maxSupply) {\r
            _isHuntOut();\r
        }\r
    }\r
\r
    /**\r
    * @dev Marks the Hunting process as complete and emits a `HuntOutAchieved` event.\r
    *      Unlocks Marketplaces by setting `tradePaused` to false (unlock approvals).\r
    *\r
    * Note:\r
    *       This function assumes that the condition for "Hunt out" has already been met \r
    *       and is simply used to trigger related state changes and notifications.\r
    */\r
    function _isHuntOut() private {\r
        emit HuntOutAchieved(block.timestamp);\r
        marketsPaused = false;\r
    } \r
\r
    /**\r
    * @dev Internal validation function that runs multiple checks before Hunting.\r
    *\r
    * Reverts with:\r
    * - `ZeroQuantityOrPaused` if quantity is zero or Hunting is currently paused.\r
    * - `WalletHuntLimit` if Hunting this quantity exceeds the caller's wallet limit.\r
    * - `NotHuntListedOrPaused` if HuntList is active and caller is not HuntListed.\r
    * - `QuantityExceedsStageMaxSupply` if Hunting this quantity would exceed the current stage's max supply.\r
    *\r
    * @param quantity The number of tokens the caller intends to Hunt.\r
    */\r
    function _checkBeforeHunt(uint256 quantity) private view {\r
        // Check for zero quantity or paused Hunt.\r
        if (quantity == 0 || HuntPaused) {\r
            revert ZeroQuantityOrPaused();\r
        }\r
        // Check if the Hunted quantity exceeds the wallet's Hunt limit.\r
        if (_numberMinted(msg.sender) + quantity > walletHuntLimit) {\r
            revert WalletHuntLimit();\r
        }\r
        // If HuntList is active, verify caller is HuntListed.\r
        if (HuntListActive) {\r
            if(!HuntList.contains(msg.sender)){\r
                revert NotHuntListed();\r
            }\r
        }\r
         // Ensure Hunted quantity does not exceed current stage max supply.\r
        if (totalSupply() + quantity > stageMaxSupply) {\r
            revert QuantityExceedsStageMaxSupply();\r
        }\r
    }\r
\r
    /**\r
    * @notice Calculates and returns the current price of the token during the Dutch Auction.\r
    * @dev Reverts if the auction hasn't started (`auctionStartTime == 0` or is in the future) \r
    *      or if the auction duration has already elapsed.\r
    * \r
    * Note:    \r
    *      Price decreases over time in steps defined by `auctionDropInterval`, \r
    *      with each step reducing the price by `auctionDropPerStep`. \r
    *      The price will never drop below `auctionEndPrice`.\r
    *\r
    * @return The current Dutch Auction price in wei.\r
    */\r
    function getDutchAuctionCurrentPrice() public view returns (uint256) {\r
        if (auctionStartTime == 0 || block.timestamp < auctionStartTime) {\r
            revert DutchAuctionNotStarted();\r
        }\r
\r
        uint256 elapsed = block.timestamp - auctionStartTime;\r
\r
        if (elapsed >= auctionDuration) {\r
            revert DutchAuctionEnded();\r
        }\r
\r
        // Calculate number of steps passed\r
        uint256 steps = elapsed / auctionDropInterval;\r
\r
        // Calculate price drop based on steps\r
        uint256 discount = steps * auctionDropPerStep;\r
\r
        uint256 price = auctionStartPrice > discount\r
            ? auctionStartPrice - discount\r
            : auctionEndPrice;\r
\r
        // Ensure price doesn't go below auctionEndPrice\r
        if (price < auctionEndPrice) {\r
            price = auctionEndPrice;\r
        }\r
        return price;\r
    }\r
\r
    /**\r
    * @notice Returns the remaining time in the Dutch Auction.\r
    * @dev Return 0 if the auction hasn't started (`auctionStartTime == 0` or is in the future) \r
    *\r
    * @return The Dutch Auction remaining time in seconds.\r
    */\r
    function getAuctionRemainingTime() public view returns (uint256) {\r
        if (auctionStartTime == 0 || block.timestamp < auctionStartTime \r
            || block.timestamp > auctionStartTime + auctionDuration) {\r
            return 0;\r
        }\r
        return auctionStartTime + auctionDuration - block.timestamp;\r
    }\r
\r
    /**\r
    * @notice Allows the owner to Hunt new WANTED to a specified account.\r
    * @dev Intended for preHunting or forcing Hunt out for opening sales.\r
    *      Reverts if quantity is zero or if Hunting exceeds max supply.\r
    *\r
    * @param account The address receiving the Hunted tokens.\r
    * @param quantity The number of WANTED tokens to Hunt.\r
    */\r
    function HuntFor(address account, uint256 quantity) external { \r
        _isOwnerOrAdmin();\r
\r
        if (quantity == 0) {\r
            revert ZeroQuantity();\r
        } \r
\r
        if (totalSupply() + quantity > stageMaxSupply) {\r
            revert QuantityExceedsStageMaxSupply();\r
        }\r
\r
        if (totalSupply() + quantity > maxSupply) {\r
            revert QuantityExceedMaxSupply();\r
        } \r
\r
        _safeMint(account, quantity);\r
	    emit Hunted(account, quantity);\r
\r
        // Check if Hunt Out and call _isHuntOut() if true\r
        if(totalSupply() == maxSupply) {\r
            _isHuntOut();\r
        }\r
    }\r
\r
    /**\r
    * @notice Configure Hunting parameters and stages (Admin only).\r
    * @dev Allows toggling huntList activation, Hunt price, and max supply for the current stage.\r
    *\r
    * @param _HuntListActive Whether HuntList is active for the current Hunt stage.\r
    * @param _HuntPrice Price per token Hunt (in wei).\r
    * @param _stageMaxSupply Maximum number of tokens allowed to be Hunted in this stage.\r
    */\r
    function setHunt(\r
        bool _HuntListActive, \r
        uint256 _HuntPrice, \r
        uint256 _stageMaxSupply\r
        ) external {\r
        _isOwnerOrAdmin();\r
        HuntPrice = _HuntPrice;\r
        stageMaxSupply = _stageMaxSupply;\r
        HuntListActive = _HuntListActive;\r
    }\r
\r
    /**\r
    * @notice Pause/Unpause the Hunt.\r
    *\r
    * @param _HuntPaused set 'true' to pause Hunting.\r
    */\r
    function pauseHunt(bool _HuntPaused) external {\r
        _isOwnerOrAdmin();\r
        HuntPaused = _HuntPaused;\r
    }\r
\r
    /**\r
    * @notice Configure Dutch Auction parameters (Admin only).\r
    * @dev Sets auction pricing and timing details in wei and seconds.\r
    *\r
    * @param _startPrice Initial price at auction start (in wei).\r
    * @param _endPrice Minimum price at auction end (in wei).\r
    * @param _duration Total duration of the auction (in seconds).\r
    * @param _dropInterval Time interval between each price drop (in seconds).\r
    * @param _dropPerStep Amount by which the price decreases at each interval (in wei).\r
    */\r
    function setDutchAuction(\r
        uint256 _startPrice, \r
        uint256 _endPrice, \r
        uint256 _duration, \r
        uint256 _dropInterval, \r
        uint256 _dropPerStep\r
        ) external {\r
        _isOwnerOrAdmin();\r
        auctionStartPrice = _startPrice;\r
        auctionEndPrice = _endPrice;\r
        auctionDuration = _duration;\r
        auctionDropInterval = _dropInterval;\r
        auctionDropPerStep = _dropPerStep;\r
    }\r
\r
    /**\r
    * @notice Launches the Dutch Auction (Admin only).\r
    * @dev Unpauses the auction by setting the start time to the current block timestamp.\r
    */\r
    function startDutchAuction() external {\r
        _isOwnerOrAdmin();\r
        HuntPaused = false;\r
        auctionStartTime = block.timestamp;\r
    }\r
\r
    /**\r
    * @notice Stops and resets the Dutch Auction (Admin only).\r
    * @dev Pauses the auction by setting the start time to zero.\r
    *      Also pauses the Hunting function by setting `HuntPaused = true`.\r
    *      To temporarily pause Hunting without resetting the auction,\r
    *      use `setHunt()` instead.\r
    */\r
    function stopDutchAuction() external {\r
        _isOwnerOrAdmin();\r
        HuntPaused = true;\r
        auctionStartTime == 0;\r
    }\r
\r
    /**\r
    * @notice Returns the total number of NFTs Hunted by a given account.\r
    * @param account The address to query.\r
    * @return The number of tokens Hunted by the account.\r
    */\r
    function amountHunted(address account) external view returns (uint256) {\r
        return _numberMinted(account);\r
    }\r
\r
    /**\r
    * @notice Updates the maximum number of tokens that a single wallet can Hunt.\r
    * @dev Callable only by the contract owner or admin.\r
    * @param _limit The new wallet Hunting limit.\r
    */\r
    function setWalletHuntLimit(uint256 _limit) external {\r
        _isOwnerOrAdmin();\r
        walletHuntLimit = _limit;\r
    }\r
    \r
    /**\r
    * @notice Checks if a given account is on the HuntList.\r
    * @param account The address to check.\r
    * @return 'true' if the account is HuntListed, 'false' otherwise.\r
    */\r
    function isHuntListed(address account) external view returns (bool) {\r
        return HuntList.contains(account);\r
    }\r
\r
    /**\r
    * @notice Add or Remove accounts to/from the HuntList (Admin only).               \r
    * @param accounts The list of accounts to add or remove.\r
    * @param add : Set 'true' to add account(s) ('false" to remove).\r
    */\r
    function addToHuntList(address[] calldata accounts, bool add) external {\r
        _isOwnerOrAdmin();\r
\r
        for (uint256 i = 0; i < accounts.length; i++) {\r
            if(add) {\r
                HuntList.add(accounts[i]);\r
            } else {\r
                HuntList.remove(accounts[i]);\r
            }\r
        }\r
        emit HuntListUpdated(accounts, add);\r
    }\r
\r
    /**\r
    * @notice Returns the number of entries in the HuntList.\r
    * @dev This function returns the current length of the HuntList array.\r
    * @return uint256 The total count of HuntListed addresses.\r
    */\r
    function HuntListSize() external view returns (uint256) {\r
        return HuntList.length();\r
    }\r
\r
    /**\r
    * @notice Batch removes a specified number of accounts from the HuntList. (Admin only)\r
    * @dev Removes up to `batchSize` entries from the HuntList, iterating backwards to avoid\r
    *      index shifting issues during removal. Requires caller to be owner or admin.\r
    *      Emits `HuntListCleared` of the `batchSize` event upon completion.\r
    *\r
    * @param batchSize The number of HuntList accounts to remove in this batch.\r
    *        Must not exceed the current HuntList length.\r
    */\r
    function clearHuntList(uint256 batchSize) external {\r
        _isOwnerOrAdmin();\r
        uint256 length = HuntList.length();\r
        require(length > 0, "HuntList already empty");\r
        require(batchSize <= length, "Batchsize exceeds WL");\r
\r
        for (uint256 i = batchSize; i > 0; i--) {\r
            address account = HuntList.at(length - i);\r
            HuntList.remove(account);\r
        }\r
        emit HuntListCleared(batchSize);\r
    }\r
\r
    /**\r
    * @dev Overrides ERC721 `approve` to restrict approvals while `marketsPaused` is true \r
    *      (i.e., before Hunting is complete). Only the owner or admin can approve operators \r
    *      before _isHuntOut() is triggered to prevent secondary sales until the Hunt has fully ended.\r
    *\r
    * @param to The address to approve.\r
    * @param tokenId The token ID to approve.\r
    */\r
    function approve(address to, uint256 tokenId) public virtual override {\r
        address sender = _msgSenderERC721A();\r
\r
        if(marketsPaused) {\r
            // Revert if sender is not Owner nor Admin.\r
            require(sender == owner() || sender == Admin, "Paused until HuntOut");\r
        }\r
        super.approve(to, tokenId);\r
    }\r
\r
    /**\r
    * @dev Overrides ERC721 `setApprovalForAll` to restrict approvals while `marketsPaused` is true.\r
    *      (i.e., before Hunting is complete). Only the owner or admin can approve transfers \r
    *      before _isHuntOut() is triggered to prevent secondary sales until Hunting ends.\r
    *\r
    * @param operator The address to be approved or disapproved as an operator.\r
    * @param approved Boolean indicating approval status (true = approve, false = revoke).\r
    */\r
    function setApprovalForAll(address operator, bool approved) public virtual override {\r
        address sender = _msgSenderERC721A();\r
        \r
        if(marketsPaused) {\r
            // Revert if sender is not Owner nor Admin.\r
            require(sender == owner() || sender == Admin, "Paused until HuntOut");\r
        }\r
        super.setApprovalForAll(operator, approved);\r
    }\r
    \r
    /**\r
    * @dev Internal hook called before any token transfer, including Hunting.\r
    *      Enforces royalty or transfer restrictions via an external validator contract, if set.\r
    *\r
    * Note Derived from Limit Break's ERC721-C implementation.\r
    *      Allows configurable transfer validation logics for on-chain royalty enforcement,\r
    *      operator filtering, and marketplace restrictions.\r
    *       \r
    * Requirements:\r
    * - If both `from` and `to` are non-zero (i.e., not Hunt or burn), the transfer validator (if configured)\r
    *   is called to validate the transfer.\r
    */\r
    function _beforeTokenTransfers(\r
        address from,\r
        address to,\r
        uint256 startTokenId,\r
        uint256 quantity \r
    ) internal override {\r
        if (from != address(0) && to != address(0)) {\r
            // Call the transfer validator if one is set.\r
            address transferValidator = _transferValidator;\r
            if (transferValidator != address(0)) {\r
                ITransferValidator721(transferValidator).validateTransfer(\r
                    msg.sender,\r
                    from,\r
                    to,\r
                    startTokenId\r
                );\r
            }\r
        }\r
        super._beforeTokenTransfers(from, to, startTokenId, quantity);\r
    }\r
\r
    /**\r
    * @dev Hook called after any token transfer, including Hunting.\r
    *      Resets the token holding start time on transfer to track new ownership duration.\r
    *\r
    * Effects:\r
    * - For each token transferred, updates `tokenHoldingStart` to the current timestamp.\r
    */\r
    function _afterTokenTransfers(address from, address to, uint256 startTokenId, \r
         uint256 quantity) internal override {\r
\r
        //Reset token holdingTime to zero when ownership changes.\r
        for (uint256 i = 0; i < quantity; i++) {\r
            tokenHoldingStart[startTokenId + i] = uint96(block.timestamp);\r
        }\r
        super._afterTokenTransfers(from, to, startTokenId, quantity);\r
    }\r
\r
    /**\r
    * @notice Returns the current holding duration (in seconds) for a specific token.\r
    * @dev Reverts if the token has not been Hunted or has no recorded holding start time.\r
    *\r
    * @param tokenId The ID of the token to query.\r
    * @return The number of seconds the current owner has held the token.\r
    */\r
    function getTokenHoldingTime(uint256 tokenId) external view returns (uint256) {\r
        if (tokenHoldingStart[tokenId] == 0) {\r
            revert HoldingTimeQueryForNonExistantToken();\r
        }\r
        return block.timestamp - tokenHoldingStart[tokenId];\r
    }\r
\r
    /**\r
    * @notice Updates the base URI for token metadata (Admin only).\r
    * @dev Reverts if metadata is frozen. Emits `BatchMetadataUpdate` event \r
    *      covering all Hunted tokens when total supply is non-zero.\r
    *\r
    * @param newBaseURI The new base URI to set for all tokens.\r
    */\r
    function setBaseURI(string calldata newBaseURI) external {\r
        _isOwnerOrAdmin();\r
\r
        if(isMetadataFrozen) {revert FrozenMetadata();} \r
\r
        // Set the new base URI.\r
        baseURI = newBaseURI;\r
\r
        // Emit an event with the update.\r
        if (totalSupply() != 0) {\r
            emit BatchMetadataUpdate(1, _nextTokenId());\r
        }\r
    }\r
\r
    /**\r
    * @notice Sets the contract-level metadata URI (Admin only).\r
    * @dev Reverts if metadata is frozen.\r
    *\r
    * @param newContractURI The new URI pointing to the contract metadata.\r
    */\r
    function setContractURI(string calldata newContractURI) external {\r
        _isOwnerOrAdmin();\r
\r
        if(isMetadataFrozen) {revert FrozenMetadata();} \r
\r
        // Set the new contract URI.\r
        contractURI = newContractURI;\r
\r
        // Emit an event with the update.\r
        emit ContractURIUpdated(newContractURI);\r
    }\r
\r
    /**\r
    * @notice Permanently freezes metadata, preventing further updates (Admin only).\r
    * @dev This action is irreversible. Emits a confirmation event upon freezing.\r
    */\r
    function freezeMetadata() external {\r
        _isOwnerOrAdmin();\r
        isMetadataFrozen = true;\r
        emit MetadataFrozen();\r
    }\r
\r
    /**\r
    * @notice Updates the royalty recipient address and royalty fee basis points (Admin only).\r
    * @dev Reverts if the royalty address is zero or if the basis points exceed 10,000 (100%).\r
    *\r
    * @param RoyaltyAddress The address to receive royalty payments.\r
    * @param RoyaltyFeesInBips The royalty fee in basis points (1 basis point = 0.01%).\r
    */\r
    function setRoyaltyInfo(address RoyaltyAddress, uint96 RoyaltyFeesInBips) external {\r
        _isOwnerOrAdmin();\r
        // Revert if the provided royalty address is the zero address.\r
        if (RoyaltyAddress == address(0)) {\r
            revert RoyaltyAddressCannotBeZeroAddress();\r
        }\r
\r
        // Revert if the royalty fee exceeds exceeds 100% (10,000 basis points).\r
        if (RoyaltyFeesInBips > 10_000) {\r
            revert InvalidRoyaltyBasisPoints(RoyaltyFeesInBips);\r
        }\r
\r
        // Set the new royalty info.\r
        _setDefaultRoyalty(RoyaltyAddress, RoyaltyFeesInBips);\r
\r
        // Emit an event with the updated params.\r
        emit RoyaltyInfoUpdated(RoyaltyAddress, RoyaltyFeesInBips);\r
    }\r
\r
    /**\r
    * @notice Returns the selector of the transfer validation function used by the contract.\r
    * @dev Indicates whether the function is a view or modifies state.\r
    *\r
    * @return functionSignature The function selector of `validateTransfer`.\r
    * @return isViewFunction Boolean indicating if the function is a view function (false in this case).\r
    */\r
    function getTransferValidationFunction()\r
        external\r
        pure\r
        returns (bytes4 functionSignature, bool isViewFunction)\r
    {\r
        functionSignature = ITransferValidator721.validateTransfer.selector;\r
        isViewFunction = false;\r
    }\r
\r
    /**\r
    * @notice Sets the address of the transfer validator contract (Admin only).\r
    * @dev The transfer validator enforces custom rules on token transfers.\r
    *\r
    * @param newValidator The address of the new transfer validator contract.\r
    */\r
    function setTransferValidator(address newValidator) external {\r
        _isOwnerOrAdmin();\r
        // Set the new transfer validator.\r
        _setTransferValidator(newValidator);\r
    }\r
\r
    /**\r
    * @notice Helper function for marketplaces to check if a transfer is allowed by the validator.\r
    * @dev Calls the external transfer validator’s `validateTransfer` function via staticcall.\r
    *      Returns a boolean indicating if transfer is allowed, and a string with the reason if disallowed.\r
    *      If no validator is set, returns allowed by default.\r
    *\r
    * @param from The current owner of the token.\r
    * @param to The recipient address of the token transfer.\r
    * @param tokenId The ID of the token to be transferred.\r
    * @return _allowed (bool) : Whether the transfer is permitted.\r
    * @return _reason : Human-readable reason why the transfer is disallowed or an empty string if allowed.\r
    */\r
    function isTransferable(address from, address to, uint256 tokenId)\r
        external\r
        view\r
        returns (bool _allowed, string memory _reason)\r
    {\r
        address transferValidator = _transferValidator;\r
\r
        if (transferValidator == address(0)) {\r
            return (true, "");\r
        }\r
\r
        (bool success, bytes memory result) = transferValidator.staticcall(\r
            abi.encodeWithSignature(\r
                "validateTransfer(address,address,uint256)",\r
                from,\r
                to,\r
                tokenId\r
            )\r
        );\r
\r
        if (success) {\r
            // Validator returned normally: decode as (bool, string)\r
            (bool allowed, string memory reason) = abi.decode(result, (bool, string));\r
            return (allowed, reason);\r
        }\r
\r
        if (result.length > 68) {\r
            // if this is a standard Error(string) revert: decode string\r
            string memory reason = abi.decode(result, (string));\r
            return (false, reason);\r
        }\r
\r
        // If revert reason is short or unknown, return a default string\r
        return (false, "Reverted without reason");\r
    }\r
\r
    /**\r
    * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\r
    * Can only be called by the current owner.\r
    *\r
    * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.\r
    */\r
    function transferOwnership(address newOwner) public virtual override onlyOwner {\r
        pendingOwner = newOwner;\r
        emit OwnershipTransferStarted(owner(), newOwner);\r
    }\r
\r
    /**\r
    * @notice Allows the contract owner to set the price required for an ownership transfer.\r
    * @param _ownershipPrice The new price (in wei) for transferring ownership.\r
    */\r
    function setOwnershipPrice(uint256 _ownershipPrice) public virtual payable onlyOwner {\r
        ownershipPrice = _ownershipPrice;\r
    }\r
\r
    /**\r
    * @notice Allows the pending owner to accept ownership transfer by paying the required price.\r
    * @dev Reverts if the caller is not the pending owner or if the sent ETH amount does not match `ownershipPrice`.\r
    *      Uses `nonReentrant` modifier to prevent reentrancy attacks.\r
    *      Transfers ownership upon successful payment and verification.\r
    */\r
    function acceptOwnership() nonReentrant public virtual payable {\r
        require(msg.value == ownershipPrice, "Incorrect Price");\r
        address sender = _msgSender();\r
        if (pendingOwner != sender) {\r
            revert OwnableUnauthorizedAccount(sender);\r
        }\r
        _transferOwnership(sender);\r
    }\r
\r
    /**\r
    * @dev Transfers contract ownership to `newOwner` and clears any pending ownership transfer.\r
    *      This is an internal function without access restrictions and overrides the parent implementation.\r
    *\r
    * @param newOwner The address to transfer ownership to.\r
    */\r
    function _transferOwnership(address newOwner) internal virtual override {\r
        delete pendingOwner;\r
        super._transferOwnership(newOwner);\r
    }\r
\r
    /**\r
    * @dev Internal helper function to verify if the caller is either the contract owner or the designated admin.\r
    *      Reverts with `NotOwnerNorAdmin` error if unauthorized.\r
    */   \r
    function _isOwnerOrAdmin() internal view {\r
        if(msg.sender != owner() && msg.sender != Admin) {\r
            revert NotOwnerNorAdmin();\r
        }\r
    }\r
\r
    /**\r
    * @notice Grants the Admin role to a specified account. Use zero address to revoke.\r
    * @dev Can only be called by the owner or current admin.\r
    *\r
    * @param newAdmin The address to assign as Admin. Use `address(0)` to revoke the role.\r
    */\r
    function grantAdminRole(address newAdmin) external {\r
        _isOwnerOrAdmin();\r
        Admin = newAdmin; // Grant the role to an address\r
        emit AdminRoleGranted(newAdmin);\r
    }\r
\r
    /**\r
    * @notice On-chain gamification: Allows an account to claim the "MasterHunter" position.\r
    *      Caller must own at least 15 WANTEDs and hold more than the current MasterHunter.\r
    *      Reverts if conditions are not met.\r
    * @dev Updates the MasterHunter to the caller and emits an event.\r
    */       \r
    function claimMasterHunter() external {\r
\r
        // Claimer must hold 15+ WANTEDs.\r
        if(balanceOf(msg.sender) < 15) {\r
            revert MasterHunterNotClaimable();\r
        } \r
        // Claimer must hold more WANTEDs than current MasterHunter.\r
        if(MasterHunter != address(0) && balanceOf(msg.sender) <= balanceOf(MasterHunter)) {\r
            revert MasterHunterNotClaimable();\r
        }\r
\r
        // Set claimer as the new MasterHunter.\r
        MasterHunter = msg.sender;\r
\r
        // Emit an event with the updated account.\r
        emit NewMasterHunter(msg.sender);\r
    }\r
\r
    /**\r
    * @notice Withdraw all Ether from the contract. Only owner or Admin.\r
    */\r
    function withdraw() external {\r
        _isOwnerOrAdmin();\r
\r
        require(Admin != address(0), "Admin not set");\r
        uint256 balance = address(this).balance;\r
        require(balance > 0, "0 Balance");\r
\r
        payable(owner()).transfer(balance * 25/100);\r
        payable(Admin).transfer(balance * 75/100);\r
\r
        emit Withdrawal(msg.sender, balance);\r
    }\r
}\r
"
    },
    "@openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

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

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 * - Set can be cleared (all elements removed) in O(n).
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

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

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

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

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

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

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

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

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function _clear(Set storage set) private {
        uint256 len = _length(set);
        for (uint256 i = 0; i < len; ++i) {
            delete set._positions[set._values[i]];
        }
        Arrays.unsafeSetLength(set._values, 0);
    }

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(Bytes32Set storage set) internal {
        _clear(set._inner);
    }

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(AddressSet storage set) internal {
        _clear(set._inner);
    }

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

    /**
     * @dev Removes all the values from a set. O(n).
     *
     * WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
     * function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
     */
    function clear(UintSet storage set) internal {
        _clear(set._inner);
    }

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

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

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

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}
"
    },
    "WANTED/lib/WANTEDErrorsAndEvents.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.29;\r
\r
interface WANTEDErrorsAndEvents {\r
\r
    /// @dev Revert if the royalty basis points is greater than 10_000.\r
    error InvalidRoyaltyBasisPoints(uint256 basisPoints);\r
\r
    /// @dev Revert if the royalty address is being set to the zero address.\r
    error RoyaltyAddressCannotBeZeroAddress();\r
\r
    /// @dev Revert if Metadata is fully frozen.\r
    error FrozenMetadata();\r
\r
    /// @dev Revert if set Supply > MaxSupply.\r
    error ExceedMaxSupply();\r
\r
    /// @dev Revert if Hunt quantity is zero or Hunt is Paused.\r
    error ZeroQuantityOrPaused();\r
\r
    /// @dev Revert if Public Hunt not Opened.\r
    error PublicHuntPaused();\r
\r
    /// @dev Revert if account is not on the HuntList.\r
    error NotHuntListed();\r
\r
    /// @dev Revert if quantity exceeds wallet limit.\r
    error WalletHuntLimit();\r
\r
    /// @dev Revert if Public Hunt Supply has been capped for a Stage and Hunt quantity Exceeds it.\r
    error QuantityExceedsStageMaxSupply();\r
\r
    /// @dev Revert if Hunt quantity will overflow MaxSupply.\r
    error QuantityExceedMaxSupply();\r
\r
    /// @dev Revert if paid price (sent price) too low.\r
    error InvalidPaidPrice();\r
\r
    /// @dev Revert if Hunt quantity is zero.\r
    error ZeroQuantity();\r
\r
    /// @dev Revert if holding time is queried but token doesn't exist.\r
    error HoldingTimeQueryForNonExistantToken();\r
\r
    /// @dev Revert if Caller is neither Owner nor Admin.\r
    error NotOwnerNorAdmin();\r
\r
    /// @dev Revert if Dutch Auction paused.\r
    error DutchAuctionNotStarted();\r
\r
    /// @dev Revert if Dutch Auction paused.\r
    error DutchAuctionEnded();\r
\r
    /// @dev Revert if not meeting requirements to become the MasterHunter.\r
    error MasterHunterNotClaimable(); \r
\r
    /// @dev Revert if ether withdrawl failed.\r
    error WithdrawalFailed(); \r
\r
    // @dev Emit an event when accounts are added (true) to or removed (false) from the HuntList.\r
    event HuntListUpdated(address[] accounts, bool addedOrRemoved);\r
\r
    // @dev Emit an event when the HuntList has been cleared of amount entries.\r
    event HuntListCleared(uint256 amount);\r
\r
    /// @dev Emit a Hunted event for HuntFor() and Hunt()\r
    event Hunted(address Hunter, uint256 HuntedAmount);\r
\r
    // @dev Emit an event for token transfer with holding time update.\r
    event TokenHeldTime(address from, uint256 tokenId, uint256 heldTime);\r
\r
    // @dev Emit an event for HuntOut.\r
    event HuntOutAchieved(uint256 HuntOutTime);\r
\r
    /**\r
     * @dev Emit an event for token metadata reveals/updates,\r
     *      according to EIP-4906.\r
     *\r
     * @param _fromTokenId The start token id.\r
     * @param _toTokenId   The end token id.\r
     */\r
    event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);\r
\r
    /**\r
     * @dev Emit an event when the URI for the collection-level metadata\r
     *      is updated.\r
     */\r
    event TokenURIUpdated(string newTokentURI);\r
\r
    /**\r
     * @dev Emit an event when the URI for the collection-level metadata\r
     *      is updated.\r
     */\r
    event ContractURIUpdated(string newContractURI);\r
\r
    /**\r
     * @dev Emit an event when Metadata is Frozen.\r
     */\r
    event MetadataFrozen();  \r
\r
    /**\r
     * @dev Emit an event when the royalties info is updated.\r
     */\r
    event RoyaltyInfoUpdated(address receiver, uint256 bps);\r
\r
    /**\r
     * @dev Emit an event when Admin Role is granted.\r
     */\r
    event AdminRoleGranted(address account);\r
\r
    /**\r
     * @dev Emit an event when an Ownership transfer is initialized.\r
     */\r
    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\r
\r
    /**\r
     * @dev Emit an event when MasterHunter position is claimed.\r
     */\r
    event NewMasterHunter(address account);\r
    \r
    /**\r
     * @dev Event to log Withdrawal.\r
     */\r
    event Withdrawal(address account, uint256 amount);\r
}"
    },
    "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
    },
    "lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981 is IERC165 {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}
"
    },
    "@openzeppelin/contracts/token/common/ERC2981.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/common/ERC2981.sol)

pragma solidity ^0.8.20;

import {IERC2981} from "../../interfaces/IERC2981.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
 *
 * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
 * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
 *
 * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
 * fee is specified in basis points by default.
 *
 * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
 * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the ERC. Marketplaces are expected to
 * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
 */
abstract contract ERC2981 is IERC2981, ERC165 {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }

    RoyaltyInfo internal _defaultRoyaltyInfo;
    mapping(uint256 tokenId => RoyaltyInfo) internal _tokenRoyaltyInfo;

    /**
     * @dev The default royalty set is invalid (eg. (numerator / denominator) >= 1).
     */
    error ERC2981InvalidDefaultRoyalty(uint256 numerator, uint256 denominator);

    /**
     * @dev The default royalty receiver is invalid.
     */
    error ERC2981InvalidDefaultRoyaltyReceiver(address receiver);

    /**
     * @dev The royalty set for a specific `tokenId` is invalid (eg. (numerator / denominator) >= 1).
     */
    error ERC2981InvalidTokenRoyalty(uint256 tokenId, uint256 numerator, uint256 denominator);

    /**
     * @dev The royalty receiver for `tokenId` is invalid.
     */
    error ERC2981InvalidTokenRoyaltyReceiver(uint256 tokenId, address receiver);

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
        return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
    }

    /// @inheritdoc IERC2981
    function royaltyInfo(
        uint256 tokenId,
        uint256 salePrice
    ) public view virtual returns (address receiver, uint256 amount) {
        RoyaltyInfo storage _royaltyInfo = _tokenRoyaltyInfo[tokenId];
        address royaltyReceiver = _royaltyInfo.receiver;
        uint96 royaltyFraction = _royaltyInfo.royaltyFraction;

        if (royaltyReceiver == address(0)) {
            royaltyReceiver = _defaultRoyaltyInfo.receiver;
            royaltyFraction = _defaultRoyaltyInfo.royaltyFraction;
        }

        uint256 royaltyAmount = (salePrice * royaltyFraction) / _feeDenominator();

        return (royaltyReceiver, royaltyAmount);
    }

        /**
     * @notice Returns the address that receives royalties.
     */
    function royaltyAddress() external view returns (address) {
        return _defaultRoyaltyInfo.receiver;
    }

    /**
     * @notice Returns the royalty basis points out of 10_000.
     */
    function royaltyBasisPoints() external view returns (uint256) {
        return _defaultRoyaltyInfo.royaltyFraction;
    }

    /**
     * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
     * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
     * override.
     */
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }

    /**
     * @dev Sets the royalty information that all ids in this contract will default to.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        uint256 denominator = _feeDenominator();
        if (feeNumerator > denominator) {
            // Royalty fee will exceed the sale price
            revert ERC2981InvalidDefaultRoyalty(feeNumerator, denominator);
        }
        if (receiver == address(0)) {
            revert ERC2981InvalidDefaultRoyaltyReceiver(address(0));
        }

        _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Removes default royalty information.
     */
    function _deleteDefaultRoyalty() internal virtual {
        delete _defaultRoyaltyInfo;
    }

    /**
     * @dev Sets the royalty information for a specific token id, overriding the global default.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
        uint256 denominator = _feeDenominator();
        if (feeNumerator > denominator) {
            // Royalty fee will exceed the sale price
            revert ERC2981InvalidTokenRoyalty(tokenId, feeNumerator, denominator);
        }
        if (receiver == address(0)) {
            revert ERC2981InvalidTokenRoyaltyReceiver(tokenId, address(0));
        }

        _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Resets royalty information for the token id back to the global default.
     */
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        delete _tokenRoyaltyInfo[tokenId];
    }
}
"
    },
    "src/interfaces/ITransferValidator.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.17;\r
\r
interface ITransferValidator721 {\r
    /// @notice Ensure that a transfer has been authorized for a specific tokenId\r
    function validateTransfer(\r
        address caller,\r
        address from,\r
        address to,\r
        uint256 tokenId\r
    ) external view;\r
}\r
\r
interface ITransferValidator1155 {\r
    /// @notice Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and reduce the transferable amount remaining\r
    function validateTransfer(\r
        address caller,\r
        address from,\r
        address to,\r
        uint256 tokenId,\r
        uint256 amount\r
    ) external;\r
}\r
"
    },
    "src/interfaces/ICreatorToken.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.4;\r
\r
interface ICreatorToken {\r
    event TransferValidatorUpdated(address oldValidator, address newValidator);\r
\r
    function getTransferValidator() external view returns (address validator);\r
\r
    function getTransferValidationFunction()\r
        external\r
        view\r
        returns (bytes4 functionSignature, bool isViewFunction);\r
\r
    function setTransferValidator(address validator) external;\r
}\r
\r
interface ILegacyCreatorToken {\r
    event TransferValidatorUpdated(address oldValidator, address newValidator);\r
\r
    function getTransferValidator() external view returns (address validator);\r
\r
    function setTransferValidator(address validator) external;\r
}\r
"
    },
    "src/lib/ERC721TransferValidator.sol": {
      "content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.17;\r
\r
import { ICreatorToken } from "../interfaces/ICreatorToken.sol";\r
\r
/**\r
 * @title  ERC721TransferValidator\r
 * @notice Functionality to use a transfer validator.\r
 */\r
abstract contract ERC721TransferValidator is ICreatorToken {\r
    /// @dev Store the transfer validator. The null address means no transfer validator is set.\r
    address internal _transferValidator;\r
\r
    /// @notice Revert with an error if the transfer validator is being set to the same address.\r
    error SameTransferValidator();\r
\r
    /// @notice Returns the currently active transfer validator.\r
    ///         The null address means no transfer validator is set.\r
    function getTransferValidator() external view returns (address) {\r
        return _transferValidator;\r
    }\r
\r
    /// @notice Set the transfer validator.\r
    ///         The external method that uses this must include access control.\r
    function _setTransferValidator(address newValidator) internal {\r
        address oldValidator = _transferValidator;\r
        if (oldValidator == newValidator) {\r
            revert SameTransferValidator();\r
        }\r
        _transferValidator = newValidator;\r
        emit TransferValidatorUpdated(oldValidator, newValidator);\r
    }\r
}\r
"
    },
    "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonRee

Tags:
ERC721, ERC165, Multisig, Non-Fungible, Upgradeable, Multi-Signature, Factory|addr:0x8635e150c31c7f5dbce16f40d400d495d2eec9b6|verified:true|block:23621118|tx:0x7a6f59472beaad7beaff7d2d3886b851e156d8a6131e74122fabeb68935b8b9b|first_check:1761035425

Submitted on: 2025-10-21 10:30:27

Comments

Log in to comment.

No comments yet.