EthereumEthereum
0x21...Ca8d
Teiko Key

Teiko Key

TEIKOKEY

收藏品
底价
0.0001 ETH
$2,345.34
大小
2,008
收藏品
所有者
754
38% 独特的所有者
此合同的源代码已经过验证!
合同元数据
编译器
0.8.17+commit.8df45f5f
语言
Solidity
合同源代码
文件 1 的 10:Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;
合同源代码
文件 2 的 10:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
合同源代码
文件 3 的 10:Counters.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}
合同源代码
文件 4 的 10:ERC721A.sol
合同源代码
文件 5 的 10:IERC721A.sol
合同源代码
文件 6 的 10:IOperatorFilterRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IOperatorFilterRegistry {
    /**
     * @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
     *         true if supplied registrant address is not registered.
     */
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);

    /**
     * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
     */
    function register(address registrant) external;

    /**
     * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
     */
    function registerAndSubscribe(address registrant, address subscription) external;

    /**
     * @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
     *         address without subscribing.
     */
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;

    /**
     * @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
     *         Note that this does not remove any filtered addresses or codeHashes.
     *         Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
     */
    function unregister(address addr) external;

    /**
     * @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
     */
    function updateOperator(address registrant, address operator, bool filtered) external;

    /**
     * @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
     */
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;

    /**
     * @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
     */
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;

    /**
     * @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
     */
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;

    /**
     * @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
     *         subscription if present.
     *         Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
     *         subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
     *         used.
     */
    function subscribe(address registrant, address registrantToSubscribe) external;

    /**
     * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
     */
    function unsubscribe(address registrant, bool copyExistingEntries) external;

    /**
     * @notice Get the subscription address of a given registrant, if any.
     */
    function subscriptionOf(address addr) external returns (address registrant);

    /**
     * @notice Get the set of addresses subscribed to a given registrant.
     *         Note that order is not guaranteed as updates are made.
     */
    function subscribers(address registrant) external returns (address[] memory);

    /**
     * @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
     *         Note that order is not guaranteed as updates are made.
     */
    function subscriberAt(address registrant, uint256 index) external returns (address);

    /**
     * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
     */
    function copyEntriesOf(address registrant, address registrantToCopy) external;

    /**
     * @notice Returns true if operator is filtered by a given address or its subscription.
     */
    function isOperatorFiltered(address registrant, address operator) external returns (bool);

    /**
     * @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
     */
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);

    /**
     * @notice Returns true if a codeHash is filtered by a given address or its subscription.
     */
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);

    /**
     * @notice Returns a list of filtered operators for a given address or its subscription.
     */
    function filteredOperators(address addr) external returns (address[] memory);

    /**
     * @notice Returns the set of filtered codeHashes for a given address or its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);

    /**
     * @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
     *         its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);

    /**
     * @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
     *         its subscription.
     *         Note that order is not guaranteed as updates are made.
     */
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);

    /**
     * @notice Returns true if an address has registered
     */
    function isRegistered(address addr) external returns (bool);

    /**
     * @dev Convenience method to compute the code hash of an arbitrary contract
     */
    function codeHashOf(address addr) external returns (bytes32);
}
合同源代码
文件 7 的 10:OperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "./lib/Constants.sol";
/**
 * @title  OperatorFilterer
 * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
 *         registrant's entries in the OperatorFilterRegistry.
 * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
 *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
 *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
 *         Please note that if your token contract does not provide an owner with EIP-173, it must provide
 *         administration methods on the contract itself to interact with the registry otherwise the subscription
 *         will be locked to the options set during construction.
 */

abstract contract OperatorFilterer {
    /// @dev Emitted when an operator is not allowed.
    error OperatorNotAllowed(address operator);

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);

    /// @dev The constructor that is called when the contract is being deployed.
    constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
        // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
        // will not revert, but the contract will need to be registered with the registry once it is deployed in
        // order for the modifier to filter addresses.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (subscribe) {
                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
            } else {
                if (subscriptionOrRegistrantToCopy != address(0)) {
                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                } else {
                    OPERATOR_FILTER_REGISTRY.register(address(this));
                }
            }
        }
    }

    /**
     * @dev A helper function to check if an operator is allowed.
     */
    modifier onlyAllowedOperator(address from) virtual {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
        // from an EOA.
        if (from != msg.sender) {
            _checkFilterOperator(msg.sender);
        }
        _;
    }

    /**
     * @dev A helper function to check if an operator approval is allowed.
     */
    modifier onlyAllowedOperatorApproval(address operator) virtual {
        _checkFilterOperator(operator);
        _;
    }

    /**
     * @dev A helper function to check if an operator is allowed.
     */
    function _checkFilterOperator(address operator) internal view virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            // under normal circumstances, this function will revert rather than return false, but inheriting contracts
            // may specify their own OperatorFilterRegistry implementations, which may behave differently
            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}
合同源代码
文件 8 的 10:Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

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

pragma solidity ^0.8.0;

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

// @title Teiko Key for Teiko World
// @author TheV

//                                                                                                                       _________________________ 
// [... [......[........[..[..   [..      [....          [..        [..    [....     [.......    [..      [.....        ///      __       __      \
//      [..    [..      [..[..  [..     [..    [..       [..        [..  [..    [..  [..    [..  [..      [..   [..    |||      |  |     |  |      |
//      [..    [..      [..[.. [..    [..        [..     [..   [.   [..[..        [..[..    [..  [..      [..    [..   |||      |__|  _  |__|      |
//      [..    [......  [..[. [.      [..        [..     [..  [..   [..[..        [..[. [..      [..      [..    [..    \\\______    /_\     _____/
//      [..    [..      [..[..  [..   [..        [..     [.. [. [.. [..[..        [..[..  [..    [..      [..    [..           \\\__________/
//      [..    [..      [..[..   [..    [..     [..      [. [.    [....  [..     [.. [..    [..  [..      [..   [..            |||         |
//      [..    [........[..[..     [..    [....          [..        [..    [....     [..      [..[........[.....                \\\_______/

pragma solidity >=0.7.0;

import "https://github.com/chiru-labs/ERC721A/contracts/ERC721A.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "operator_filter/OperatorFilterer.sol";
import {CANONICAL_CORI_SUBSCRIPTION} from "operator_filter/lib/Constants.sol";


contract TeikoKey is ERC721A, Ownable, OperatorFilterer
{
    //////////////////////////////////////////////////////////////////
    // Library
    //////////////////////////////////////////////////////////////////
    
    using Counters for Counters.Counter;

    //////////////////////////////////////////////////////////////////
    // Enums
    //////////////////////////////////////////////////////////////////

    enum MintPhase
    {
        CLOSED,
        WHITELIST,
        ALLOWLIST,
        PUBLIC
    }

    enum KeyType
    {
        REGULAR,
        ROBOT,
        ZOMBIE,
        APE,
        SPIRIT,
        GOLD
    }

    //////////////////////////////////////////////////////////////////
    // Attributes
    //////////////////////////////////////////////////////////////////

    Counters.Counter private _tokenIds;
    MintPhase public currentMintPhase = MintPhase.CLOSED;
    uint256 public constant MAX_SUPPLY = 2008;
    bytes32 constant public whitelistRole = keccak256("whitelisted");
    bytes32 constant public allowlistRole = keccak256("allowlisted");
    uint8 public maxMintWhitelist = 2;
    uint8 public maxMintAllowlist = 1;
    uint8 public maxMintPublic = 1;
    address public mainAddress = 0x9Cc8C097251d71f68f674c0f4d2c86fB170e7BCD;
    address public signerAddress = 0x8fD261f08991619c2c0ad36B3a73E8f874BB5372;
    string public baseURI = "https://ipfs.io/ipns/k51qzi5uqu5djzdmli4l9utu46cvd35sn9plo3u50pkam7gtqw3ifq0crypp0p";
    uint256 public price = 0 ether;
    mapping (address => uint256) public mints;
    mapping (uint256 => uint8) public keyPercents;
    mapping(address => int8) public approvedCallers;
    mapping(uint256 => string) public tokenURIs;
    mapping(uint256 => KeyType) public keyTypes;
    mapping(KeyType => uint16) public maxCountKeyTypes;
    mapping(KeyType => uint8) public currentCountKeyTypes;

    //////////////////////////////////////////////////////////////////
    // Constructor
    //////////////////////////////////////////////////////////////////

    constructor() ERC721A("Teiko Key", "TEIKOKEY") OperatorFilterer(CANONICAL_CORI_SUBSCRIPTION, false)
    {
        maxCountKeyTypes[KeyType.REGULAR] = 1083;
        maxCountKeyTypes[KeyType.ROBOT] = 450;
        maxCountKeyTypes[KeyType.ZOMBIE] = 300;
        maxCountKeyTypes[KeyType.APE] = 120;
        maxCountKeyTypes[KeyType.SPIRIT] = 50;
        maxCountKeyTypes[KeyType.GOLD] = 5;
    }

    //////////////////////////////////////////////////////////////////
    // Modifiers
    //////////////////////////////////////////////////////////////////

    modifier onlyApprovedOrOwner(address addr)
    {
        require(approvedCallers[addr] == 1 || addr == owner(), "Caller is not approved nor owner");
        _;
    }

    modifier onlyOwnerOfOrApproved(address addr, uint256 tokenId)
    {
        require(ownerOf(tokenId) == addr || approvedCallers[addr] == 1, "Caller is not owner of that token id nor approved");
        _;
    }

    //////////////////////////////////////////////////////////////////
    // OperatorFilter functions
    //////////////////////////////////////////////////////////////////

    /**
     * @notice Registers self with the operator filter registry
    */
    function register()
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.register(address(this));
    }

    /**
     * @notice Unregisters self from the operator filter registry
    */
    function unregister()
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.unregister(address(this));
    }

    /**
     * @notice Registers self with the operator filter registry, and susbscribe to 
        the filtered operators of the given subscription 
    */
    function registerAndSubscribe(address subscription)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscription);
    }

    /**
     * @notice Registers self with the operator filter registry, and copy
        the filtered operators of the given subscription 
    */
    function registerAndCopyEntries(address registrantToCopy)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), registrantToCopy);
    }

    /**
     * @notice Update given operator address to filtered/unfiltered state
    */
    function updateOperator(address operator, bool filtered)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.updateOperator(address(this), operator, filtered);
    }

    /**
     * @notice Update given operator smart contract code hash to filtered/unfiltered state
    */
    function updateCodeHash(bytes32 codeHash, bool filtered)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.updateCodeHash(address(this), codeHash, filtered);
    }

    /**
     * @notice Batch function for updateOperator
    */
    function updateOperators(address[] calldata operators, bool filtered)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.updateOperators(address(this), operators, filtered);
    }

    /**
     * @notice Batch function for updateCodeHash
    */
    function updateCodeHashes(bytes32[] calldata codeHashes, bool filtered)
        external onlyOwner
    {
        OPERATOR_FILTER_REGISTRY.updateCodeHashes(address(this), codeHashes, filtered);
    }

    /**
     * @notice Check if a given operator address is currently filtered
    */
    function isOperatorAllowed(address operator)
        external view
        returns (bool)
    {
        return OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator);
    }

    /**
     * @notice Subscribe to OperatorFilterRegistry contract : activate modifiers
    */
    function subscribe(address subscription)
        external onlyOwner
    {
        return OPERATOR_FILTER_REGISTRY.subscribe(address(this), subscription);
    }

    /**
     * @notice Unsubscribe to OperatorFilterRegistry contract : deactivate modifiers
    */
    function unsubscribe(bool copyExistingEntries)
        external onlyOwner
    {
        return OPERATOR_FILTER_REGISTRY.unsubscribe(address(this), copyExistingEntries);
    }

    /**
     * @notice Copy filtered operators of a given OperatorFilterRegistry
        registered smart contract
    */
    function copyEntriesOf(address registrantToCopy)
        external onlyOwner
    {
        return OPERATOR_FILTER_REGISTRY.copyEntriesOf(address(this), registrantToCopy);
    }

    /**
     * @notice Returns the list of filtered operators
    */
    function filteredOperators()
        external
        returns (address[] memory)
    {
        return OPERATOR_FILTER_REGISTRY.filteredOperators(address(this));
    }

    /**
    * @notice Overriding ERC721A.supportsInterface as advised for OperatorFilterRegistry 
        smart contract
    */
    function supportsInterface(bytes4 interfaceId)
        public view virtual 
        override
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    /**
    * @notice Overriding ERC721A.approve to integrate OperatorFilter modifier 
        onlyAllowedOperatorApproval
    */
    function approve(address operator, uint256 tokenId)
        public payable
        override
        onlyAllowedOperatorApproval(operator)
    {
        super.approve(operator, tokenId);
    }
    
    /**
    * @notice Overriding ERC721A.setApprovalForAll to integrate OperatorFilter modifier 
        onlyAllowedOperatorApproval
    */
    function setApprovalForAll(address operator, bool approved)
        public
        override
        onlyAllowedOperatorApproval(operator)
    {
        super.setApprovalForAll(operator, approved);
    }

    /**
    * @notice Overriding ERC721A.safeTransferFrom to integrate
        OperatorFilter modifier onlyAllowedOperator
    */
    function safeTransferFrom(address from, address to, uint256 tokenId)
        public payable
        override
        onlyAllowedOperator(from)
    {
        super.safeTransferFrom(from, to, tokenId);
    }

    /**
    * @notice Overriding ERC721A.safeTransferFrom to integrate
        OperatorFilter modifier onlyAllowedOperator
    */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
        public payable
        override
        onlyAllowedOperator(from)
    {
        super.safeTransferFrom(from, to, tokenId, data);
    }

    /**
    * @notice Overriding ERC721A.transferFrom to integrate
        OperatorFilter modifier onlyAllowedOperator
    */
    function transferFrom(address from, address to, uint256 tokenId)
        public payable
        override
        onlyAllowedOperator(from)
    {
        super.transferFrom(from, to, tokenId);
    }

    // Mint Functions
    ////////////////////

    /**
    * @notice Internal mint logic
    */
    function _mintNFT(uint256 nMint, address recipient)
        private
    {
        require(_tokenIds.current() + nMint <= MAX_SUPPLY, "No more NFT to mint");

        mints[recipient] += nMint;

        for (uint256 i = 0; i < nMint; i++)
        {
            _tokenIds.increment();
        }

        // Use _mint instead of _safeMint 
        // Because _mint is better when INDIVIDUALS are going to mint
        // While _safeMint is better when SMART CONTRACTS are going to mint
        _mint(recipient, nMint);
    }

    /**
    * @notice Main mint entry point
    */
    function mintNFT(uint256 nMint, uint8 v, bytes32 r, bytes32 s, string calldata nonce, uint256 deadline)
        external payable
    {
        require(currentMintPhase != MintPhase.CLOSED, "Mint period have not started yet");
        require(tx.origin == msg.sender, "No bots allowed");
        require(msg.value >= price * nMint, "Not enough ETH to mint");
        if (currentMintPhase == MintPhase.WHITELIST)
        {
            require(isRole(whitelistRole, nonce, deadline, v, r, s), "You are not whitelisted");
            require(mints[msg.sender] + nMint <= maxMintWhitelist, "Too much NFT minted");
        }
        else if (currentMintPhase == MintPhase.ALLOWLIST)
        {
            require(isRole(allowlistRole, nonce, deadline, v, r, s), "You are not allowlisted");
            require(mints[msg.sender] + nMint <= maxMintAllowlist, "Too much NFT minted");
        }
        else if (currentMintPhase == MintPhase.PUBLIC)
        {
            require(mints[msg.sender] + nMint <= maxMintPublic, "Too much NFT minted");
        }

        return _mintNFT(nMint, msg.sender);
    }

    /**
    * @notice Team giveway mint entry point
    */
    function giveaway(uint256 nMint, address recipient)
        external
        onlyApprovedOrOwner(msg.sender)
    {
        return _mintNFT(nMint, recipient);
    }

    /**
    * @notice Burn function
    */
    function burnNFT(uint256 tokenId)
        external
        onlyOwnerOfOrApproved(msg.sender, tokenId)
    {
        _burn(tokenId);
    }

    // Attributes getters
    ////////////////////

    /**
    * @notice Get metadatas of the given <tokenId>
    */
    function tokenURI(uint256 tokenId)
        public view
        override
        returns (string memory)
    {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory tokenIdURI = tokenURIs[tokenId];
        string memory emptyString = "";

        if (keccak256(abi.encodePacked(tokenIdURI)) == keccak256(abi.encodePacked(emptyString)))
        {
            return string(abi.encodePacked(abi.encodePacked(abi.encodePacked(baseURI, "/"), Strings.toString(tokenId)), ".json"));
        }
        else
        {
            return tokenIdURI;
        }        
    }

    // Attributes setters
    ////////////////////

    /**
    * @notice If <addr> is an approved caller, remove it from the list.
    * otherwise, add it to the list of approved callers
    */
    function toggleApprovedCaller(address addr)
        external
        onlyOwner
    {
        if (approvedCallers[addr] == 1)
        {
            approvedCallers[addr] = 0;
        }
        else
        {
            approvedCallers[addr] = 1;
        }
    }
    
    /**
    * @notice Set a specific <tokenIdURI> for a given <tokenId>
    */
    function setTokenUri(uint8 tokenId, string memory tokenIdURI)
        external
        onlyApprovedOrOwner(msg.sender)
    {
        tokenURIs[tokenId] = tokenIdURI;
    }

    /**
    * @notice Set the current mint phase (Whitelist, Allowlist or Public)
    */
    function setMintPhase(MintPhase _mintPhase)
        external
        onlyOwner
    {
        require(uint256(_mintPhase) >= 0 && uint256(_mintPhase) <= 3, "_mintPhase have to be between 0 and 3");
        require(_mintPhase > currentMintPhase, "new mint phase must be strictly greater than current one");
        currentMintPhase = _mintPhase;
    }
    
    /**
    * @notice Set the mint price
    */
    function setPrice(uint256 priceGwei)
        external
        onlyOwner
    {
        price = priceGwei * 10**9;
    }

    /**
    * @notice Set the base tokenURI, on which metadatas are stored like <_baseURI>/<token_id>.json
    */
    function setBaseUri(string memory _baseURI)
        external
        onlyOwner
    {
        baseURI = _baseURI;
    }

    /**
    * @notice Set the max mintable NFTs by Whitelisted address
    */
    function setMaxPerWalletWhitelist(uint8 maxMint)
        external
        onlyOwner
    {
        maxMintWhitelist = maxMint;
    }

    /**
    * @notice Set the max mintable NFTs by Allowlisted address
    */
    function setMaxPerWalletAllowlist(uint8 maxMint)
        external
        onlyOwner
    {
        maxMintAllowlist = maxMint;
    }

    /**
    * @notice Set the max mintable NFTs by Public (neither Whitelist or Allowlist) address
    */
    function setMaxPerWalletPublic(uint8 maxMint)
        external
        onlyOwner
    {
        maxMintPublic = maxMint;
    }

    /**
    * @notice Set the main team wallet
    */
    function setMainAddress(address _mainAddress)
        external
        onlyOwner
    {
        mainAddress = _mainAddress;
    }

    /**
    * @notice Set the address of the signer
    */
    function setSignerAddress(address addr)
        external
        onlyOwner
    {
        signerAddress = addr;
    }

    // Helpers function
    //////////////////////////

    /**
    * @notice Freeze the <tokenId> metadata, and attribute its key type (Regular, Robot, Ape, Zombie or Spirit)
    */
    function freezeKey(uint256 tokenId, uint8 percent, string calldata nonce, uint256 deadline, uint8 v, bytes32 r, bytes32 s)    
        external
    {
        require(ownerOf(tokenId) == msg.sender || msg.sender == owner(), "Not your NFT nor the owner");
        require(isAllowed(nonce, deadline, v, r, s), "You are not allowed");
        require(keyTypes[tokenId] == KeyType.REGULAR, "Key have already been frozen");
        require(percent >= 0 && percent <= 100, "Percent have to be between 0 and 100");

        keyPercents[tokenId] = percent;

        if (percent < 25)
        {
            revert("Nothing to claim");
        }
        else if (percent < 50 && (currentCountKeyTypes[KeyType.ROBOT] < maxCountKeyTypes[KeyType.ROBOT]))
        {
            keyTypes[tokenId] = KeyType.ROBOT;
            currentCountKeyTypes[KeyType.ROBOT]++;
        }
        else if (percent < 75 && (currentCountKeyTypes[KeyType.ZOMBIE] < maxCountKeyTypes[KeyType.ZOMBIE]))
        {
            keyTypes[tokenId] = KeyType.ZOMBIE;
            currentCountKeyTypes[KeyType.ZOMBIE]++;
        }
        else if (percent < 100 && (currentCountKeyTypes[KeyType.APE] < maxCountKeyTypes[KeyType.APE]))
        {
            keyTypes[tokenId] = KeyType.APE;
            currentCountKeyTypes[KeyType.APE]++;
        }
        else if (percent == 100 && (currentCountKeyTypes[KeyType.SPIRIT] < maxCountKeyTypes[KeyType.SPIRIT]))
        {
            keyTypes[tokenId] = KeyType.SPIRIT;
            currentCountKeyTypes[KeyType.SPIRIT]++;
        }
    }

    /**
    * @notice Freeze the Gold keys (1/1 NFTs)
    */
    function freezeGoldKeys(uint256[] calldata tokenIds)
        external
        onlyOwner
    {
        for (uint256 idx = 0; idx < tokenIds.length; idx++)
        {
            unchecked
            {
                keyPercents[tokenIds[idx]] = 100;
                keyTypes[tokenIds[idx]] = KeyType.GOLD;
            }
        }
    }

    /**
    * @notice Check the given signature params against signer address
        Used to verify signature validity
    */
    function isAllowed(string calldata nonce, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        internal view
        returns (bool)
    {
        require(block.timestamp <= deadline, "Signing too late");
        require(
            recoverSigner(msg.sender, nonce, deadline, v, r, s) == signerAddress,
            "Wrong signer"
        );
        return true;
    }

    /**
    * @notice Check the given signature params against signer address
        Used to verify role validity
    */
    function isRole(bytes32 role, string calldata nonce, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        internal view
        returns (bool)
    {
        require(block.timestamp <= deadline, "Signing too late");
        require(
            recoverRole(role, msg.sender, nonce, deadline, v, r, s) == signerAddress,
            "Wrong signer"
        );
        return true;
    }

    /**
    * @notice Retrieve the signer address induced by the given signature params
    */
    function recoverSigner(address addr, string memory nonce, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        internal pure
        returns (address)
    {
        return ecrecover(sha256(abi.encodePacked(addr, nonce, deadline)), v, r, s);
    }

    function recoverRole(bytes32 role, address addr, string memory nonce, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        internal pure
        returns (address)
    {
        return ecrecover(sha256(abi.encodePacked(role, addr, nonce, deadline)), v, r, s);
    }

    function withdraw()
        external onlyOwner
    {
        uint256 amount = address(this).balance;
        require(amount > 0, "Nothing to withdraw");
        // bool success = payable(mainAddress).send(amount);
        (bool success, ) = payable(mainAddress).call{value: amount}("");
        require(success, "Failed to withdraw");
    }

    receive()
        external payable
    {
    }

    fallback()
        external
    {
    }
}
设置
{
  "compilationTarget": {
    "teiko_key/TeikoKey.sol": "TeikoKey"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowlistRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedCallers","outputs":[{"internalType":"int8","name":"","type":"int8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"registrantToCopy","type":"address"}],"name":"copyEntriesOf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum TeikoKey.KeyType","name":"","type":"uint8"}],"name":"currentCountKeyTypes","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentMintPhase","outputs":[{"internalType":"enum TeikoKey.MintPhase","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"filteredOperators","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"freezeGoldKeys","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint8","name":"percent","type":"uint8"},{"internalType":"string","name":"nonce","type":"string"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"freezeKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nMint","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"giveaway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperatorAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"keyPercents","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"keyTypes","outputs":[{"internalType":"enum TeikoKey.KeyType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mainAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum TeikoKey.KeyType","name":"","type":"uint8"}],"name":"maxCountKeyTypes","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxMintAllowlist","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxMintPublic","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxMintWhitelist","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nMint","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"string","name":"nonce","type":"string"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"mintNFT","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"register","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"registrantToCopy","type":"address"}],"name":"registerAndCopyEntries","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"subscription","type":"address"}],"name":"registerAndSubscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_mainAddress","type":"address"}],"name":"setMainAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"maxMint","type":"uint8"}],"name":"setMaxPerWalletAllowlist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"maxMint","type":"uint8"}],"name":"setMaxPerWalletPublic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"maxMint","type":"uint8"}],"name":"setMaxPerWalletWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum TeikoKey.MintPhase","name":"_mintPhase","type":"uint8"}],"name":"setMintPhase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"priceGwei","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setSignerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"tokenId","type":"uint8"},{"internalType":"string","name":"tokenIdURI","type":"string"}],"name":"setTokenUri","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"subscription","type":"address"}],"name":"subscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"toggleApprovedCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenURIs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unregister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"copyExistingEntries","type":"bool"}],"name":"unsubscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"codeHash","type":"bytes32"},{"internalType":"bool","name":"filtered","type":"bool"}],"name":"updateCodeHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"codeHashes","type":"bytes32[]"},{"internalType":"bool","name":"filtered","type":"bool"}],"name":"updateCodeHashes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"filtered","type":"bool"}],"name":"updateOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"},{"internalType":"bool","name":"filtered","type":"bool"}],"name":"updateOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whitelistRole","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]