ETH Price: $3,753.15 (+5.59%)

Transaction Decoder

Block:
13762703 at Dec-08-2021 04:23:23 AM +UTC
Transaction Fee:
0.068379685902572608 ETH $256.64
Gas Used:
922,528 Gas / 74.122070986 Gwei

Emitted Events:

42 WnD.WizardMinted( tokenId=42656 )
43 WnD.Transfer( from=0x00000000...000000000, to=Tower, tokenId=42656 )
44 WnD.WizardMinted( tokenId=42657 )
45 WnD.Transfer( from=0x00000000...000000000, to=Tower, tokenId=42657 )
46 WnD.DragonMinted( tokenId=42658 )
47 WnD.Transfer( from=0x00000000...000000000, to=Tower, tokenId=42658 )
48 Tower.TokenStaked( owner=[Sender] 0x3f32613fd7f10f47469e344de162d72e619fd94e, tokenId=42656, isWizard=True, value=1638937403 )
49 Tower.TokenStaked( owner=[Sender] 0x3f32613fd7f10f47469e344de162d72e619fd94e, tokenId=42657, isWizard=True, value=1638937403 )
50 Tower.TokenStaked( owner=[Sender] 0x3f32613fd7f10f47469e344de162d72e619fd94e, tokenId=42658, isWizard=False, value=21852796072328152981473 )
51 WnDGameCR.MintCommitted( owner=[Sender] 0x3f32613fd7f10f47469e344de162d72e619fd94e, amount=3 )

Account State Difference:

  Address   Before After State Difference Code
0x1C519466...077cDd732
0x3F32613F...e619fD94E
0.532365897670963363 Eth
Nonce: 69
0.463986211768390755 Eth
Nonce: 70
0.068379685902572608
0x999e8807...f39A1D3ab
(Miner: 0xab3...83a)
4,153.792442020299951451 Eth4,153.793364548299951451 Eth0.000922528
0xF042A49F...85dC72281
(Wizards & Dragons: Tower V1)

Execution Trace

WnDGameCR.CALL( )
  • WnD.CALL( )
  • WnD.mint( recipient=0xF042A49FB03cb9D98CbA9DEf8711CEE85dC72281, seed=53532159681307922466072580219416067988045286863698843082977578238372872562827 )
    • Tower.onERC721Received( 0x1C5194668FaAb6a895b1febBF3dc219077cDd732, from=0x0000000000000000000000000000000000000000, 42656, 0x )
    • WnD.mint( recipient=0xF042A49FB03cb9D98CbA9DEf8711CEE85dC72281, seed=32295724700029976755306946051852491450883883621936175203700450210163591988216 )
      • Tower.onERC721Received( 0x1C5194668FaAb6a895b1febBF3dc219077cDd732, from=0x0000000000000000000000000000000000000000, 42657, 0x )
      • WnD.mint( recipient=0xF042A49FB03cb9D98CbA9DEf8711CEE85dC72281, seed=20805035051757925541520150991850387930415019580529558777979216340168892069624 )
        • Tower.onERC721Received( 0x1C5194668FaAb6a895b1febBF3dc219077cDd732, from=0x0000000000000000000000000000000000000000, 42658, 0x )
        • WnD.updateOriginAccess( tokenIds=[42656, 42657, 42658] )
        • Tower.addManyToTowerAndFlight( account=0x3F32613Fd7f10F47469e344dE162d72e619fD94E, tokenIds=[42656, 42657, 42658] )
          • WnD.isWizard( tokenId=42656 ) => ( True )
          • WnD.isWizard( tokenId=42657 ) => ( True )
          • WnD.isWizard( tokenId=42658 ) => ( False )
          • WnD.getTokenTraits( tokenId=42658 ) => ( [{name:isWizard, type:bool, order:1, indexed:false, value:false, valueString:False}, {name:body, type:uint8, order:2, indexed:false, value:4, valueString:4}, {name:head, type:uint8, order:3, indexed:false, value:0, valueString:0}, {name:spell, type:uint8, order:4, indexed:false, value:0, valueString:0}, {name:eyes, type:uint8, order:5, indexed:false, value:3, valueString:3}, {name:neck, type:uint8, order:6, indexed:false, value:0, valueString:0}, {name:mouth, type:uint8, order:7, indexed:false, value:4, valueString:4}, {name:wand, type:uint8, order:8, indexed:false, value:0, valueString:0}, {name:tail, type:uint8, order:9, indexed:false, value:2, valueString:2}, {name:rankIndex, type:uint8, order:10, indexed:false, value:3, valueString:3}] )
            File 1 of 3: WnDGameCR
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/access/Ownable.sol";
            import "@openzeppelin/contracts/security/Pausable.sol";
            import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
            import "./interfaces/IWnDGame.sol";
            import "./interfaces/ITower.sol";
            import "./interfaces/ITraits.sol";
            import "./interfaces/IGP.sol";
            import "./interfaces/IWnD.sol";
            import "./interfaces/ISacrificialAlter.sol";
            import "./interfaces/IRandomizer.sol";
            contract WnDGameCR is IWnDGame, Ownable, ReentrancyGuard, Pausable {
              event MintCommitted(address indexed owner, uint256 indexed amount);
              event MintRevealed(address indexed owner, uint256 indexed amount);
              struct MintCommit {
                bool stake;
                uint16 amount;
              }
              uint256 public treasureChestTypeId;
              // max $GP cost 
              uint256 private maxGpCost = 72000 ether;
              // address -> commit # -> commits
              mapping(address => mapping(uint16 => MintCommit)) private _mintCommits;
              // address -> commit num of commit need revealed for account
              mapping(address => uint16) private _pendingCommitId;
              // commit # -> offchain random
              mapping(uint16 => uint256) private _commitRandoms;
              uint16 private _commitId = 1;
              uint16 private pendingMintAmt;
              bool public allowCommits = true;
              // address => can call addCommitRandom
              mapping(address => bool) private admins;
              // reference to the Tower for choosing random Dragon thieves
              ITower public tower;
              // reference to $GP for burning on mint
              IGP public gpToken;
              // reference to Traits
              ITraits public traits;
              // reference to NFT collection
              IWnD public wndNFT;
              // reference to alter collection
              ISacrificialAlter public alter;
              constructor() {
                _pause();
              }
              /** CRITICAL TO SETUP */
              modifier requireContractsSet() {
                  require(address(gpToken) != address(0) && address(traits) != address(0) 
                    && address(wndNFT) != address(0) && address(tower) != address(0) && address(alter) != address(0)
                    , "Contracts not set");
                  _;
              }
              function setContracts(address _gp, address _traits, address _wnd, address _tower, address _alter) external onlyOwner {
                gpToken = IGP(_gp);
                traits = ITraits(_traits);
                wndNFT = IWnD(_wnd);
                tower = ITower(_tower);
                alter = ISacrificialAlter(_alter);
              }
              /** EXTERNAL */
              function getPendingMint(address addr) external view returns (MintCommit memory) {
                require(_pendingCommitId[addr] != 0, "no pending commits");
                return _mintCommits[addr][_pendingCommitId[addr]];
              }
              function hasMintPending(address addr) external view returns (bool) {
                return _pendingCommitId[addr] != 0;
              }
              function canMint(address addr) external view returns (bool) {
                return _pendingCommitId[addr] != 0 && _commitRandoms[_pendingCommitId[addr]] > 0;
              }
              // Seed the current commit id so that pending commits can be revealed
              function addCommitRandom(uint256 seed) external {
                require(owner() == _msgSender() || admins[_msgSender()], "Only admins can call this");
                _commitRandoms[_commitId] = seed;
                _commitId += 1;
              }
              function deleteCommit(address addr) external {
                require(owner() == _msgSender() || admins[_msgSender()], "Only admins can call this");
                uint16 commitIdCur = _pendingCommitId[_msgSender()];
                require(commitIdCur > 0, "No pending commit");
                delete _mintCommits[addr][commitIdCur];
                delete _pendingCommitId[addr];
              }
              function forceRevealCommit(address addr) external {
                require(owner() == _msgSender() || admins[_msgSender()], "Only admins can call this");
                reveal(addr);
              }
              /** Initiate the start of a mint. This action burns $GP, as the intent of committing is that you cannot back out once you've started.
                * This will add users into the pending queue, to be revealed after a random seed is generated and assigned to the commit id this
                * commit was added to. */
              function mintCommit(uint256 amount, bool stake) external whenNotPaused nonReentrant {
                require(allowCommits, "adding commits disallowed");
                require(tx.origin == _msgSender(), "Only EOA");
                require(_pendingCommitId[_msgSender()] == 0, "Already have pending mints");
                uint16 minted = wndNFT.minted();
                uint256 maxTokens = wndNFT.getMaxTokens();
                require(minted + pendingMintAmt + amount <= maxTokens, "All tokens minted");
                require(amount > 0 && amount <= 10, "Invalid mint amount");
                uint256 totalGpCost = 0;
                // Loop through the amount of 
                for (uint i = 1; i <= amount; i++) {
                  totalGpCost += mintCost(minted + pendingMintAmt + i, maxTokens);
                }
                if (totalGpCost > 0) {
                  gpToken.burn(_msgSender(), totalGpCost);
                  gpToken.updateOriginAccess();
                }
                uint16 amt = uint16(amount);
                _mintCommits[_msgSender()][_commitId] = MintCommit(stake, amt);
                _pendingCommitId[_msgSender()] = _commitId;
                pendingMintAmt += amt;
                emit MintCommitted(_msgSender(), amount);
              }
              /** Reveal the commits for this user. This will be when the user gets their NFT, and can only be done when the commit id that
                * the user is pending for has been assigned a random seed. */
              function mintReveal() external whenNotPaused nonReentrant {
                require(tx.origin == _msgSender(), "Only EOA1");
                reveal(_msgSender());
              }
              function reveal(address addr) internal {
                uint16 commitIdCur = _pendingCommitId[addr];
                require(commitIdCur > 0, "No pending commit");
                require(_commitRandoms[commitIdCur] > 0, "random seed not set");
                uint16 minted = wndNFT.minted();
                MintCommit memory commit = _mintCommits[addr][commitIdCur];
                pendingMintAmt -= commit.amount;
                uint16[] memory tokenIds = new uint16[](commit.amount);
                uint16[] memory tokenIdsToStake = new uint16[](commit.amount);
                uint256 seed = _commitRandoms[commitIdCur];
                for (uint k = 0; k < commit.amount; k++) {
                  minted++;
                  // scramble the random so the steal / treasure mechanic are different per mint
                  seed = uint256(keccak256(abi.encode(seed, addr)));
                  address recipient = selectRecipient(seed);
                  if(recipient != addr && alter.balanceOf(addr, treasureChestTypeId) > 0) {
                    // If the mint is going to be stolen, there's a 50% chance 
                    //  a dragon will prefer a treasure chest over it
                    if(seed & 1 == 1) {
                      alter.safeTransferFrom(addr, recipient, treasureChestTypeId, 1, "");
                      recipient = addr;
                    }
                  }
                  tokenIds[k] = minted;
                  if (!commit.stake || recipient != addr) {
                    wndNFT.mint(recipient, seed);
                  } else {
                    wndNFT.mint(address(tower), seed);
                    tokenIdsToStake[k] = minted;
                  }
                }
                wndNFT.updateOriginAccess(tokenIds);
                if(commit.stake) {
                  tower.addManyToTowerAndFlight(addr, tokenIdsToStake);
                }
                delete _mintCommits[addr][commitIdCur];
                delete _pendingCommitId[addr];
                emit MintCommitted(addr, tokenIds.length);
              }
              /** 
               * @param tokenId the ID to check the cost of to mint
               * @return the cost of the given token ID
               */
              function mintCost(uint256 tokenId, uint256 maxTokens) public view returns (uint256) {
                if (tokenId <= maxTokens * 8 / 20) return 24000 ether;
                if (tokenId <= maxTokens * 11 / 20) return 36000 ether;
                if (tokenId <= maxTokens * 14 / 20) return 48000 ether;
                if (tokenId <= maxTokens * 17 / 20) return 60000 ether; 
                // if (tokenId > maxTokens * 17 / 20)
                return maxGpCost;
              }
              function payTribute(uint256 gpAmt) external whenNotPaused nonReentrant {
                require(tx.origin == _msgSender(), "Only EOA");
                uint16 minted = wndNFT.minted();
                uint256 maxTokens = wndNFT.getMaxTokens();
                uint256 gpMintCost = mintCost(minted, maxTokens);
                require(gpMintCost > 0, "Sacrificial alter currently closed");
                require(gpAmt >= gpMintCost, "Not enough gp given");
                gpToken.burn(_msgSender(), gpAmt);
                if(gpAmt < gpMintCost * 2) {
                  alter.mint(1, 1, _msgSender());
                }
                else {
                  alter.mint(2, 1, _msgSender());
                }
              }
              function makeTreasureChests(uint16 qty) external whenNotPaused {
                require(tx.origin == _msgSender(), "Only EOA");
                require(treasureChestTypeId > 0, "DEVS DO SOMETHING");
                // $GP exchange amount handled within alter contract
                // Will fail if sender doesn't have enough $GP
                // Transfer does not need approved,
                //  as there is established trust between this contract and the alter contract 
                alter.mint(treasureChestTypeId, qty, _msgSender());
              }
              function sellTreasureChests(uint16 qty) external whenNotPaused {
                require(tx.origin == _msgSender(), "Only EOA");
                require(treasureChestTypeId > 0, "DEVS DO SOMETHING");
                // $GP exchange amount handled within alter contract
                alter.burn(treasureChestTypeId, qty, _msgSender());
              }
              function sacrifice(uint256 tokenId, uint256 gpAmt) external whenNotPaused nonReentrant {
                require(tx.origin == _msgSender(), "Only EOA");
                uint64 lastTokenWrite = wndNFT.getTokenWriteBlock(tokenId);
                // Must check this, as getTokenTraits will be allowed since this contract is an admin
                require(lastTokenWrite < block.number, "hmmmm what doing?");
                IWnD.WizardDragon memory nft = wndNFT.getTokenTraits(tokenId);
                uint16 minted = wndNFT.minted();
                uint256 maxTokens = wndNFT.getMaxTokens();
                uint256 gpMintCost = mintCost(minted, maxTokens);
                require(gpMintCost > 0, "Sacrificial alter currently closed");
                if(nft.isWizard) {
                  // Wizard sacrifice requires 3x $GP curve
                  require(gpAmt >= gpMintCost * 3, "not enough gp provided");
                  gpToken.burn(_msgSender(), gpAmt);
                  // This will check if origin is the owner of the token
                  wndNFT.burn(tokenId);
                  alter.mint(3, 1, _msgSender());
                }
                else {
                  // Dragon sacrifice requires 4x $GP curve
                  require(gpAmt >= gpMintCost * 4, "not enough gp provided");
                  gpToken.burn(_msgSender(), gpAmt);
                  // This will check if origin is the owner of the token
                  wndNFT.burn(tokenId);
                  alter.mint(4, 1, _msgSender());
                }
              }
              /** INTERNAL */
              /**
               * the first 25% (ETH purchases) go to the minter
               * the remaining 80% have a 10% chance to be given to a random staked dragon
               * @param seed a random value to select a recipient from
               * @return the address of the recipient (either the minter or the Dragon thief's owner)
               */
              function selectRecipient(uint256 seed) internal view returns (address) {
                if (((seed >> 245) % 10) != 0) return _msgSender(); // top 10 bits haven't been used
                address thief = tower.randomDragonOwner(seed >> 144); // 144 bits reserved for trait selection
                if (thief == address(0x0)) return _msgSender();
                return thief;
              }
              /** ADMIN */
              /**
               * enables owner to pause / unpause contract
               */
              function setPaused(bool _paused) external requireContractsSet onlyOwner {
                if (_paused) _pause();
                else _unpause();
              }
              function setMaxGpCost(uint256 _amount) external requireContractsSet onlyOwner {
                maxGpCost = _amount;
              } 
              function setTreasureChestId(uint256 typeId) external onlyOwner {
                treasureChestTypeId = typeId;
              }
              function setAllowCommits(bool allowed) external onlyOwner {
                allowCommits = allowed;
              }
              /** Allow the contract owner to set the pending mint amount.
                * This allows any long-standing pending commits to be overwritten, say for instance if the max supply has been 
                *  reached but there are many stale pending commits, it could be used to free up those spaces if needed/desired by the community.
                * This function should not be called lightly, this will have negative consequences on the game. */
              function setPendingMintAmt(uint256 pendingAmt) external onlyOwner {
                pendingMintAmt = uint16(pendingAmt);
              }
              /**
              * enables an address to mint / burn
              * @param addr the address to enable
              */
              function addAdmin(address addr) external onlyOwner {
                  admins[addr] = true;
              }
              /**
              * disables an address from minting / burning
              * @param addr the address to disbale
              */
              function removeAdmin(address addr) external onlyOwner {
                  admins[addr] = false;
              }
              /**
               * allows owner to withdraw funds from minting
               */
              function withdraw() external onlyOwner {
                payable(owner()).transfer(address(this).balance);
              }
            }// SPDX-License-Identifier: MIT
            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() {
                    _setOwner(_msgSender());
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    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 {
                    _setOwner(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");
                    _setOwner(newOwner);
                }
                function _setOwner(address newOwner) private {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../utils/Context.sol";
            /**
             * @dev Contract module which allows children to implement an emergency stop
             * mechanism that can be triggered by an authorized account.
             *
             * This module is used through inheritance. It will make available the
             * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
             * the functions of your contract. Note that they will not be pausable by
             * simply including this module, only once the modifiers are put in place.
             */
            abstract contract Pausable is Context {
                /**
                 * @dev Emitted when the pause is triggered by `account`.
                 */
                event Paused(address account);
                /**
                 * @dev Emitted when the pause is lifted by `account`.
                 */
                event Unpaused(address account);
                bool private _paused;
                /**
                 * @dev Initializes the contract in unpaused state.
                 */
                constructor() {
                    _paused = false;
                }
                /**
                 * @dev Returns true if the contract is paused, and false otherwise.
                 */
                function paused() public view virtual returns (bool) {
                    return _paused;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                modifier whenNotPaused() {
                    require(!paused(), "Pausable: paused");
                    _;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                modifier whenPaused() {
                    require(paused(), "Pausable: not paused");
                    _;
                }
                /**
                 * @dev Triggers stopped state.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                function _pause() internal virtual whenNotPaused {
                    _paused = true;
                    emit Paused(_msgSender());
                }
                /**
                 * @dev Returns to normal state.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                function _unpause() internal virtual whenPaused {
                    _paused = false;
                    emit Unpaused(_msgSender());
                }
            }
            // SPDX-License-Identifier: MIT
            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 nonReentrant will be lower in
                // amount. Since refunds are capped to a percentage of the total
                // transaction's gas, it is best to keep them low in cases like this one, to
                // increase the likelihood of the full refund coming into effect.
                uint256 private constant _NOT_ENTERED = 1;
                uint256 private constant _ENTERED = 2;
                uint256 private _status;
                constructor() {
                    _status = _NOT_ENTERED;
                }
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                    // Any calls to nonReentrant after this point will fail
                    _status = _ENTERED;
                    _;
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _status = _NOT_ENTERED;
                }
            }
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface IWnDGame {
              
            }// SPDX-License-Identifier: MIT LICENSE 
            pragma solidity ^0.8.0;
            interface ITower {
              function addManyToTowerAndFlight(address account, uint16[] calldata tokenIds) external;
              function randomDragonOwner(uint256 seed) external view returns (address);
            }// SPDX-License-Identifier: MIT LICENSE 
            pragma solidity ^0.8.0;
            interface ITraits {
              function tokenURI(uint256 tokenId) external view returns (string memory);
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface IGP {
                function mint(address to, uint256 amount) external;
                function burn(address from, uint256 amount) external;
                function updateOriginAccess() external;
                function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
            interface IWnD is IERC721Enumerable {
                // game data storage
                struct WizardDragon {
                    bool isWizard;
                    uint8 body;
                    uint8 head;
                    uint8 spell;
                    uint8 eyes;
                    uint8 neck;
                    uint8 mouth;
                    uint8 wand;
                    uint8 tail;
                    uint8 rankIndex;
                }
                function minted() external returns (uint16);
                function updateOriginAccess(uint16[] memory tokenIds) external;
                function mint(address recipient, uint256 seed) external;
                function burn(uint256 tokenId) external;
                function getMaxTokens() external view returns (uint256);
                function getPaidTokens() external view returns (uint256);
                function getTokenTraits(uint256 tokenId) external view returns (WizardDragon memory);
                function getTokenWriteBlock(uint256 tokenId) external view returns(uint64);
                function isWizard(uint256 tokenId) external view returns(bool);
              
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface ISacrificialAlter {
                function mint(uint256 typeId, uint16 qty, address recipient) external;
                function burn(uint256 typeId, uint16 qty, address burnFrom) external;
                function updateOriginAccess() external;
                function balanceOf(address account, uint256 id) external returns (uint256);
                function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) external;
            }pragma solidity ^0.8.0;
            interface IRandomizer {
                function random() external returns (uint256);
            }// SPDX-License-Identifier: MIT
            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;
                }
            }
            // SPDX-License-Identifier: MIT
            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 tokenId);
                /**
                 * @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
            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
            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);
            }
            

            File 2 of 3: WnD
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/access/Ownable.sol";
            import "@openzeppelin/contracts/security/Pausable.sol";
            import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
            import "./interfaces/IWnD.sol";
            import "./interfaces/ITower.sol";
            import "./interfaces/ITraits.sol";
            import "./interfaces/IGP.sol";
            import "./interfaces/IRandomizer.sol";
            contract WnD is IWnD, ERC721Enumerable, Ownable, Pausable {
                struct LastWrite {
                    uint64 time;
                    uint64 blockNum;
                }
                event WizardMinted(uint256 indexed tokenId);
                event DragonMinted(uint256 indexed tokenId);
                event WizardStolen(uint256 indexed tokenId);
                event DragonStolen(uint256 indexed tokenId);
                event WizardBurned(uint256 indexed tokenId);
                event DragonBurned(uint256 indexed tokenId);
                // max number of tokens that can be minted: 60000 in production
                uint256 public maxTokens;
                // number of tokens that can be claimed for a fee: 15,000
                uint256 public PAID_TOKENS;
                // number of tokens have been minted so far
                uint16 public override minted;
                // mapping from tokenId to a struct containing the token's traits
                mapping(uint256 => WizardDragon) private tokenTraits;
                // mapping from hashed(tokenTrait) to the tokenId it's associated with
                // used to ensure there are no duplicates
                mapping(uint256 => uint256) public existingCombinations;
                // Tracks the last block and timestamp that a caller has written to state.
                // Disallow some access to functions if they occur while a change is being written.
                mapping(address => LastWrite) private lastWriteAddress;
                mapping(uint256 => LastWrite) private lastWriteToken;
                // list of probabilities for each trait type
                // 0 - 9 are associated with Wizard, 10 - 18 are associated with Dragons
                uint8[][18] public rarities;
                // list of aliases for Walker's Alias algorithm
                // 0 - 9 are associated with Wizard, 10 - 18 are associated with Dragons
                uint8[][18] public aliases;
                // reference to the Tower contract to allow transfers to it without approval
                ITower public tower;
                // reference to Traits
                ITraits public traits;
                // reference to Randomizer
                IRandomizer public randomizer;
                // address => allowedToCallFunctions
                mapping(address => bool) private admins;
                constructor(uint256 _maxTokens) ERC721("Wizards & Dragons Game", "WnD") {
                    maxTokens = _maxTokens;
                    PAID_TOKENS = _maxTokens / 4;
                    _pause();
                    // A.J. Walker's Alias Algorithm
                    // Wizards
                    // body
                    rarities[0] = [80, 150, 200, 250, 255];
                    aliases[0] = [4, 4, 4, 4, 4];
                    // head
                    rarities[1] = [150, 40, 240, 90, 115, 135, 40, 199, 100];
                    aliases[1] = [3, 7, 4, 0, 5, 6, 8, 5, 0];
                    // spell
                    rarities[2] =  [255, 135, 60, 130, 190, 156, 250, 120, 60, 25, 190];
                    aliases[2] = [0, 0, 0, 6, 6, 0, 0, 0, 6, 8, 0];
                    // eyes
                    rarities[3] = [221, 100, 181, 140, 224, 147, 84, 228, 140, 224, 250, 160, 241, 207, 173, 84, 254];
                    aliases[3] = [1, 2, 5, 0, 1, 7, 1, 10, 5, 10, 11, 12, 13, 14, 16, 11, 0];
                    // neck
                    rarities[4] = [175, 100, 40, 250, 115, 100, 80, 110, 180, 255, 210, 180];
                    aliases[4] = [3, 0, 4, 1, 11, 7, 8, 10, 9, 9, 8, 8];
                    // mouth
                    rarities[5] = [80, 225, 220, 35, 100, 240, 70, 160, 175, 217, 175, 60];
                    aliases[5] = [1, 2, 5, 8, 2, 8, 8, 9, 9, 10, 7, 10];
                    // neck
                    rarities[6] = [255];
                    aliases[6] = [0];
                    // wand
                    rarities[7] = [243, 189, 50, 30, 55, 180, 80, 90, 155, 30, 222, 255];
                    aliases[7] = [1, 7, 5, 2, 11, 11, 0, 10, 0, 0, 11, 3];
                    // rankIndex
                    rarities[8] = [255];
                    aliases[8] = [0];
                    // Dragons
                    // body
                    rarities[9] = [100, 80, 177, 199, 255, 40, 211, 177, 25, 230, 90, 130, 199, 230];
                    aliases[9] = [4, 3, 3, 4, 4, 13, 9, 1, 2, 5, 13, 0, 6, 12];
                    // head
                    rarities[10] = [255];
                    aliases[10] = [0];
                    // spell
                    rarities[11] = [255];
                    aliases[11] = [0];
                    // eyes
                    rarities[12] = [90, 40, 219, 80, 183, 225, 40, 120, 60, 220];
                    aliases[12] = [1, 8, 3, 2, 5, 6, 5, 9, 4, 3];
                    // nose
                    rarities[13] = [255];
                    aliases[13] = [0];
                    // mouth
                    rarities[14] = [239, 244, 255, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234];
                    aliases[14] = [1, 2, 2, 0, 2, 2, 9, 0, 5, 4, 4, 4, 4, 4];
                    // tails
                    rarities[15] = [80, 200, 144, 145, 80, 140, 120];
                    aliases[15] = [1, 6, 0, 0, 3, 0, 3];
                    // wand 
                    rarities[16] = [255];
                    aliases[16] = [0];
                    // rankIndex
                    rarities[17] = [14, 155, 80, 255];
                    aliases[17] = [2, 3, 3, 3];
                }
                /** CRITICAL TO SETUP / MODIFIERS */
                modifier requireContractsSet() {
                    require(address(traits) != address(0) && address(tower) != address(0) && address(randomizer) != address(0), "Contracts not set");
                    _;
                }
                modifier blockIfChangingAddress() {
                    // frens can always call whenever they want :)
                    require(admins[_msgSender()] || lastWriteAddress[tx.origin].blockNum < block.number, "hmmmm what doing?");
                    _;
                }
                modifier blockIfChangingToken(uint256 tokenId) {
                    // frens can always call whenever they want :)
                    require(admins[_msgSender()] || lastWriteToken[tokenId].blockNum < block.number, "hmmmm what doing?");
                    _;
                }
                function setContracts(address _traits, address _tower, address _rand) external onlyOwner {
                    traits = ITraits(_traits);
                    tower = ITower(_tower);
                    randomizer = IRandomizer(_rand);
                }
                /** EXTERNAL */
                function getTokenWriteBlock(uint256 tokenId) external view override returns(uint64) {
                    require(admins[_msgSender()], "Only admins can call this");
                    return lastWriteToken[tokenId].blockNum;
                }
                /** 
                * Mint a token - any payment / game logic should be handled in the game contract. 
                * This will just generate random traits and mint a token to a designated address.
                */
                function mint(address recipient, uint256 seed) external override whenNotPaused {
                    require(admins[_msgSender()], "Only admins can call this");
                    require(minted + 1 <= maxTokens, "All tokens minted");
                    minted++;
                    generate(minted, seed, lastWriteAddress[tx.origin]);
                    if(tx.origin != recipient && recipient != address(tower)) {
                        // Stolen!
                        if(tokenTraits[minted].isWizard) {
                            emit WizardStolen(minted);
                        }
                        else {
                            emit DragonStolen(minted);
                        }
                    }
                    _safeMint(recipient, minted);
                }
                /** 
                * Burn a token - any game logic should be handled before this function.
                */
                function burn(uint256 tokenId) external override whenNotPaused {
                    require(admins[_msgSender()], "Only admins can call this");
                    require(ownerOf(tokenId) == tx.origin, "Oops you don't own that");
                    if(tokenTraits[tokenId].isWizard) {
                        emit WizardBurned(tokenId);
                    }
                    else {
                        emit DragonBurned(tokenId);
                    }
                    _burn(tokenId);
                }
                function updateOriginAccess(uint16[] memory tokenIds) external override {
                    require(admins[_msgSender()], "Only admins can call this");
                    uint64 blockNum = uint64(block.number);
                    uint64 time = uint64(block.timestamp);
                    lastWriteAddress[tx.origin] = LastWrite(time, blockNum);
                    for (uint256 i = 0; i < tokenIds.length; i++) {
                        lastWriteToken[tokenIds[i]] = LastWrite(time, blockNum);
                    }
                }
                function transferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) public virtual override(ERC721, IERC721) blockIfChangingToken(tokenId) {
                    // allow admin contracts to be send without approval
                    if(!admins[_msgSender()]) {
                        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
                    }
                    _transfer(from, to, tokenId);
                }
                /** INTERNAL */
                /**
                * generates traits for a specific token, checking to make sure it's unique
                * @param tokenId the id of the token to generate traits for
                * @param seed a pseudorandom 256 bit number to derive traits from
                * @return t - a struct of traits for the given token ID
                */
                function generate(uint256 tokenId, uint256 seed, LastWrite memory lw) internal returns (WizardDragon memory t) {
                    t = selectTraits(seed);
                    if (existingCombinations[structToHash(t)] == 0) {
                        tokenTraits[tokenId] = t;
                        existingCombinations[structToHash(t)] = tokenId;
                        if(t.isWizard) {
                            emit WizardMinted(tokenId);
                        }
                        else {
                            emit DragonMinted(tokenId);
                        }
                        return t;
                    }
                    return generate(tokenId, randomizer.random(), lw);
                }
                /**
                * uses A.J. Walker's Alias algorithm for O(1) rarity table lookup
                * ensuring O(1) instead of O(n) reduces mint cost by more than 50%
                * probability & alias tables are generated off-chain beforehand
                * @param seed portion of the 256 bit seed to remove trait correlation
                * @param traitType the trait type to select a trait for 
                * @return the ID of the randomly selected trait
                */
                function selectTrait(uint16 seed, uint8 traitType) internal view returns (uint8) {
                    uint8 trait = uint8(seed) % uint8(rarities[traitType].length);
                    // If a selected random trait probability is selected (biased coin) return that trait
                    if (seed >> 8 < rarities[traitType][trait]) return trait;
                    return aliases[traitType][trait];
                }
                /**
                * selects the species and all of its traits based on the seed value
                * @param seed a pseudorandom 256 bit number to derive traits from
                * @return t -  a struct of randomly selected traits
                */
                function selectTraits(uint256 seed) internal view returns (WizardDragon memory t) {    
                    t.isWizard = (seed & 0xFFFF) % 10 != 0;
                    uint8 shift = t.isWizard ? 0 : 9;
                    seed >>= 16;
                    t.body = selectTrait(uint16(seed & 0xFFFF), 0 + shift);
                    seed >>= 16;
                    t.head = selectTrait(uint16(seed & 0xFFFF), 1 + shift);
                    seed >>= 16;
                    t.spell = selectTrait(uint16(seed & 0xFFFF), 2 + shift);
                    seed >>= 16;
                    t.eyes = selectTrait(uint16(seed & 0xFFFF), 3 + shift);
                    seed >>= 16;
                    t.neck = selectTrait(uint16(seed & 0xFFFF), 4 + shift);
                    seed >>= 16;
                    t.mouth = selectTrait(uint16(seed & 0xFFFF), 5 + shift);
                    seed >>= 16;
                    t.tail = selectTrait(uint16(seed & 0xFFFF), 6 + shift);
                    seed >>= 16;
                    t.wand = selectTrait(uint16(seed & 0xFFFF), 7 + shift);
                    seed >>= 16;
                    t.rankIndex = selectTrait(uint16(seed & 0xFFFF), 8 + shift);
                }
                /**
                * converts a struct to a 256 bit hash to check for uniqueness
                * @param s the struct to pack into a hash
                * @return the 256 bit hash of the struct
                */
                function structToHash(WizardDragon memory s) internal pure returns (uint256) {
                    return uint256(keccak256(
                        abi.encodePacked(
                            s.isWizard,
                            s.body,
                            s.head,
                            s.spell,
                            s.eyes,
                            s.neck,
                            s.mouth,
                            s.tail,
                            s.wand,
                            s.rankIndex
                        )
                    ));
                }
                /** READ */
                /**
                * checks if a token is a Wizards
                * @param tokenId the ID of the token to check
                * @return wizard - whether or not a token is a Wizards
                */
                function isWizard(uint256 tokenId) external view override blockIfChangingToken(tokenId) returns (bool) {
                    // Sneaky dragons will be slain if they try to peep this after mint. Nice try.
                    IWnD.WizardDragon memory s = tokenTraits[tokenId];
                    return s.isWizard;
                }
                function getMaxTokens() external view override returns (uint256) {
                    return maxTokens;
                }
                function getPaidTokens() external view override returns (uint256) {
                    return PAID_TOKENS;
                }
                /** ADMIN */
                /**
                * updates the number of tokens for sale
                */
                function setPaidTokens(uint256 _paidTokens) external onlyOwner {
                    PAID_TOKENS = uint16(_paidTokens);
                }
                /**
                * enables owner to pause / unpause minting
                */
                function setPaused(bool _paused) external requireContractsSet onlyOwner {
                    if (_paused) _pause();
                    else _unpause();
                }
                /**
                * enables an address to mint / burn
                * @param addr the address to enable
                */
                function addAdmin(address addr) external onlyOwner {
                    admins[addr] = true;
                }
                /**
                * disables an address from minting / burning
                * @param addr the address to disbale
                */
                function removeAdmin(address addr) external onlyOwner {
                    admins[addr] = false;
                }
                /** Traits */
                function getTokenTraits(uint256 tokenId) external view override blockIfChangingAddress blockIfChangingToken(tokenId) returns (WizardDragon memory) {
                    return tokenTraits[tokenId];
                }
                function tokenURI(uint256 tokenId) public view override blockIfChangingAddress blockIfChangingToken(tokenId) returns (string memory) {
                    require(_exists(tokenId), "Token ID does not exist");
                    return traits.tokenURI(tokenId);
                }
                /** OVERRIDES FOR SAFETY */
                function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override(ERC721Enumerable, IERC721Enumerable) blockIfChangingAddress returns (uint256) {
                    // Y U checking on this address in the same block it's being modified... hmmmm
                    require(admins[_msgSender()] || lastWriteAddress[owner].blockNum < block.number, "hmmmm what doing?");
                    uint256 tokenId = super.tokenOfOwnerByIndex(owner, index);
                    require(admins[_msgSender()] || lastWriteToken[tokenId].blockNum < block.number, "hmmmm what doing?");
                    return tokenId;
                }
                
                function balanceOf(address owner) public view virtual override(ERC721, IERC721) blockIfChangingAddress returns (uint256) {
                    // Y U checking on this address in the same block it's being modified... hmmmm
                    require(admins[_msgSender()] || lastWriteAddress[owner].blockNum < block.number, "hmmmm what doing?");
                    return super.balanceOf(owner);
                }
                function ownerOf(uint256 tokenId) public view virtual override(ERC721, IERC721) blockIfChangingAddress blockIfChangingToken(tokenId) returns (address) {
                    address addr = super.ownerOf(tokenId);
                    // Y U checking on this address in the same block it's being modified... hmmmm
                    require(admins[_msgSender()] || lastWriteAddress[addr].blockNum < block.number, "hmmmm what doing?");
                    return addr;
                }
                function tokenByIndex(uint256 index) public view virtual override(ERC721Enumerable, IERC721Enumerable) returns (uint256) {
                    uint256 tokenId = super.tokenByIndex(index);
                    // NICE TRY TOAD DRAGON
                    require(admins[_msgSender()] || lastWriteToken[tokenId].blockNum < block.number, "hmmmm what doing?");
                    return tokenId;
                }
                function approve(address to, uint256 tokenId) public virtual override(ERC721, IERC721) blockIfChangingToken(tokenId) {
                    super.approve(to, tokenId);
                }
                function getApproved(uint256 tokenId) public view virtual override(ERC721, IERC721) blockIfChangingToken(tokenId) returns (address) {
                    return super.getApproved(tokenId);
                }
                function setApprovalForAll(address operator, bool approved) public virtual override(ERC721, IERC721) blockIfChangingAddress {
                    super.setApprovalForAll(operator, approved);
                }
                function isApprovedForAll(address owner, address operator) public view virtual override(ERC721, IERC721) blockIfChangingAddress returns (bool) {
                    return super.isApprovedForAll(owner, operator);
                }
                
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) public virtual override(ERC721, IERC721) blockIfChangingToken(tokenId) {
                    super.safeTransferFrom(from, to, tokenId);
                }
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory _data
                ) public virtual override(ERC721, IERC721) blockIfChangingToken(tokenId) {
                    super.safeTransferFrom(from, to, tokenId, _data);
                }
            }// SPDX-License-Identifier: MIT
            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() {
                    _setOwner(_msgSender());
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    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 {
                    _setOwner(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");
                    _setOwner(newOwner);
                }
                function _setOwner(address newOwner) private {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../utils/Context.sol";
            /**
             * @dev Contract module which allows children to implement an emergency stop
             * mechanism that can be triggered by an authorized account.
             *
             * This module is used through inheritance. It will make available the
             * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
             * the functions of your contract. Note that they will not be pausable by
             * simply including this module, only once the modifiers are put in place.
             */
            abstract contract Pausable is Context {
                /**
                 * @dev Emitted when the pause is triggered by `account`.
                 */
                event Paused(address account);
                /**
                 * @dev Emitted when the pause is lifted by `account`.
                 */
                event Unpaused(address account);
                bool private _paused;
                /**
                 * @dev Initializes the contract in unpaused state.
                 */
                constructor() {
                    _paused = false;
                }
                /**
                 * @dev Returns true if the contract is paused, and false otherwise.
                 */
                function paused() public view virtual returns (bool) {
                    return _paused;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                modifier whenNotPaused() {
                    require(!paused(), "Pausable: paused");
                    _;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                modifier whenPaused() {
                    require(paused(), "Pausable: not paused");
                    _;
                }
                /**
                 * @dev Triggers stopped state.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                function _pause() internal virtual whenNotPaused {
                    _paused = true;
                    emit Paused(_msgSender());
                }
                /**
                 * @dev Returns to normal state.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                function _unpause() internal virtual whenPaused {
                    _paused = false;
                    emit Unpaused(_msgSender());
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../ERC721.sol";
            import "./IERC721Enumerable.sol";
            /**
             * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
             * enumerability of all the token ids in the contract as well as all token ids owned by each
             * account.
             */
            abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
                // Mapping from owner to list of owned token IDs
                mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
                // Mapping from token ID to index of the owner tokens list
                mapping(uint256 => uint256) private _ownedTokensIndex;
                // Array with all token ids, used for enumeration
                uint256[] private _allTokens;
                // Mapping from token id to position in the allTokens array
                mapping(uint256 => uint256) private _allTokensIndex;
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
                    return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
                }
                /**
                 * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
                 */
                function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
                    require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
                    return _ownedTokens[owner][index];
                }
                /**
                 * @dev See {IERC721Enumerable-totalSupply}.
                 */
                function totalSupply() public view virtual override returns (uint256) {
                    return _allTokens.length;
                }
                /**
                 * @dev See {IERC721Enumerable-tokenByIndex}.
                 */
                function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
                    require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
                    return _allTokens[index];
                }
                /**
                 * @dev Hook that is called before any token transfer. This includes minting
                 * and burning.
                 *
                 * Calling conditions:
                 *
                 * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
                 * transferred to `to`.
                 * - When `from` is zero, `tokenId` will be minted for `to`.
                 * - When `to` is zero, ``from``'s `tokenId` will be burned.
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                    address from,
                    address to,
                    uint256 tokenId
                ) internal virtual override {
                    super._beforeTokenTransfer(from, to, tokenId);
                    if (from == address(0)) {
                        _addTokenToAllTokensEnumeration(tokenId);
                    } else if (from != to) {
                        _removeTokenFromOwnerEnumeration(from, tokenId);
                    }
                    if (to == address(0)) {
                        _removeTokenFromAllTokensEnumeration(tokenId);
                    } else if (to != from) {
                        _addTokenToOwnerEnumeration(to, tokenId);
                    }
                }
                /**
                 * @dev Private function to add a token to this extension's ownership-tracking data structures.
                 * @param to address representing the new owner of the given token ID
                 * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
                 */
                function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
                    uint256 length = ERC721.balanceOf(to);
                    _ownedTokens[to][length] = tokenId;
                    _ownedTokensIndex[tokenId] = length;
                }
                /**
                 * @dev Private function to add a token to this extension's token tracking data structures.
                 * @param tokenId uint256 ID of the token to be added to the tokens list
                 */
                function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
                    _allTokensIndex[tokenId] = _allTokens.length;
                    _allTokens.push(tokenId);
                }
                /**
                 * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
                 * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
                 * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
                 * This has O(1) time complexity, but alters the order of the _ownedTokens array.
                 * @param from address representing the previous owner of the given token ID
                 * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
                 */
                function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
                    // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
                    // then delete the last slot (swap and pop).
                    uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
                    uint256 tokenIndex = _ownedTokensIndex[tokenId];
                    // When the token to delete is the last token, the swap operation is unnecessary
                    if (tokenIndex != lastTokenIndex) {
                        uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
                        _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                        _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
                    }
                    // This also deletes the contents at the last position of the array
                    delete _ownedTokensIndex[tokenId];
                    delete _ownedTokens[from][lastTokenIndex];
                }
                /**
                 * @dev Private function to remove a token from this extension's token tracking data structures.
                 * This has O(1) time complexity, but alters the order of the _allTokens array.
                 * @param tokenId uint256 ID of the token to be removed from the tokens list
                 */
                function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
                    // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
                    // then delete the last slot (swap and pop).
                    uint256 lastTokenIndex = _allTokens.length - 1;
                    uint256 tokenIndex = _allTokensIndex[tokenId];
                    // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
                    // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
                    // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
                    uint256 lastTokenId = _allTokens[lastTokenIndex];
                    _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
                    _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
                    // This also deletes the contents at the last position of the array
                    delete _allTokensIndex[tokenId];
                    _allTokens.pop();
                }
            }
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
            interface IWnD is IERC721Enumerable {
                // game data storage
                struct WizardDragon {
                    bool isWizard;
                    uint8 body;
                    uint8 head;
                    uint8 spell;
                    uint8 eyes;
                    uint8 neck;
                    uint8 mouth;
                    uint8 wand;
                    uint8 tail;
                    uint8 rankIndex;
                }
                function minted() external returns (uint16);
                function updateOriginAccess(uint16[] memory tokenIds) external;
                function mint(address recipient, uint256 seed) external;
                function burn(uint256 tokenId) external;
                function getMaxTokens() external view returns (uint256);
                function getPaidTokens() external view returns (uint256);
                function getTokenTraits(uint256 tokenId) external view returns (WizardDragon memory);
                function getTokenWriteBlock(uint256 tokenId) external view returns(uint64);
                function isWizard(uint256 tokenId) external view returns(bool);
              
            }// SPDX-License-Identifier: MIT LICENSE 
            pragma solidity ^0.8.0;
            interface ITower {
              function addManyToTowerAndFlight(address account, uint16[] calldata tokenIds) external;
              function randomDragonOwner(uint256 seed) external view returns (address);
            }// SPDX-License-Identifier: MIT LICENSE 
            pragma solidity ^0.8.0;
            interface ITraits {
              function tokenURI(uint256 tokenId) external view returns (string memory);
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface IGP {
                function mint(address to, uint256 amount) external;
                function burn(address from, uint256 amount) external;
                function updateOriginAccess() external;
                function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            }pragma solidity ^0.8.0;
            interface IRandomizer {
                function random() external returns (uint256);
            }// SPDX-License-Identifier: MIT
            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;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "./IERC721.sol";
            import "./IERC721Receiver.sol";
            import "./extensions/IERC721Metadata.sol";
            import "../../utils/Address.sol";
            import "../../utils/Context.sol";
            import "../../utils/Strings.sol";
            import "../../utils/introspection/ERC165.sol";
            /**
             * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
             * the Metadata extension, but not including the Enumerable extension, which is available separately as
             * {ERC721Enumerable}.
             */
            contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
                using Address for address;
                using Strings for uint256;
                // Token name
                string private _name;
                // Token symbol
                string private _symbol;
                // Mapping from token ID to owner address
                mapping(uint256 => address) private _owners;
                // Mapping owner address to token count
                mapping(address => uint256) private _balances;
                // Mapping from token ID to approved address
                mapping(uint256 => address) private _tokenApprovals;
                // Mapping from owner to operator approvals
                mapping(address => mapping(address => bool)) private _operatorApprovals;
                /**
                 * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
                 */
                constructor(string memory name_, string memory symbol_) {
                    _name = name_;
                    _symbol = symbol_;
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                    return
                        interfaceId == type(IERC721).interfaceId ||
                        interfaceId == type(IERC721Metadata).interfaceId ||
                        super.supportsInterface(interfaceId);
                }
                /**
                 * @dev See {IERC721-balanceOf}.
                 */
                function balanceOf(address owner) public view virtual override returns (uint256) {
                    require(owner != address(0), "ERC721: balance query for the zero address");
                    return _balances[owner];
                }
                /**
                 * @dev See {IERC721-ownerOf}.
                 */
                function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                    address owner = _owners[tokenId];
                    require(owner != address(0), "ERC721: owner query for nonexistent token");
                    return owner;
                }
                /**
                 * @dev See {IERC721Metadata-name}.
                 */
                function name() public view virtual override returns (string memory) {
                    return _name;
                }
                /**
                 * @dev See {IERC721Metadata-symbol}.
                 */
                function symbol() public view virtual override returns (string memory) {
                    return _symbol;
                }
                /**
                 * @dev See {IERC721Metadata-tokenURI}.
                 */
                function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                    require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
                    string memory baseURI = _baseURI();
                    return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                }
                /**
                 * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                 * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                 * by default, can be overriden in child contracts.
                 */
                function _baseURI() internal view virtual returns (string memory) {
                    return "";
                }
                /**
                 * @dev See {IERC721-approve}.
                 */
                function approve(address to, uint256 tokenId) public virtual override {
                    address owner = ERC721.ownerOf(tokenId);
                    require(to != owner, "ERC721: approval to current owner");
                    require(
                        _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                        "ERC721: approve caller is not owner nor approved for all"
                    );
                    _approve(to, tokenId);
                }
                /**
                 * @dev See {IERC721-getApproved}.
                 */
                function getApproved(uint256 tokenId) public view virtual override returns (address) {
                    require(_exists(tokenId), "ERC721: approved query for nonexistent token");
                    return _tokenApprovals[tokenId];
                }
                /**
                 * @dev See {IERC721-setApprovalForAll}.
                 */
                function setApprovalForAll(address operator, bool approved) public virtual override {
                    require(operator != _msgSender(), "ERC721: approve to caller");
                    _operatorApprovals[_msgSender()][operator] = approved;
                    emit ApprovalForAll(_msgSender(), operator, approved);
                }
                /**
                 * @dev See {IERC721-isApprovedForAll}.
                 */
                function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                    return _operatorApprovals[owner][operator];
                }
                /**
                 * @dev See {IERC721-transferFrom}.
                 */
                function transferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) public virtual override {
                    //solhint-disable-next-line max-line-length
                    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
                    _transfer(from, to, tokenId);
                }
                /**
                 * @dev See {IERC721-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId
                ) public virtual override {
                    safeTransferFrom(from, to, tokenId, "");
                }
                /**
                 * @dev See {IERC721-safeTransferFrom}.
                 */
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory _data
                ) public virtual override {
                    require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
                    _safeTransfer(from, to, tokenId, _data);
                }
                /**
                 * @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.
                 *
                 * `_data` is additional data, it has no specified format and it is sent in call to `to`.
                 *
                 * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
                 * implement alternative mechanisms to perform token transfer, such as signature-based.
                 *
                 * Requirements:
                 *
                 * - `from` cannot be the zero address.
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must exist and be owned by `from`.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function _safeTransfer(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory _data
                ) internal virtual {
                    _transfer(from, to, tokenId);
                    require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
                }
                /**
                 * @dev Returns whether `tokenId` exists.
                 *
                 * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                 *
                 * Tokens start existing when they are minted (`_mint`),
                 * and stop existing when they are burned (`_burn`).
                 */
                function _exists(uint256 tokenId) internal view virtual returns (bool) {
                    return _owners[tokenId] != address(0);
                }
                /**
                 * @dev Returns whether `spender` is allowed to manage `tokenId`.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 */
                function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                    require(_exists(tokenId), "ERC721: operator query for nonexistent token");
                    address owner = ERC721.ownerOf(tokenId);
                    return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
                }
                /**
                 * @dev Safely mints `tokenId` and transfers it to `to`.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must not exist.
                 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function _safeMint(address to, uint256 tokenId) internal virtual {
                    _safeMint(to, tokenId, "");
                }
                /**
                 * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                 * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
                 */
                function _safeMint(
                    address to,
                    uint256 tokenId,
                    bytes memory _data
                ) internal virtual {
                    _mint(to, tokenId);
                    require(
                        _checkOnERC721Received(address(0), to, tokenId, _data),
                        "ERC721: transfer to non ERC721Receiver implementer"
                    );
                }
                /**
                 * @dev Mints `tokenId` and transfers it to `to`.
                 *
                 * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
                 *
                 * Requirements:
                 *
                 * - `tokenId` must not exist.
                 * - `to` cannot be the zero address.
                 *
                 * Emits a {Transfer} event.
                 */
                function _mint(address to, uint256 tokenId) internal virtual {
                    require(to != address(0), "ERC721: mint to the zero address");
                    require(!_exists(tokenId), "ERC721: token already minted");
                    _beforeTokenTransfer(address(0), to, tokenId);
                    _balances[to] += 1;
                    _owners[tokenId] = to;
                    emit Transfer(address(0), to, tokenId);
                }
                /**
                 * @dev Destroys `tokenId`.
                 * The approval is cleared when the token is burned.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must exist.
                 *
                 * Emits a {Transfer} event.
                 */
                function _burn(uint256 tokenId) internal virtual {
                    address owner = ERC721.ownerOf(tokenId);
                    _beforeTokenTransfer(owner, address(0), tokenId);
                    // Clear approvals
                    _approve(address(0), tokenId);
                    _balances[owner] -= 1;
                    delete _owners[tokenId];
                    emit Transfer(owner, address(0), tokenId);
                }
                /**
                 * @dev Transfers `tokenId` from `from` to `to`.
                 *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
                 *
                 * Requirements:
                 *
                 * - `to` cannot be the zero address.
                 * - `tokenId` token must be owned by `from`.
                 *
                 * Emits a {Transfer} event.
                 */
                function _transfer(
                    address from,
                    address to,
                    uint256 tokenId
                ) internal virtual {
                    require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
                    require(to != address(0), "ERC721: transfer to the zero address");
                    _beforeTokenTransfer(from, to, tokenId);
                    // Clear approvals from the previous owner
                    _approve(address(0), tokenId);
                    _balances[from] -= 1;
                    _balances[to] += 1;
                    _owners[tokenId] = to;
                    emit Transfer(from, to, tokenId);
                }
                /**
                 * @dev Approve `to` to operate on `tokenId`
                 *
                 * Emits a {Approval} event.
                 */
                function _approve(address to, uint256 tokenId) internal virtual {
                    _tokenApprovals[tokenId] = to;
                    emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
                }
                /**
                 * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
                 * The call is not executed if the target address is not a contract.
                 *
                 * @param from address representing the previous owner of the given token ID
                 * @param to target address that will receive the tokens
                 * @param tokenId uint256 ID of the token to be transferred
                 * @param _data bytes optional data to send along with the call
                 * @return bool whether the call correctly returned the expected magic value
                 */
                function _checkOnERC721Received(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory _data
                ) private returns (bool) {
                    if (to.isContract()) {
                        try IERC721Receiver(to).onERC721Received(_msgSender(), 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;
                    }
                }
                /**
                 * @dev Hook that is called before any token transfer. This includes minting
                 * and burning.
                 *
                 * Calling conditions:
                 *
                 * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
                 * transferred to `to`.
                 * - When `from` is zero, `tokenId` will be minted for `to`.
                 * - When `to` is zero, ``from``'s `tokenId` will be burned.
                 * - `from` and `to` are never both zero.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                    address from,
                    address to,
                    uint256 tokenId
                ) internal virtual {}
            }
            // SPDX-License-Identifier: MIT
            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 tokenId);
                /**
                 * @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
            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
            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
            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);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    assembly {
                        size := extcodesize(account)
                    }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value
                ) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(
                    address target,
                    bytes memory data,
                    uint256 value,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    require(isContract(target), "Address: call to non-contract");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @dev String operations.
             */
            library Strings {
                bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                /**
                 * @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);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "./IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165 is IERC165 {
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165).interfaceId;
                }
            }
            // SPDX-License-Identifier: MIT
            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);
            }
            

            File 3 of 3: Tower
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/access/Ownable.sol";
            import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
            import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
            import "@openzeppelin/contracts/security/Pausable.sol";
            import "./interfaces/IWnDGame.sol";
            import "./interfaces/IWnD.sol";
            import "./interfaces/IGP.sol";
            import "./interfaces/ITower.sol";
            import "./interfaces/ISacrificialAlter.sol";
            import "./interfaces/IRandomizer.sol";
            contract Tower is ITower, Ownable, ReentrancyGuard, IERC721Receiver, Pausable {
              
              // maximum rank for a Wizard/Dragon
              uint8 public constant MAX_RANK = 8;
              // struct to store a stake's token, owner, and earning values
              struct Stake {
                uint16 tokenId;
                uint80 value;
                address owner;
              }
              uint256 private totalRankStaked;
              uint256 private numWizardsStaked;
              event TokenStaked(address indexed owner, uint256 indexed tokenId, bool indexed isWizard, uint256 value);
              event WizardClaimed(uint256 indexed tokenId, bool indexed unstaked, uint256 earned);
              event DragonClaimed(uint256 indexed tokenId, bool indexed unstaked, uint256 earned);
              // reference to the WnD NFT contract
              IWnD public wndNFT;
              // reference to the WnD NFT contract
              IWnDGame public wndGame;
              // reference to the $GP contract for minting $GP earnings
              IGP public gpToken;
              // reference to Randomer 
              IRandomizer public randomizer;
              // maps tokenId to stake
              mapping(uint256 => Stake) private tower; 
              // maps rank to all Dragon staked with that rank
              mapping(uint256 => Stake[]) private flight; 
              // tracks location of each Dragon in Flight
              mapping(uint256 => uint256) private flightIndices; 
              // any rewards distributed when no dragons are staked
              uint256 private unaccountedRewards = 0; 
              // amount of $GP due for each rank point staked
              uint256 private gpPerRank = 0; 
              // wizards earn 12000 $GP per day
              uint256 public constant DAILY_GP_RATE = 12000 ether;
              // wizards must have 2 days worth of $GP to unstake or else they're still guarding the tower
              uint256 public constant MINIMUM_TO_EXIT = 2 days;
              // dragons take a 20% tax on all $GP claimed
              uint256 public constant GP_CLAIM_TAX_PERCENTAGE = 20;
              // there will only ever be (roughly) 2.4 billion $GP earned through staking
              uint256 public constant MAXIMUM_GLOBAL_GP = 2880000000 ether;
              uint256 public treasureChestTypeId;
              // amount of $GP earned so far
              uint256 public totalGPEarned;
              // the last time $GP was claimed
              uint256 private lastClaimTimestamp;
              // emergency rescue to allow unstaking without any checks but without $GP
              bool public rescueEnabled = false;
              /**
               */
              constructor() {
                _pause();
              }
              /** CRITICAL TO SETUP */
              modifier requireContractsSet() {
                  require(address(wndNFT) != address(0) && address(gpToken) != address(0) 
                    && address(wndGame) != address(0) && address(randomizer) != address(0), "Contracts not set");
                  _;
              }
              function setContracts(address _wndNFT, address _gp, address _wndGame, address _rand) external onlyOwner {
                wndNFT = IWnD(_wndNFT);
                gpToken = IGP(_gp);
                wndGame = IWnDGame(_wndGame);
                randomizer = IRandomizer(_rand);
              }
              function setTreasureChestId(uint256 typeId) external onlyOwner {
                treasureChestTypeId = typeId;
              }
              /** STAKING */
              /**
               * adds Wizards and Dragons to the Tower and Flight
               * @param account the address of the staker
               * @param tokenIds the IDs of the Wizards and Dragons to stake
               */
              function addManyToTowerAndFlight(address account, uint16[] calldata tokenIds) external override nonReentrant {
                require(tx.origin == _msgSender() || _msgSender() == address(wndGame), "Only EOA");
                require(account == tx.origin, "account to sender mismatch");
                for (uint i = 0; i < tokenIds.length; i++) {
                  if (_msgSender() != address(wndGame)) { // dont do this step if its a mint + stake
                    require(wndNFT.ownerOf(tokenIds[i]) == _msgSender(), "You don't own this token");
                    wndNFT.transferFrom(_msgSender(), address(this), tokenIds[i]);
                  } else if (tokenIds[i] == 0) {
                    continue; // there may be gaps in the array for stolen tokens
                  }
                  if (wndNFT.isWizard(tokenIds[i])) 
                    _addWizardToTower(account, tokenIds[i]);
                  else 
                    _addDragonToFlight(account, tokenIds[i]);
                }
              }
              /**
               * adds a single Wizard to the Tower
               * @param account the address of the staker
               * @param tokenId the ID of the Wizard to add to the Tower
               */
              function _addWizardToTower(address account, uint256 tokenId) internal whenNotPaused _updateEarnings {
                tower[tokenId] = Stake({
                  owner: account,
                  tokenId: uint16(tokenId),
                  value: uint80(block.timestamp)
                });
                numWizardsStaked += 1;
                emit TokenStaked(account, tokenId, true, block.timestamp);
              }
              /**
               * adds a single Dragon to the Flight
               * @param account the address of the staker
               * @param tokenId the ID of the Dragon to add to the Flight
               */
              function _addDragonToFlight(address account, uint256 tokenId) internal {
                uint8 rank = _rankForDragon(tokenId);
                totalRankStaked += rank; // Portion of earnings ranges from 8 to 5
                flightIndices[tokenId] = flight[rank].length; // Store the location of the dragon in the Flight
                flight[rank].push(Stake({
                  owner: account,
                  tokenId: uint16(tokenId),
                  value: uint80(gpPerRank)
                })); // Add the dragon to the Flight
                emit TokenStaked(account, tokenId, false, gpPerRank);
              }
              /** CLAIMING / UNSTAKING */
              /**
               * realize $GP earnings and optionally unstake tokens from the Tower / Flight
               * to unstake a Wizard it will require it has 2 days worth of $GP unclaimed
               * @param tokenIds the IDs of the tokens to claim earnings from
               * @param unstake whether or not to unstake ALL of the tokens listed in tokenIds
               */
              function claimManyFromTowerAndFlight(uint16[] calldata tokenIds, bool unstake) external whenNotPaused _updateEarnings nonReentrant {
                require(tx.origin == _msgSender() || _msgSender() == address(wndGame), "Only EOA");
                uint256 owed = 0;
                for (uint i = 0; i < tokenIds.length; i++) {
                  if (wndNFT.isWizard(tokenIds[i])) {
                    owed += _claimWizardFromTower(tokenIds[i], unstake);
                  }
                  else {
                    owed += _claimDragonFromFlight(tokenIds[i], unstake);
                  }
                }
                gpToken.updateOriginAccess();
                if (owed == 0) {
                  return;
                }
                gpToken.mint(_msgSender(), owed);
              }
              function calculateRewards(uint256 tokenId) external view returns (uint256 owed) {
                uint64 lastTokenWrite = wndNFT.getTokenWriteBlock(tokenId);
                // Must check this, as getTokenTraits will be allowed since this contract is an admin
                require(lastTokenWrite < block.number, "hmmmm what doing?");
                Stake memory stake = tower[tokenId];
                if(wndNFT.isWizard(tokenId)) {
                  if (totalGPEarned < MAXIMUM_GLOBAL_GP) {
                    owed = (block.timestamp - stake.value) * DAILY_GP_RATE / 1 days;
                  } else if (stake.value > lastClaimTimestamp) {
                    owed = 0; // $GP production stopped already
                  } else {
                    owed = (lastClaimTimestamp - stake.value) * DAILY_GP_RATE / 1 days; // stop earning additional $GP if it's all been earned
                  }
                }
                else {
                  uint8 rank = _rankForDragon(tokenId);
                  owed = (rank) * (gpPerRank - stake.value); // Calculate portion of tokens based on Rank
                }
              }
              /**
               * realize $GP earnings for a single Wizard and optionally unstake it
               * if not unstaking, pay a 20% tax to the staked Dragons
               * if unstaking, there is a 50% chance all $GP is stolen
               * @param tokenId the ID of the Wizards to claim earnings from
               * @param unstake whether or not to unstake the Wizards
               * @return owed - the amount of $GP earned
               */
              function _claimWizardFromTower(uint256 tokenId, bool unstake) internal returns (uint256 owed) {
                Stake memory stake = tower[tokenId];
                require(stake.owner == _msgSender(), "Don't own the given token");
                require(!(unstake && block.timestamp - stake.value < MINIMUM_TO_EXIT), "Still guarding the tower");
                if (totalGPEarned < MAXIMUM_GLOBAL_GP) {
                  owed = (block.timestamp - stake.value) * DAILY_GP_RATE / 1 days;
                } else if (stake.value > lastClaimTimestamp) {
                  owed = 0; // $GP production stopped already
                } else {
                  owed = (lastClaimTimestamp - stake.value) * DAILY_GP_RATE / 1 days; // stop earning additional $GP if it's all been earned
                }
                if (unstake) {
                  if (randomizer.random() & 1 == 1) { // 50% chance of all $GP stolen
                    _payDragonTax(owed);
                    owed = 0;
                  }
                  delete tower[tokenId];
                  numWizardsStaked -= 1;
                  // Always transfer last to guard against reentrance
                  wndNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // send back Wizard
                } else {
                  _payDragonTax(owed * GP_CLAIM_TAX_PERCENTAGE / 100); // percentage tax to staked dragons
                  owed = owed * (100 - GP_CLAIM_TAX_PERCENTAGE) / 100; // remainder goes to Wizard owner
                  tower[tokenId] = Stake({
                    owner: _msgSender(),
                    tokenId: uint16(tokenId),
                    value: uint80(block.timestamp)
                  }); // reset stake
                }
                emit WizardClaimed(tokenId, unstake, owed);
              }
              /**
               * realize $GP earnings for a single Dragon and optionally unstake it
               * Dragons earn $GP proportional to their rank
               * @param tokenId the ID of the Dragon to claim earnings from
               * @param unstake whether or not to unstake the Dragon
               * @return owed - the amount of $GP earned
               */
              function _claimDragonFromFlight(uint256 tokenId, bool unstake) internal returns (uint256 owed) {
                require(wndNFT.ownerOf(tokenId) == address(this), "Doesn't own token");
                uint8 rank = _rankForDragon(tokenId);
                Stake memory stake = flight[rank][flightIndices[tokenId]];
                require(stake.owner == _msgSender(), "Doesn't own token");
                owed = (rank) * (gpPerRank - stake.value); // Calculate portion of tokens based on Rank
                if (unstake) {
                  totalRankStaked -= rank; // Remove rank from total staked
                  Stake memory lastStake = flight[rank][flight[rank].length - 1];
                  flight[rank][flightIndices[tokenId]] = lastStake; // Shuffle last Dragon to current position
                  flightIndices[lastStake.tokenId] = flightIndices[tokenId];
                  flight[rank].pop(); // Remove duplicate
                  delete flightIndices[tokenId]; // Delete old mapping
                  // Always remove last to guard against reentrance
                  wndNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // Send back Dragon
                } else {
                  flight[rank][flightIndices[tokenId]] = Stake({
                    owner: _msgSender(),
                    tokenId: uint16(tokenId),
                    value: uint80(gpPerRank)
                  }); // reset stake
                }
                emit DragonClaimed(tokenId, unstake, owed);
              }
              /**
               * emergency unstake tokens
               * @param tokenIds the IDs of the tokens to claim earnings from
               */
              function rescue(uint256[] calldata tokenIds) external nonReentrant {
                require(rescueEnabled, "RESCUE DISABLED");
                uint256 tokenId;
                Stake memory stake;
                Stake memory lastStake;
                uint8 rank;
                for (uint i = 0; i < tokenIds.length; i++) {
                  tokenId = tokenIds[i];
                  if (wndNFT.isWizard(tokenId)) {
                    stake = tower[tokenId];
                    require(stake.owner == _msgSender(), "SWIPER, NO SWIPING");
                    delete tower[tokenId];
                    numWizardsStaked -= 1;
                    wndNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // send back Wizards
                    emit WizardClaimed(tokenId, true, 0);
                  } else {
                    rank = _rankForDragon(tokenId);
                    stake = flight[rank][flightIndices[tokenId]];
                    require(stake.owner == _msgSender(), "SWIPER, NO SWIPING");
                    totalRankStaked -= rank; // Remove Rank from total staked
                    lastStake = flight[rank][flight[rank].length - 1];
                    flight[rank][flightIndices[tokenId]] = lastStake; // Shuffle last Dragon to current position
                    flightIndices[lastStake.tokenId] = flightIndices[tokenId];
                    flight[rank].pop(); // Remove duplicate
                    delete flightIndices[tokenId]; // Delete old mapping
                    wndNFT.safeTransferFrom(address(this), _msgSender(), tokenId, ""); // Send back Dragon
                    emit DragonClaimed(tokenId, true, 0);
                  }
                }
              }
              /** ACCOUNTING */
              /** 
               * add $GP to claimable pot for the Flight
               * @param amount $GP to add to the pot
               */
              function _payDragonTax(uint256 amount) internal {
                if (totalRankStaked == 0) { // if there's no staked dragons
                  unaccountedRewards += amount; // keep track of $GP due to dragons
                  return;
                }
                // makes sure to include any unaccounted $GP 
                gpPerRank += (amount + unaccountedRewards) / totalRankStaked;
                unaccountedRewards = 0;
              }
              /**
               * tracks $GP earnings to ensure it stops once 2.4 billion is eclipsed
               */
              modifier _updateEarnings() {
                if (totalGPEarned < MAXIMUM_GLOBAL_GP) {
                  totalGPEarned += 
                    (block.timestamp - lastClaimTimestamp)
                    * numWizardsStaked
                    * DAILY_GP_RATE / 1 days; 
                  lastClaimTimestamp = block.timestamp;
                }
                _;
              }
              /** ADMIN */
              /**
               * allows owner to enable "rescue mode"
               * simplifies accounting, prioritizes tokens out in emergency
               */
              function setRescueEnabled(bool _enabled) external onlyOwner {
                rescueEnabled = _enabled;
              }
              /**
               * enables owner to pause / unpause contract
               */
              function setPaused(bool _paused) external requireContractsSet onlyOwner {
                if (_paused) _pause();
                else _unpause();
              }
              /** READ ONLY */
              /**
               * gets the rank score for a Dragon
               * @param tokenId the ID of the Dragon to get the rank score for
               * @return the rank score of the Dragon (5-8)
               */
              function _rankForDragon(uint256 tokenId) internal view returns (uint8) {
                IWnD.WizardDragon memory s = wndNFT.getTokenTraits(tokenId);
                return MAX_RANK - s.rankIndex; // rank index is 0-3
              }
              /**
               * chooses a random Dragon thief when a newly minted token is stolen
               * @param seed a random value to choose a Dragon from
               * @return the owner of the randomly selected Dragon thief
               */
              function randomDragonOwner(uint256 seed) external view override returns (address) {
                if (totalRankStaked == 0) {
                  return address(0x0);
                }
                uint256 bucket = (seed & 0xFFFFFFFF) % totalRankStaked; // choose a value from 0 to total rank staked
                uint256 cumulative;
                seed >>= 32;
                // loop through each bucket of Dragons with the same rank score
                for (uint i = MAX_RANK - 3; i <= MAX_RANK; i++) {
                  cumulative += flight[i].length * i;
                  // if the value is not inside of that bucket, keep going
                  if (bucket >= cumulative) continue;
                  // get the address of a random Dragon with that rank score
                  return flight[i][seed % flight[i].length].owner;
                }
                return address(0x0);
              }
              function onERC721Received(
                    address,
                    address from,
                    uint256,
                    bytes calldata
                ) external pure override returns (bytes4) {
                  require(from == address(0x0), "Cannot send to Tower directly");
                  return IERC721Receiver.onERC721Received.selector;
                }
              
            }// SPDX-License-Identifier: MIT
            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() {
                    _setOwner(_msgSender());
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    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 {
                    _setOwner(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");
                    _setOwner(newOwner);
                }
                function _setOwner(address newOwner) private {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
            }
            // SPDX-License-Identifier: MIT
            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
            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 nonReentrant will be lower in
                // amount. Since refunds are capped to a percentage of the total
                // transaction's gas, it is best to keep them low in cases like this one, to
                // increase the likelihood of the full refund coming into effect.
                uint256 private constant _NOT_ENTERED = 1;
                uint256 private constant _ENTERED = 2;
                uint256 private _status;
                constructor() {
                    _status = _NOT_ENTERED;
                }
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                    // Any calls to nonReentrant after this point will fail
                    _status = _ENTERED;
                    _;
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _status = _NOT_ENTERED;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../utils/Context.sol";
            /**
             * @dev Contract module which allows children to implement an emergency stop
             * mechanism that can be triggered by an authorized account.
             *
             * This module is used through inheritance. It will make available the
             * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
             * the functions of your contract. Note that they will not be pausable by
             * simply including this module, only once the modifiers are put in place.
             */
            abstract contract Pausable is Context {
                /**
                 * @dev Emitted when the pause is triggered by `account`.
                 */
                event Paused(address account);
                /**
                 * @dev Emitted when the pause is lifted by `account`.
                 */
                event Unpaused(address account);
                bool private _paused;
                /**
                 * @dev Initializes the contract in unpaused state.
                 */
                constructor() {
                    _paused = false;
                }
                /**
                 * @dev Returns true if the contract is paused, and false otherwise.
                 */
                function paused() public view virtual returns (bool) {
                    return _paused;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                modifier whenNotPaused() {
                    require(!paused(), "Pausable: paused");
                    _;
                }
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                modifier whenPaused() {
                    require(paused(), "Pausable: not paused");
                    _;
                }
                /**
                 * @dev Triggers stopped state.
                 *
                 * Requirements:
                 *
                 * - The contract must not be paused.
                 */
                function _pause() internal virtual whenNotPaused {
                    _paused = true;
                    emit Paused(_msgSender());
                }
                /**
                 * @dev Returns to normal state.
                 *
                 * Requirements:
                 *
                 * - The contract must be paused.
                 */
                function _unpause() internal virtual whenPaused {
                    _paused = false;
                    emit Unpaused(_msgSender());
                }
            }
            // SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface IWnDGame {
              
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
            interface IWnD is IERC721Enumerable {
                // game data storage
                struct WizardDragon {
                    bool isWizard;
                    uint8 body;
                    uint8 head;
                    uint8 spell;
                    uint8 eyes;
                    uint8 neck;
                    uint8 mouth;
                    uint8 wand;
                    uint8 tail;
                    uint8 rankIndex;
                }
                function minted() external returns (uint16);
                function updateOriginAccess(uint16[] memory tokenIds) external;
                function mint(address recipient, uint256 seed) external;
                function burn(uint256 tokenId) external;
                function getMaxTokens() external view returns (uint256);
                function getPaidTokens() external view returns (uint256);
                function getTokenTraits(uint256 tokenId) external view returns (WizardDragon memory);
                function getTokenWriteBlock(uint256 tokenId) external view returns(uint64);
                function isWizard(uint256 tokenId) external view returns(bool);
              
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface IGP {
                function mint(address to, uint256 amount) external;
                function burn(address from, uint256 amount) external;
                function updateOriginAccess() external;
                function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            }// SPDX-License-Identifier: MIT LICENSE 
            pragma solidity ^0.8.0;
            interface ITower {
              function addManyToTowerAndFlight(address account, uint16[] calldata tokenIds) external;
              function randomDragonOwner(uint256 seed) external view returns (address);
            }// SPDX-License-Identifier: MIT LICENSE
            pragma solidity ^0.8.0;
            interface ISacrificialAlter {
                function mint(uint256 typeId, uint16 qty, address recipient) external;
                function burn(uint256 typeId, uint16 qty, address burnFrom) external;
                function updateOriginAccess() external;
                function balanceOf(address account, uint256 id) external returns (uint256);
                function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) external;
            }pragma solidity ^0.8.0;
            interface IRandomizer {
                function random() external returns (uint256);
            }// SPDX-License-Identifier: MIT
            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;
                }
            }
            // SPDX-License-Identifier: MIT
            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 tokenId);
                /**
                 * @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
            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
            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);
            }