ETH Price: $2,735.30 (-0.80%)

Contract Diff Checker

Contract Name:
Quest3

Contract Source Code:

// SPDX-License-Identifier: AGPL-3.0
// ©2022 Ponderware Ltd

pragma solidity ^0.8.12;

import "./openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "./openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import "./openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";

interface IReverseResolver {
    function claim(address owner) external returns (bytes32);
}

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
}

interface IQuest3Data {
    function getDeath (uint256 seed, uint256 prevLevel, bytes[8] memory stats) external view returns (bytes memory death, string memory ending);
    function getFail (uint256 seed, uint256 level, bytes[8] memory stats) external view returns (bytes memory happening);
    function getAdvance (uint256 seed, uint256 level, bytes[8] memory stats) external view returns (bytes memory happening, bytes memory stat);
    function getMetadata (uint256 tokenId, uint256 level, uint8 journeyLength, bytes[15] memory storySegments, bytes[8] memory stats, uint16 heroStatus) external pure returns (string memory);
    function generateCompletionImage (uint tokenId, uint level, bytes memory lastWords, uint heroStatus) external pure returns (bytes memory);
    function generateProgressImage (uint tokenId, uint level) external pure returns (bytes memory);
}

/**
 * @title Quest3
 * @author Ponderware Ltd (a.k.a. Pondertech Digital Solutions)
 * @notice ERC-721 Quest Tokens (where will your journey lead?)
 * @dev ERC-721 Enumerable Token with fully-on-chain ERC721 Metadata
 */
contract Quest3 is IERC721Enumerable, IERC721Metadata {

    string public name = "Quest-3";
    string public symbol = unicode"⛰";

    uint256 public maxSupply = 25600;
    uint256 public totalSupply = 0;

    address public contractOwner;

    address[25600] internal Owners; // Maps tokenIds to owning addresses.
    mapping (address => uint256[]) internal TokensByOwner; // Mapping from address to owned tokens.
    uint16[25600] internal OwnerTokenIndex; // Maps the a tokenId to its index in the `TokensByOwner[address]` array.


    mapping(uint256 => address) internal TokenApprovals; // Mapping from token ID to approved address.
    mapping(address => mapping(address => bool)) internal OperatorApprovals; // Mapping from owner to operator approvals.

    bool paused = true; // Pausing stops all user interactions.
    bool frozen = false; // Freezing stops minting and actions.

    uint256 public MintPriceWei = 0.01994206980085 ether;

    /**
     * @dev Contains the journey information for the token. Idx 0 is the journey length, Idx [1..14] contains the reveal seed at level of the token at that journey position, and Idx 15 is a flag to indicate if an action is in the `ActionQueue`.
     */
    mapping (uint256 => uint16[16]) TokenHistory;

    /**
     * @dev The number of items a token must reveal to increase their hero status.
     */
    uint256 public HeroThreshold = 10;
    /**
     * @dev If a token reveals more than HeroThreshold actions, the number of reveals is added to that token's hero status.
     */
    uint16[25600] public HeroStatus;

    /**
     * @dev Reference to the metadata assembly contract.
     */
    IQuest3Data Data;

    // Owner Functions

    constructor (address quest3DataContract) {
        contractOwner = msg.sender;
        Data = IQuest3Data(quest3DataContract);
        IReverseResolver(0x084b1c3C81545d370f3634392De611CaaBFf8148).claim(msg.sender);
    }

    /**
     * @dev Change the owner of the contract.
     */
    function transferOwnership (address newOwner) public onlyOwner {
        contractOwner = newOwner;
    }

    function pause () public onlyOwner {
        paused = true;
    }

    function unpause () public onlyOwner {
        paused = false;
    }

    function ownerWithdraw () public {
        payable(contractOwner).transfer(address(this).balance);
    }

    function clearPendingStatus (uint256 tokenId) public onlyOwner {
        TokenHistory[tokenId][IS_PENDING_INDEX] = 0;
    }

    function setHeroThreshold (uint256 threshold) public onlyOwner {
        HeroThreshold = threshold;
    }

    /**
     * @dev Set `maxSupply` to `totalSupply` to end minting.
     */
    function permanentlyCloseMint() public onlyOwner {
        maxSupply = totalSupply;
    }

    /**
     * @dev When frozen action (and mint) calls will throw.
     */
    function setFrozen (bool state) public onlyOwner {
        frozen = state;
    }

    // Modifiers

    modifier onlyOwner() {
        require(msg.sender == contractOwner, "Not Owner");
        _;
    }

    modifier whenNotPaused() {
        require(paused == false || msg.sender == contractOwner, "Paused");
        _;
    }

    // Action Queue

    /**
     * @dev Actions are placed into the FIFO `ActionQueue` ring and revealed on future blocks.
     */
    Action[256] public ActionQueue;
    uint constant public MAX_QUEUE = 256;

    /**
     * @dev Actions are queued. The `revealBlock` is the block at which this action becomes eligible for reveal.
     */
    struct Action {
        uint128 revealBlock;
        uint128 tokenId;
    }

    /**
     * @dev `count` is the current length of the queue. `index` is the offset to the first queue item.
     */
    struct QueueCursor {
        uint16 index;
        uint16 count;
    }

    QueueCursor public Cursor = QueueCursor(0,0);

    function getQueueLength () public view returns (uint256) {
        return Cursor.count;
    }

    /**
     * @dev Assembles the `ActionQueue` into an array of actions in order (deconstructs the "ring").
     */
    function getQueue () public view returns (Action[] memory) {
        uint count = Cursor.count;
        uint index = Cursor.index;
        Action[] memory queue = new Action[](count);
        for (uint i = 0; i < queue.length; i++) {
            queue[i] = ActionQueue[index];
            index++;
            if(index == MAX_QUEUE) index = 0;
        }
        return queue;
    }

    // Quest Actions / Progress Handling

    /**
     * @dev Indexes into `TokenHistory` arrays. The seed/level data is stored in indexes [1..14].
     */
    uint256 constant JOURNEY_LENGTH_INDEX = 0;
    uint256 constant IS_PENDING_INDEX = 15;

    /**
     * @dev Reveals the most recent pending action on a token. Packs the result into [seed (12 bits), level (4 bits)].
     */
    function updateTokenHistory (uint256 tokenId) internal {
        uint16[16] storage history = TokenHistory[tokenId];
        uint journeyLength = history[JOURNEY_LENGTH_INDEX];

        uint level = history[journeyLength] & 15;

        uint prevLevel = 0;

        if (journeyLength == 0) {
            level = 1; // starting level
        } else if (journeyLength == 1) {
            prevLevel = 1; // starting level is always 1
        } else {
            prevLevel = history[journeyLength - 1] & 15; // prevLevel is penultimate level in pendingHistory
        }

        uint nextSeed = uint256(keccak256(abi.encodePacked(tokenId, blockhash(block.number-1))));

        uint resolution = nextSeed & 255;
        uint deathThreshold = 5 + level * 9;
        uint failThreshold = 90 + level * 22;
        if (level == 1) { deathThreshold = 2; } // low chance to die on level 1
        if (prevLevel == level) { failThreshold = 0; } // must die or advance
        if (resolution < deathThreshold) {
            level = 0; // died
        } else if (resolution >= failThreshold) {
            level = level + 1; // advanced
        }

        history[JOURNEY_LENGTH_INDEX] = uint16(journeyLength + 1);
        history[journeyLength + 1] = uint16((nextSeed << 4) + level);
        history[IS_PENDING_INDEX] = 0;
    }

    /**
     * @dev Reveals up to `maxReveals` pending `Action`s in the Action Queue, then enqueues the supplied `tokenId` if eligible.
     */
    function handleAction (uint256 tokenId, uint256 maxReveals) private whenNotPaused {
        require(frozen == false, "Frozen");
        uint count = Cursor.count;
        uint index = Cursor.index;
        if (maxReveals < 3) {
            maxReveals = 3;
        }
        uint revealCount = 0;
        for (uint i = 0; i < maxReveals; i++) {
            if (count == 0) break;
            Action storage action = ActionQueue[index];
            if (block.number <= action.revealBlock) break;
            updateTokenHistory(action.tokenId);
            delete ActionQueue[index];
            count--;
            index++;
            revealCount++;
            if(index == MAX_QUEUE) index = 0;
        }
        if (revealCount >= HeroThreshold) {
            HeroStatus[tokenId] += uint16(revealCount);
        }

        uint16[16] storage history = TokenHistory[tokenId];

        uint tokenJourneyLength = history[JOURNEY_LENGTH_INDEX];
        uint tokenLevel = history[tokenJourneyLength] & 15;

        if (((tokenLevel > 0 && tokenLevel < 8) || tokenJourneyLength == 0)
            && count < MAX_QUEUE
            && history[IS_PENDING_INDEX] == 0)
        {
            uint tokenQueueIndex = count + index;
            count++;
            if (MAX_QUEUE <= tokenQueueIndex) {
                tokenQueueIndex -= MAX_QUEUE;
            }
            ActionQueue[tokenQueueIndex] = Action(uint128(block.number + 1), uint128(tokenId));
            history[IS_PENDING_INDEX] = 1;
        }
        Cursor.count = uint16(count);
        Cursor.index = uint16(index);
    }

    /**
     * @notice Like `doSomething` but set a max number of reveals to perform (must be >= HeroThreshold). If it reveals enough, the number of reveals will be added to the tokens HeroScore. Can be called even if your quest is complete.
     * @dev Cannot be called by a smart contract.
     */
    function doSomethingHeroic (uint256 tokenId, uint256 maxAssists) public {
        require(msg.sender == Owners[tokenId] && msg.sender == tx.origin, "Not Owner");
        require(maxAssists >= HeroThreshold, "A true hero must assist many others");
        handleAction(tokenId, maxAssists);
    }

    /**
     * @notice Places the token into the `ActionQueue` where it will be revealed by actions in future blocks. Reveals up to 3 pending actions.
     * @dev Cannot be called by a smart contract.
     */
    function doSomething (uint256 tokenId) public {
        require(msg.sender == Owners[tokenId] && msg.sender == tx.origin, "Not Owner");
        handleAction(tokenId, 3);
    }

    /**
     * @notice Like `doSomething` but allows multiple tokenIds to be put in the ActionQueue.
     * @dev Cannot be called by a smart contract.
     */
    function doSomething (uint256[] memory tokenIds) public {
        require(msg.sender == tx.origin);
        for (uint i = 0; i < tokenIds.length; i++) {
            uint256 tokenId = tokenIds[i];
            require(msg.sender == Owners[tokenId], "Not Owner");
            handleAction(tokenId, 3);
        }
    }

    // Minting

    /**
     * @dev Bookkeeping for minting. Note: minting does not guarantee entry into the `ActionQueue`.
     */
    function mintHelper (address recipient) private  {
        uint256 tokenId = totalSupply;
        TokensByOwner[recipient].push(tokenId);
        OwnerTokenIndex[tokenId] = uint16(TokensByOwner[recipient].length);
        Owners[tokenId] = recipient;
        totalSupply++;
        handleAction(tokenId, 3);
        emit Transfer(address(0), recipient, tokenId);
    }

    /**
     * @notice Mint tokens to the provided recipient address, quantity per call is limited to 10.
     */
    function mint (address recipient, uint256 quantity) public payable whenNotPaused {
        require (quantity <= 10, "Quantity Limit Exceeded");
        require (totalSupply + quantity <= maxSupply, "Max Supply Exceeded");
        uint256 cost = quantity * MintPriceWei;
        require(msg.value >= cost, "Insufficent Funds");
        for (uint i = 0; i < quantity; i++) {
            mintHelper(recipient);
        }
    }

    /**
     * @notice Mint tokens to msg.sender, quantity per call is limited to 10.
     */
    function mint (uint256 quantity) public payable {
        mint(msg.sender, quantity);
    }

    /**
     * @notice Mint tokens to an array of recipient addresses, array length must be <= 10.
     */
    function mint (address[] memory recipients) public payable whenNotPaused {
        uint quantity = recipients.length;
        require (quantity <= 10 || msg.sender == contractOwner, "Quantity Limit Exceeded");
        require (totalSupply + quantity <= maxSupply, "Max Supply Exceeded");
        uint256 cost = quantity * MintPriceWei;
        require(msg.value >= cost, "Insufficent Funds");
        for (uint i = 0; i < quantity; i++) {
            mintHelper(recipients[i]);
        }
    }


    // Quest Info

    /**
     * @notice Shows where the token is in the `Action Queue`.
     * @dev `pending` indicates the token is in the queue. `position` is the number of preceding Actions. `revealBlock` is the block at which the action becomes eligible for reveal.
     */
    function isPending (uint256 tokenId) public view returns (bool pending, uint position, uint revealBlock) {
        pending = TokenHistory[tokenId][IS_PENDING_INDEX] == 1;
        if (pending) {
            uint count = Cursor.count;
            uint index = Cursor.index;
            for (uint i = 0; i < count; i++) {
                Action storage action = ActionQueue[index];
                if (action.tokenId == tokenId) {
                    position = i;
                    revealBlock = action.revealBlock;
                    break;
                }
                index++;
                if(index == MAX_QUEUE) index = 0;
            }
        }
    }

    /**
     * @notice Fetches details used to generate token metadata. `level` => indicates numeric stage of the quest. `journeyLength` => number of revealed actions. `storySegments` => text corresponding to each reveled action. `stats` => attributes collected on the quest. `heroStatus` => number of tokens revealed through `doSomethingHeroic`.
     * @dev `level` will be in range [0(ngmi)..8(gmi)]. `storySegments` will have `journeyLength` entries unless `level` == 0 in which case it will have one additional element. `stats` indexes correspond to levels - 1.
     */
    function getDetails (uint256 tokenId) public view returns (uint256 level,
                                                               uint8 journeyLength,
                                                               bytes[15] memory storySegments,
                                                               bytes[8] memory stats,
                                                               uint16 heroStatus)
    {
        require(tokenId < totalSupply, "Doesn't Exist");
        uint16[16] storage tokenHistory = TokenHistory[tokenId];
        journeyLength = uint8(tokenHistory[JOURNEY_LENGTH_INDEX]);
        level = 1; // if quest has just begun, level will be 1
        uint prevLevel = 1;
        for (uint i = 1; i <= journeyLength; i++) {
            uint256 seed = uint256(keccak256(abi.encodePacked(tokenHistory[i], tokenId)));
            level = tokenHistory[i] & 15;
            if (level == 0) {
                (bytes memory storySegment, string memory ending) = Data.getDeath(seed, prevLevel, stats);
                stats[7] = storySegment;
                storySegments[i-1] = storySegment;
                storySegments[i] = bytes(ending);
            } else if (prevLevel == level) {
                storySegments[i-1] = Data.getFail(seed, level, stats);
            } else {
                (bytes memory storySegment, bytes memory stat) = Data.getAdvance(seed, level, stats);
                stats[level - 1] = stat;
                storySegments[i-1] = storySegment;
            }
            prevLevel = level;
        }

        heroStatus = HeroStatus[tokenId];

        if (tokenHistory[IS_PENDING_INDEX] == 1) {
            stats[0] = "Pending";
        } else if (level == 0) {
            stats[0] = "NGMI";
        } else if (level == 8) {
            stats[0] = "GMI";
        } else {
            stats[0] = "Questing";
        }
    }

    /**
     * @notice Fetches the current stage of the journey in numeric terms. 0 => NGMI. 8 => GMI.
     * @dev `level` is always in the range [0..8].
     */
    function getLevel (uint256 tokenId) public view returns (uint256 level) {
        require(tokenId < totalSupply, "Doesn't Exist");
        uint16[16] storage tokenHistory = TokenHistory[tokenId];
        uint16 journeyLength = tokenHistory[JOURNEY_LENGTH_INDEX];
        if (journeyLength == 0) {
            return 1;
        } else {
            return (tokenHistory[journeyLength] & 15);
        }
    }

    function getSym(int seed) internal pure returns (uint8) {
        if (seed & 1 == 0) return 0;
        if ((seed >> 1) & 1 == 0) {
            return 1;
        }
        return 2;
    }

    /**
     * @dev `cartouche` is an array of chevron positions and orientations. 0 => None, 1 => Right, 2 => Left. Data is only valid if `level` == 8.
     */
    function getMysteriousCartouche (uint256 tokenId) public view returns (uint8 level, uint8[6] memory cartouche) {
        (uint256 currentLevel,uint8 journeyLength,, bytes[8] memory stats,) = getDetails(tokenId);
        if (currentLevel == 8) {
            int seed = int(uint256(keccak256(abi.encodePacked(tokenId, stats[7]))) >> 141);
            cartouche[0] = getSym(seed);
            cartouche[1] = getSym(seed >> 2);
            cartouche[2] = getSym(seed >> 4);
            cartouche[3] = getSym(seed >> 6);
            cartouche[4] = getSym(seed >> 8);
            cartouche[5] = getSym(seed >> 10);
        }
        if (journeyLength > 0) {
            level = uint8(currentLevel);
        } else {
            level = 1;
        }
    }

    // ERC-721 Metadata

    /**
     * @notice Assembles and returns the Base64 encoded token URI containing the JSON token's metadata. Assembled entirely on-chain.
     */
    function tokenURI (uint256 tokenId) public view returns (string memory) {
        (uint256 level, uint8 journeyLength, bytes[15] memory storySegments, bytes[8] memory stats, uint16 heroStatus) = getDetails(tokenId);
        return Data.getMetadata(tokenId, level, journeyLength, storySegments, stats, heroStatus);
    }

    /**
     * @notice Assembles and returns the token's SVG image. Assembled entirely on-chain.
     */
    function tokenSVG (uint256 tokenId) public view returns (string memory svg) {
        (uint256 level, uint8 journeyLength,, bytes[8] memory stats, uint16 heroStatus) = getDetails(tokenId);
        if (journeyLength > 0 && (level == 0 || level == 8)) {
            svg = string(Data.generateCompletionImage(tokenId, level, stats[7], heroStatus));
        } else {
            svg = string(Data.generateProgressImage(tokenId, level));
        }
    }

    // ERC-721 Base

    function tokenExists(uint256 tokenId) public view returns (bool) {
        return (tokenId < totalSupply);
    }

    function ownerOf(uint256 tokenId) public view returns (address) {
        require(tokenExists(tokenId), "ERC721: Nonexistent token");
        return Owners[tokenId];
    }

    function balanceOf(address owner) public view returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return TokensByOwner[owner].length;
    }

    function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC721Enumerable).interfaceId;
    }

    function _approve(address to, uint256 tokenId) internal {
        TokenApprovals[tokenId] = to;
        emit Approval(ownerOf(tokenId), to, tokenId);
    }

    function approve(address to, uint256 tokenId) public  {
        address owner = ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
                msg.sender == owner || isApprovedForAll(owner, msg.sender),
                "ERC721: approve caller is not owner nor approved for all"
                );
        _approve(to, tokenId);
    }

    function getApproved(uint256 tokenId) public view returns (address) {
        require(tokenId < totalSupply, "ERC721: approved query for nonexistent token");
        return TokenApprovals[tokenId];
    }

    function isApprovedForAll(address owner, address operator) public view  returns (bool) {
        return OperatorApprovals[owner][operator];
    }

    function setApprovalForAll(
                               address operator,
                               bool approved
                               ) external virtual {
        require(msg.sender != operator, "ERC721: approve to caller");
        OperatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
        size := extcodesize(account)
                }
        return size > 0;
    }

    function _checkOnERC721Received(
                                    address from,
                                    address to,
                                    uint256 tokenId,
                                    bytes memory _data
                                    ) private returns (bool) {
        if (isContract(to)) {
            try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                            }
                }
            }
        } else {
            return true;
        }
    }

    function _transfer(
                       address from,
                       address to,
                       uint256 tokenId
                       ) private whenNotPaused {
        require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");
        _approve(address(0), tokenId);

        uint16 valueIndex = OwnerTokenIndex[tokenId];
        uint256 toDeleteIndex = valueIndex - 1;
        uint256 lastIndex = TokensByOwner[from].length - 1;
        if (lastIndex != toDeleteIndex) {
            uint256 lastTokenId = TokensByOwner[from][lastIndex];
            TokensByOwner[from][toDeleteIndex] = lastTokenId;
            OwnerTokenIndex[lastTokenId] = valueIndex;
        }
        TokensByOwner[from].pop();

        TokensByOwner[to].push(tokenId);
        OwnerTokenIndex[tokenId] = uint16(TokensByOwner[to].length);

        Owners[tokenId] = to;
        emit Transfer(from, to, tokenId);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
        require(tokenId < totalSupply, "ERC721: operator query for nonexistent token");
        address owner = ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    function transferFrom(
                          address from,
                          address to,
                          uint256 tokenId
                          ) public {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    function safeTransferFrom(
                              address from,
                              address to,
                              uint256 tokenId
                              ) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(
                              address from,
                              address to,
                              uint256 tokenId,
                              bytes memory _data
                              ) public {
        require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }


    function _safeTransfer(
                           address from,
                           address to,
                           uint256 tokenId,
                           bytes memory _data
                           ) private {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    // Enumerable

    function tokenByIndex(uint256 tokenId) public view returns (uint256) {
        require(tokenExists(tokenId), "Nonexistent Token");
        return tokenId;
    }

    function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
        require(index < balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return TokensByOwner[owner][index];
    }

    // Rescuers

    /**
    * @dev Rescue ERC20 assets sent directly to this contract.
    */
    function withdrawForeignERC20(address tokenContract) public onlyOwner {
        IERC20 token = IERC20(tokenContract);
        token.transfer(contractOwner, token.balanceOf(address(this)));
        }

    /**
     * @dev Rescue ERC721 assets sent directly to this contract.
     */
    function withdrawForeignERC721(address tokenContract, uint256 tokenId) public onlyOwner {
        IERC721(tokenContract).safeTransferFrom(address(this), contractOwner, tokenId);
    }
}

// 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):