ETH Price: $2,496.08 (+0.31%)

Transaction Decoder

Block:
15766802 at Oct-17-2022 09:01:11 AM +UTC
Transaction Fee:
0.001651150328253206 ETH $4.12
Gas Used:
149,642 Gas / 11.034003343 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x34fc6cC5...9BEFd4acf
0.00510600024299392 Eth
Nonce: 4
0.003454849914740714 Eth
Nonce: 5
0.001651150328253206
0x3a631E4A...39a94a146
0.117101656328189174 Eth0.117326119328189174 Eth0.000224463

Execution Trace

FairXYZDeployer.mint( signature=0xFA886D94A37163E241CDD43DEB7E59DDBB7C7A1891D3613E00023E8243C9544222A3F995766D25A7B5B3F47ACB37FDC69A56BBB7E0BBB2108A4D18EF2A6DA7921C, nonce=15766800, numberOfTokens=3, phaseLimit=5555 ) => ( 4230 )
  • FairXYZDeployer.mint( signature=0xFA886D94A37163E241CDD43DEB7E59DDBB7C7A1891D3613E00023E8243C9544222A3F995766D25A7B5B3F47ACB37FDC69A56BBB7E0BBB2108A4D18EF2A6DA7921C, nonce=15766800, numberOfTokens=3, phaseLimit=5555 ) => ( 4230 )
    • FairXYZWallets.STATICCALL( )
    • Null: 0x000...001.d0619a21( )
      File 1 of 3: FairXYZDeployer
      // SPDX-License-Identifier: MIT
      // @ Fair.xyz dev
      pragma solidity 0.8.7;
      import "ERC721xyz.sol";
      import "Pausable.sol";
      import "ECDSA.sol";
      contract FairXYZDeployer is ERC721xyz, Pausable{
          
          string private _name;
          string private _symbol;
          using ECDSA for bytes32;
          
          uint256 public maxTokens;
          
          uint256 internal nftPrice;
          string private baseURI;
          string private pathURI;
          string public preRevealURI;
          string private _overrideURI;
          bool public lockURI;
          bool public mintReleased;
          address public interfaceAddress;
          bool public isBase;
          address public owner;
          bool public burnable;
          uint256 public maxMintsPerWallet;
          mapping(bytes32 => bool) private usedHashes;
          mapping(address => uint256) public mintsPerWallet;
          event OwnershipTransferred(address indexed prevOwner, address indexed newOwner);
          event NewPriceSet(uint256 newSetPrice);
          event NewMaxMintsPerWalletSet(uint256 newMaxMints);
          event NewTokenRoyaltySet(uint256 newRoyalty);
          event NewTokenURI(string newTokenURI);
          event NewPathURI(string newPathURI);
          modifier onlyOwner() {
              require(msg.sender == owner, "ERROR");
              _;
          }
          constructor() payable ERC721xyz(_name, _symbol){
              isBase = true;
              _name = "FairXYZ";
              _symbol = "FairXYZ";
              _pause();
          }
       
          // Collection Name
          function name() override public view returns (string memory) {
              return _name;
          }
          // Collection ticker
          function symbol() override public view returns (string memory) {
              return _symbol;
          }
          // View all variables
          function viewAllVariables() public view returns(uint256, uint256, string memory){
              return(nftPrice, maxMintsPerWallet, pathURI);
          }
          // Signer address for minting
          function viewSigner() public view returns(address){
              address returnSigner = IFairXYZWallets(interfaceAddress).viewSigner(); 
              return(returnSigner);
          }
          // Fair.xyz wallet address
          function viewWithdraw() public view returns(address){
              address returnWithdraw = IFairXYZWallets(interfaceAddress).viewWithdraw(); 
              return(returnWithdraw);
          }
          // Initialize Creator contract
          function initialize(uint256 maxTokens_, uint256 nftPrice_, string memory name_, string memory symbol_,
                              bool burnable_, uint256 maxMintsPerWallet_, address interfaceAddress_,
                              string[] memory URIs_, uint96 royaltyPercentage_) external {
              
              require( !isBase , "This contract is not a base contract!");
              require( interfaceAddress_ != address(0), "Cannot set to 0 address!");
              owner = tx.origin;
              maxTokens = maxTokens_;
              nftPrice = nftPrice_;
              _name = name_;
              _symbol = symbol_;
              burnable = burnable_; 
              maxMintsPerWallet = maxMintsPerWallet_;
              interfaceAddress = interfaceAddress_;
              preRevealURI = URIs_[0];
              baseURI = URIs_[1];
              pathURI = URIs_[2];
              isBase = true;
              _setDefaultRoyalty(tx.origin, royaltyPercentage_);
          }
          // Limit on NFT sale
          modifier saleIsOpen{
              require(_mintedTokens < maxTokens, "Sale end");
              _;
          }
          // Lock metadata forever
          function lockURIforever() external onlyOwner {
              lockURI = true;
          }
          
          // View price
          function price() external view returns (uint256) {
              return nftPrice; 
          }
          function hashVariableChanges(address sender, string memory newURI, string memory newPathURI, 
              uint256 newPrice, uint256 newMaxMintsPerWallet, uint256 newRoyaltyPercentage) private pure returns(bytes32) 
          {
                bytes32 hash = keccak256(abi.encodePacked(
                  "\\x19Ethereum Signed Message:\
      32",
                  keccak256(abi.encodePacked(sender, newURI, newPathURI, newPrice, newMaxMintsPerWallet, newRoyaltyPercentage)))
                );    
                return hash;
          }
           
          // change variables in contract
          function overrideVariables(bytes memory signature, string memory newURI, string memory newPathURI, 
              uint256 newPrice, uint256 newMaxMintsPerWallet, uint96 newRoyaltyPercentage)
              onlyOwner
              external
          {
              bytes32 messageHash = hashVariableChanges(msg.sender, newURI, newPathURI, 
                                                        newPrice, newMaxMintsPerWallet, newRoyaltyPercentage);
              address signAdd = viewSigner();
              require(messageHash.recover(signature) == signAdd, "Unrecognizable Hash");
              if(!lockURI)
              {
                  if (bytes(newPathURI).length != 0)       
                      pathURI = newPathURI;
                      emit NewPathURI(pathURI);
                  if(bytes(newURI).length != 0)
                  {
                      _overrideURI = newURI;
                      baseURI = "";
                      emit NewTokenURI(_overrideURI);
                  }
              }
              if(newPrice!=nftPrice)
              {
                  nftPrice = newPrice;
                  emit NewPriceSet(nftPrice);
              }
              if(newMaxMintsPerWallet!=maxMintsPerWallet)
              {
                  maxMintsPerWallet = newMaxMintsPerWallet;
                  emit NewMaxMintsPerWalletSet(maxMintsPerWallet);
              }
              _setDefaultRoyalty(owner, newRoyaltyPercentage);
              emit NewTokenRoyaltySet(newRoyaltyPercentage);
           
          }
          
          // return Base URI
          function _baseURI() public view override returns (string memory) {
              return baseURI;
          }
          // return Path URI
          function _pathURI() public view override returns (string memory) {
              if(bytes(_overrideURI).length == 0)
              {
                  return IFairXYZWallets(interfaceAddress).viewPathURI(pathURI);
              }
              else
              {
                  return _overrideURI;
              }
          }
          // return pre-Reveal URI
          function _preRevealURI() public view override returns (string memory) {
              return preRevealURI;
          }
          // See remaining mints
          function remainingMints(address minterAddress) public view returns(uint256) {
              
              if (maxMintsPerWallet == 0 ) {
                  revert("Collection with no mint limit");
              }
                  
              uint256 mintsLeft = maxMintsPerWallet - mintsPerWallet[minterAddress];
              return mintsLeft; 
          }
          
          // pause minting 
          function pause() external onlyOwner {
              _pause();
          }
          
          // unpause minting 
          function unpause() external onlyOwner {
              _unpause();
          }
          // Burn token
          function burn(uint256 tokenId) external returns(uint256)
          {
              require(burnable, "This contract does not allow burning");
              require(msg.sender == ownerOf(tokenId), "Burner is not the owner of token");
              _burn(tokenId);
              return tokenId;
          }
          // Airdrop a token
          function airdrop(address[] memory address_, uint256 tokenCount) onlyOwner external returns(uint256) 
          {
              require(_mintedTokens + address_.length * tokenCount <= maxTokens, "This exceeds the maximum number of NFTs on sale!");
              for(uint256 i = 0; i < address_.length; ) {
                  _safeMint(address_[i], tokenCount);
                  ++i;
              }
              return _mintedTokens;
          }
          // override isApprovedForAll to allow for pre-approved operators (OpenSea and LooksRare)
          function isApprovedForAll(address _owner, address operator) public view override(ERC721xyz) returns(bool)
          {
              return( IFairXYZWallets(interfaceAddress).viewPreapproved(operator) || super.isApprovedForAll(_owner, operator) );
          }
          function hashTransaction(address sender, uint256 qty, uint256 nonce, uint256 phaseLimit, address address_) private pure returns(bytes32) 
          {
                bytes32 hash = keccak256(abi.encodePacked(
                  "\\x19Ethereum Signed Message:\
      32",
                  keccak256(abi.encodePacked(sender, qty, nonce, phaseLimit, address_)))
                );    
                return hash;
          }
          // Mint a token    
          function mint(bytes memory signature, uint256 nonce, uint256 numberOfTokens, uint256 phaseLimit)
              payable
              external
              whenNotPaused
              saleIsOpen
              returns (uint256)
          {
              bytes32 messageHash = hashTransaction(msg.sender, numberOfTokens, nonce, phaseLimit, address(this));
              address signAdd = viewSigner();
              require(_mintedTokens < phaseLimit, "End of phase limit!");
              require(phaseLimit <= maxTokens, "Phase limit cannot be larger than max tokens");
              require(messageHash.recover(signature) == signAdd, "Unrecognizable Hash");
              require(!usedHashes[messageHash], "Reused Hash");
              require(msg.value  >= nftPrice * numberOfTokens, "You have not sent the required amount of ETH");
              require(numberOfTokens <= 20, "Token minting limit per transaction exceeded");
              require(block.number <= nonce  + 20, "Time limit has passed");
              require(msg.sender == tx.origin, "Cannot mint from contract");
              usedHashes[messageHash] = true;
              uint256 origMintCount = numberOfTokens;
              // If trying to mint more tokens than available -> reimburse for excess mints and allow for lower mint count
              // to avoid a failed tx
              if(maxMintsPerWallet > 0)
              {
                  require(mintsPerWallet[msg.sender] < maxMintsPerWallet, "Exceeds number of mints per wallet");
                  
                  if(mintsPerWallet[msg.sender] + numberOfTokens > maxMintsPerWallet)
                  {
                      numberOfTokens = maxMintsPerWallet - mintsPerWallet[msg.sender];
                  }            
              }
       
              if( (_mintedTokens + numberOfTokens > phaseLimit) )
              {
                  numberOfTokens = phaseLimit - _mintedTokens;
              }
              uint256 reimbursement = origMintCount - numberOfTokens; 
              uint256 reimbursementPrice = reimbursement * nftPrice;
              mintsPerWallet[msg.sender] += numberOfTokens;
              _mint(msg.sender, numberOfTokens);
              
              // cap reimbursement at msg.value in case something goes wrong
              if( 0 < reimbursementPrice && reimbursementPrice < msg.value)
              {
                  (bool sent, ) = msg.sender.call{value: reimbursementPrice}("");
                  require(sent, "Failed to send Ether");
              }
              
              return _mintedTokens;
          }
          // Release the mint, so no signature is required
          function releaseMint() onlyOwner external
          {
              require(!mintReleased);
              mintReleased = true;
          }
          // Mint a token without a signature, requires mintRelease    
          function mintNoSignature(uint256 numberOfTokens)
              payable
              external
              whenNotPaused
              saleIsOpen
              returns (uint256)
          {
              require(mintReleased, "Please use the mint function to buy your token");
              require(msg.value  >= nftPrice * numberOfTokens, "You have not sent the required amount of ETH");
              require(numberOfTokens <= 20, "Token minting limit per transaction exceeded");
              require(msg.sender == tx.origin, "Cannot mint from contract");
              uint256 origMintCount = numberOfTokens;
              // If trying to mint more tokens than available -> reimburse for excess mints and allow for lower mint count
              // to avoid a failed tx
              if(maxMintsPerWallet > 0)
              {
                  require(mintsPerWallet[msg.sender] < maxMintsPerWallet, "Exceeds number of mints per wallet");
                  
                  if(mintsPerWallet[msg.sender] + numberOfTokens > maxMintsPerWallet)
                  {
                      numberOfTokens = maxMintsPerWallet - mintsPerWallet[msg.sender];
                  }            
              }
       
              if( (_mintedTokens + numberOfTokens > maxTokens) )
              {
                  numberOfTokens = maxTokens - _mintedTokens;
              }
              uint256 reimbursement = origMintCount - numberOfTokens; 
              uint256 reimbursementPrice = reimbursement * nftPrice;
              mintsPerWallet[msg.sender] += numberOfTokens;
              _mint(msg.sender, numberOfTokens);
              
              // cap reimbursement at msg.value in case something goes wrong
              if( 0 < reimbursementPrice && reimbursementPrice < msg.value)
              {
                  (bool sent, ) = msg.sender.call{value: reimbursementPrice}("");
                  require(sent, "Failed to send Ether");
              }
              
              return _mintedTokens;
          }
          
          // transfer ownership of the smart contract
          function transferOwnership(address newOwner) onlyOwner external returns(address)
          {
              require(newOwner != address(0), "Cannot set zero address as owner!");
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
              return(owner);
          }
          // only owner or Fair.xyz - withdraw contract balance to owner wallet. 6% primary sale fee to Fair.xyz
          function withdraw()
              public
              payable
          {
              require(msg.sender == owner || msg.sender == viewWithdraw(), "Not owner or Fair.XYZ!");
              require(msg.sender == tx.origin, "Cannot withdraw from a contract");
              uint256 contractBalance = address(this).balance;
              (bool sent, ) = viewWithdraw().call{value: contractBalance*3/50}("");
              require(sent, "Failed to send Ether");
              uint256 remainingContractBalance = address(this).balance;
              payable(owner).transfer(remainingContractBalance);
          }
      }// SPDX-License-Identifier: MIT
      // @ Fair.xyz dev
      pragma solidity 0.8.7;
      import "IERC721.sol";
      import "IERC721Receiver.sol";
      import "IERC721Metadata.sol";
      import "Address.sol";
      import "Context.sol";
      import "Strings.sol";
      import "ERC165.sol";
      import "ERC2981.sol";
      interface IFairXYZWallets {
          function viewSigner() view external returns(address);
          function viewWithdraw() view external returns(address);
          function viewPreapproved(address address_) view external returns(bool);
          function viewPathURI(string memory pathURI_) view external returns(string memory);
      }
      /**
       * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
       * the Metadata extension, with modifications by the Fair.xyz team, thus setting the ERC721xyz standard
       */
      contract ERC721xyz is Context, ERC165, IERC721, ERC2981, IERC721Metadata {
          using Address for address;
          using Strings for uint256;
          // Token name
          string private _name;
          // Token symbol
          string private _symbol;
          // Token mint count
          uint256 public _mintedTokens;
          // Token burnt count
          uint256 internal _burntTokens;
          // Mapping from token ID to owner address
          mapping(uint256 => address) private _owners;
          // Mapping from token ID to original owner address
          mapping(uint256 => address) private _origOwners;
          // Burnt tokens
          mapping(uint256 => bool) private _burnedTokens;
          // 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(ERC2981, ERC165, IERC165) returns (bool) {
              return
                  interfaceId == type(IERC2981).interfaceId ||
                  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 Returns number of minted Tokens
           */
          function viewMinted() public view virtual returns(uint256) {
              return _mintedTokens;
          }
          // return all tokens
          function totalSupply() public view virtual returns(uint256) {
              return _mintedTokens - _burntTokens;
          }
          /**
           * @dev Mints a batch of `tokenIds` and transfers it to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           *
           * Emits {Transfer} events.
           */
          function _mint(address to, uint256 numberOfTokens) internal virtual {
              require(to != address(0), "ERC721: mint to the zero address");
              uint256 orig_count = _mintedTokens;
        
              _mintedTokens += numberOfTokens;
              _beforeTokenTransfer(address(0), to, _mintedTokens);
              _balances[to] += numberOfTokens;
              
              _origOwners[_mintedTokens] = to;
              uint256 loop_ = orig_count + numberOfTokens + 1; 
              for (uint i = orig_count + 1; i < loop_ ; ) {
                      emit Transfer(address(0), to, i);
                      ++i;
                  }        
              _afterTokenTransfer(address(0), to, _mintedTokens);
          }
          /**
           * @dev Returns owner of token ID.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              require(_exists(tokenId), "ERC721xyz: Query for non existent token!");
              
              uint256 counter = tokenId;
              
              if(_owners[tokenId] == address(0))
              {
                  while (true) {
                              
                              address firstOwner = _origOwners[counter];
                              if (firstOwner != address(0)) {
                                  return firstOwner;
                              }
                              ++counter;
                  }
              } else {
                  return _owners[tokenId];
              }
          }
          /**
           * @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 Combines path URI, base URI and pre-reveal URI for the full metadata journey on Fair.xyz
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
              string memory pathURI = _pathURI();
              string memory baseURI = _baseURI();
              string memory preRevealURI = _preRevealURI();
              
              if (bytes(pathURI).length == 0)
              {
                  return preRevealURI; 
              }
              else
              {
                  return string(abi.encodePacked(pathURI, baseURI, tokenId.toString()));
              }
          }
          /**
           * @dev Base URI for computing {tokenURI}. If the pathURI is set, the resulting URI for each
           * token will be the concatenation of the `baseURI`, the `pathURI` and the `tokenId`. Empty
           * by default, can be overridden in child contracts.
           */
          function _baseURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev FairXYZ - URI for computing {tokenURI}. If set, the resulting URI for each
           * token will be the concatenation of the `baseURI`, the `pathURI` and the `tokenId`. Empty
           * by default, can be overridden in child contracts.
           */
          function _pathURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev FairXYZ - URI to be shown during pre-reveal of a collection
           */
          function _preRevealURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev See {IERC721-approve}.
           */
          function approve(address to, uint256 tokenId) public virtual override {
              address owner = ERC721xyz.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 {
              _setApprovalForAll(_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) {
              if(_burnedTokens[tokenId]) return false;
              return (0 < tokenId && tokenId <= _mintedTokens);
          }
          /**
           * @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 = ERC721xyz.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 tokenCount) internal virtual {
              _safeMint(to, tokenCount, "");
          }
          /**
           * @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 tokenCount,
              bytes memory _data
          ) internal virtual {
              _mint(to, tokenCount);
              require(
                  _checkOnERC721Received(address(0), to, _mintedTokens, _data),
                  "ERC721: transfer to non ERC721Receiver implementer"
              );
          }
          /**
           * @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 {
              require(_exists(tokenId), "ERC721xyz: Query for nonexistent token!");
              address owner = ERC721xyz.ownerOf(tokenId);
              _beforeTokenTransfer(owner, address(0), tokenId);
              // Clear approvals
              _approve(address(0), tokenId);
              _balances[owner] -= 1;
              _burnedTokens[tokenId] = true;
              _burntTokens += 1;
              emit Transfer(owner, address(0), tokenId);
              _afterTokenTransfer(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(ERC721xyz.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
              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);
              _afterTokenTransfer(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(ERC721xyz.ownerOf(tokenId), to, tokenId);
          }
          /**
           * @dev Approve `operator` to operate on all of `owner` tokens
           *
           * Emits a {ApprovalForAll} event.
           */
          function _setApprovalForAll(
              address owner,
              address operator,
              bool approved
          ) internal virtual {
              require(owner != operator, "ERC721: approve to caller");
              _operatorApprovals[owner][operator] = approved;
              emit ApprovalForAll(owner, operator, approved);
          }
          /**
           * @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 {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 tokenId
          ) internal virtual {}
      }// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "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`.
           *
           * 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;
          /**
           * @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 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 the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.0;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
      pragma solidity ^0.8.0;
      import "IERC721.sol";
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Metadata is IERC721 {
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @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
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 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
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
      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
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      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
      // OpenZeppelin Contracts (last updated v4.6.0) (token/common/ERC2981.sol)
      pragma solidity ^0.8.0;
      import "IERC2981.sol";
      import "ERC165.sol";
      /**
       * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
       *
       * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
       * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
       *
       * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
       * fee is specified in basis points by default.
       *
       * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
       * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
       * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
       *
       * _Available since v4.5._
       */
      abstract contract ERC2981 is IERC2981, ERC165 {
          struct RoyaltyInfo {
              address receiver;
              uint96 royaltyFraction;
          }
          RoyaltyInfo private _defaultRoyaltyInfo;
          mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
              return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @inheritdoc IERC2981
           */
          function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
              RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
              if (royalty.receiver == address(0)) {
                  royalty = _defaultRoyaltyInfo;
              }
              uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
              return (royalty.receiver, royaltyAmount);
          }
          /**
           * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
           * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
           * override.
           */
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /**
           * @dev Sets the royalty information that all ids in this contract will default to.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: invalid receiver");
              _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Removes default royalty information.
           */
          function _deleteDefaultRoyalty() internal virtual {
              delete _defaultRoyaltyInfo;
          }
          /**
           * @dev Sets the royalty information for a specific token id, overriding the global default.
           *
           * Requirements:
           *
           * - `tokenId` must be already minted.
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setTokenRoyalty(
              uint256 tokenId,
              address receiver,
              uint96 feeNumerator
          ) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: Invalid parameters");
              _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Resets royalty information for the token id back to the global default.
           */
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              delete _tokenRoyaltyInfo[tokenId];
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
      pragma solidity ^0.8.0;
      import "IERC165.sol";
      /**
       * @dev Interface for the NFT Royalty Standard.
       *
       * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
       * support for royalty payments across all NFT marketplaces and ecosystem participants.
       *
       * _Available since v4.5._
       */
      interface IERC2981 is IERC165 {
          /**
           * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
           * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
           */
          function royaltyInfo(uint256 tokenId, uint256 salePrice)
              external
              view
              returns (address receiver, uint256 royaltyAmount);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "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
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              } else if (error == RecoverError.InvalidSignatureV) {
                  revert("ECDSA: invalid signature 'v' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              // Check the signature length
              // - case 65: r,s,v signature (standard)
              // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      vs := mload(add(signature, 0x40))
                  }
                  return tryRecover(hash, r, vs);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              if (v != 27 && v != 28) {
                  return (address(0), RecoverError.InvalidSignatureV);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      

      File 2 of 3: FairXYZDeployer
      // SPDX-License-Identifier: MIT
      // @ Fair.xyz dev
      pragma solidity 0.8.7;
      import "ERC721xyz.sol";
      import "Pausable.sol";
      import "ECDSA.sol";
      contract FairXYZDeployer is ERC721xyz, Pausable{
          
          string private _name;
          string private _symbol;
          using ECDSA for bytes32;
          
          uint256 public maxTokens;
          
          uint256 internal nftPrice;
          string private baseURI;
          string private pathURI;
          string public preRevealURI;
          string private _overrideURI;
          bool public lockURI;
          bool public mintReleased;
          address public interfaceAddress;
          bool public isBase;
          address public owner;
          bool public burnable;
          uint256 public maxMintsPerWallet;
          mapping(bytes32 => bool) private usedHashes;
          mapping(address => uint256) public mintsPerWallet;
          event OwnershipTransferred(address indexed prevOwner, address indexed newOwner);
          event NewPriceSet(uint256 newSetPrice);
          event NewMaxMintsPerWalletSet(uint256 newMaxMints);
          event NewTokenRoyaltySet(uint256 newRoyalty);
          event NewTokenURI(string newTokenURI);
          event NewPathURI(string newPathURI);
          modifier onlyOwner() {
              require(msg.sender == owner, "ERROR");
              _;
          }
          constructor() payable ERC721xyz(_name, _symbol){
              isBase = true;
              _name = "FairXYZ";
              _symbol = "FairXYZ";
              _pause();
          }
       
          // Collection Name
          function name() override public view returns (string memory) {
              return _name;
          }
          // Collection ticker
          function symbol() override public view returns (string memory) {
              return _symbol;
          }
          // View all variables
          function viewAllVariables() public view returns(uint256, uint256, string memory){
              return(nftPrice, maxMintsPerWallet, pathURI);
          }
          // Signer address for minting
          function viewSigner() public view returns(address){
              address returnSigner = IFairXYZWallets(interfaceAddress).viewSigner(); 
              return(returnSigner);
          }
          // Fair.xyz wallet address
          function viewWithdraw() public view returns(address){
              address returnWithdraw = IFairXYZWallets(interfaceAddress).viewWithdraw(); 
              return(returnWithdraw);
          }
          // Initialize Creator contract
          function initialize(uint256 maxTokens_, uint256 nftPrice_, string memory name_, string memory symbol_,
                              bool burnable_, uint256 maxMintsPerWallet_, address interfaceAddress_,
                              string[] memory URIs_, uint96 royaltyPercentage_) external {
              
              require( !isBase , "This contract is not a base contract!");
              require( interfaceAddress_ != address(0), "Cannot set to 0 address!");
              owner = tx.origin;
              maxTokens = maxTokens_;
              nftPrice = nftPrice_;
              _name = name_;
              _symbol = symbol_;
              burnable = burnable_; 
              maxMintsPerWallet = maxMintsPerWallet_;
              interfaceAddress = interfaceAddress_;
              preRevealURI = URIs_[0];
              baseURI = URIs_[1];
              pathURI = URIs_[2];
              isBase = true;
              _setDefaultRoyalty(tx.origin, royaltyPercentage_);
          }
          // Limit on NFT sale
          modifier saleIsOpen{
              require(_mintedTokens < maxTokens, "Sale end");
              _;
          }
          // Lock metadata forever
          function lockURIforever() external onlyOwner {
              lockURI = true;
          }
          
          // View price
          function price() external view returns (uint256) {
              return nftPrice; 
          }
          function hashVariableChanges(address sender, string memory newURI, string memory newPathURI, 
              uint256 newPrice, uint256 newMaxMintsPerWallet, uint256 newRoyaltyPercentage) private pure returns(bytes32) 
          {
                bytes32 hash = keccak256(abi.encodePacked(
                  "\\x19Ethereum Signed Message:\
      32",
                  keccak256(abi.encodePacked(sender, newURI, newPathURI, newPrice, newMaxMintsPerWallet, newRoyaltyPercentage)))
                );    
                return hash;
          }
           
          // change variables in contract
          function overrideVariables(bytes memory signature, string memory newURI, string memory newPathURI, 
              uint256 newPrice, uint256 newMaxMintsPerWallet, uint96 newRoyaltyPercentage)
              onlyOwner
              external
          {
              bytes32 messageHash = hashVariableChanges(msg.sender, newURI, newPathURI, 
                                                        newPrice, newMaxMintsPerWallet, newRoyaltyPercentage);
              address signAdd = viewSigner();
              require(messageHash.recover(signature) == signAdd, "Unrecognizable Hash");
              if(!lockURI)
              {
                  if (bytes(newPathURI).length != 0)       
                      pathURI = newPathURI;
                      emit NewPathURI(pathURI);
                  if(bytes(newURI).length != 0)
                  {
                      _overrideURI = newURI;
                      baseURI = "";
                      emit NewTokenURI(_overrideURI);
                  }
              }
              if(newPrice!=nftPrice)
              {
                  nftPrice = newPrice;
                  emit NewPriceSet(nftPrice);
              }
              if(newMaxMintsPerWallet!=maxMintsPerWallet)
              {
                  maxMintsPerWallet = newMaxMintsPerWallet;
                  emit NewMaxMintsPerWalletSet(maxMintsPerWallet);
              }
              _setDefaultRoyalty(owner, newRoyaltyPercentage);
              emit NewTokenRoyaltySet(newRoyaltyPercentage);
           
          }
          
          // return Base URI
          function _baseURI() public view override returns (string memory) {
              return baseURI;
          }
          // return Path URI
          function _pathURI() public view override returns (string memory) {
              if(bytes(_overrideURI).length == 0)
              {
                  return IFairXYZWallets(interfaceAddress).viewPathURI(pathURI);
              }
              else
              {
                  return _overrideURI;
              }
          }
          // return pre-Reveal URI
          function _preRevealURI() public view override returns (string memory) {
              return preRevealURI;
          }
          // See remaining mints
          function remainingMints(address minterAddress) public view returns(uint256) {
              
              if (maxMintsPerWallet == 0 ) {
                  revert("Collection with no mint limit");
              }
                  
              uint256 mintsLeft = maxMintsPerWallet - mintsPerWallet[minterAddress];
              return mintsLeft; 
          }
          
          // pause minting 
          function pause() external onlyOwner {
              _pause();
          }
          
          // unpause minting 
          function unpause() external onlyOwner {
              _unpause();
          }
          // Burn token
          function burn(uint256 tokenId) external returns(uint256)
          {
              require(burnable, "This contract does not allow burning");
              require(msg.sender == ownerOf(tokenId), "Burner is not the owner of token");
              _burn(tokenId);
              return tokenId;
          }
          // Airdrop a token
          function airdrop(address[] memory address_, uint256 tokenCount) onlyOwner external returns(uint256) 
          {
              require(_mintedTokens + address_.length * tokenCount <= maxTokens, "This exceeds the maximum number of NFTs on sale!");
              for(uint256 i = 0; i < address_.length; ) {
                  _safeMint(address_[i], tokenCount);
                  ++i;
              }
              return _mintedTokens;
          }
          // override isApprovedForAll to allow for pre-approved operators (OpenSea and LooksRare)
          function isApprovedForAll(address _owner, address operator) public view override(ERC721xyz) returns(bool)
          {
              return( IFairXYZWallets(interfaceAddress).viewPreapproved(operator) || super.isApprovedForAll(_owner, operator) );
          }
          function hashTransaction(address sender, uint256 qty, uint256 nonce, uint256 phaseLimit, address address_) private pure returns(bytes32) 
          {
                bytes32 hash = keccak256(abi.encodePacked(
                  "\\x19Ethereum Signed Message:\
      32",
                  keccak256(abi.encodePacked(sender, qty, nonce, phaseLimit, address_)))
                );    
                return hash;
          }
          // Mint a token    
          function mint(bytes memory signature, uint256 nonce, uint256 numberOfTokens, uint256 phaseLimit)
              payable
              external
              whenNotPaused
              saleIsOpen
              returns (uint256)
          {
              bytes32 messageHash = hashTransaction(msg.sender, numberOfTokens, nonce, phaseLimit, address(this));
              address signAdd = viewSigner();
              require(_mintedTokens < phaseLimit, "End of phase limit!");
              require(phaseLimit <= maxTokens, "Phase limit cannot be larger than max tokens");
              require(messageHash.recover(signature) == signAdd, "Unrecognizable Hash");
              require(!usedHashes[messageHash], "Reused Hash");
              require(msg.value  >= nftPrice * numberOfTokens, "You have not sent the required amount of ETH");
              require(numberOfTokens <= 20, "Token minting limit per transaction exceeded");
              require(block.number <= nonce  + 20, "Time limit has passed");
              require(msg.sender == tx.origin, "Cannot mint from contract");
              usedHashes[messageHash] = true;
              uint256 origMintCount = numberOfTokens;
              // If trying to mint more tokens than available -> reimburse for excess mints and allow for lower mint count
              // to avoid a failed tx
              if(maxMintsPerWallet > 0)
              {
                  require(mintsPerWallet[msg.sender] < maxMintsPerWallet, "Exceeds number of mints per wallet");
                  
                  if(mintsPerWallet[msg.sender] + numberOfTokens > maxMintsPerWallet)
                  {
                      numberOfTokens = maxMintsPerWallet - mintsPerWallet[msg.sender];
                  }            
              }
       
              if( (_mintedTokens + numberOfTokens > phaseLimit) )
              {
                  numberOfTokens = phaseLimit - _mintedTokens;
              }
              uint256 reimbursement = origMintCount - numberOfTokens; 
              uint256 reimbursementPrice = reimbursement * nftPrice;
              mintsPerWallet[msg.sender] += numberOfTokens;
              _mint(msg.sender, numberOfTokens);
              
              // cap reimbursement at msg.value in case something goes wrong
              if( 0 < reimbursementPrice && reimbursementPrice < msg.value)
              {
                  (bool sent, ) = msg.sender.call{value: reimbursementPrice}("");
                  require(sent, "Failed to send Ether");
              }
              
              return _mintedTokens;
          }
          // Release the mint, so no signature is required
          function releaseMint() onlyOwner external
          {
              require(!mintReleased);
              mintReleased = true;
          }
          // Mint a token without a signature, requires mintRelease    
          function mintNoSignature(uint256 numberOfTokens)
              payable
              external
              whenNotPaused
              saleIsOpen
              returns (uint256)
          {
              require(mintReleased, "Please use the mint function to buy your token");
              require(msg.value  >= nftPrice * numberOfTokens, "You have not sent the required amount of ETH");
              require(numberOfTokens <= 20, "Token minting limit per transaction exceeded");
              require(msg.sender == tx.origin, "Cannot mint from contract");
              uint256 origMintCount = numberOfTokens;
              // If trying to mint more tokens than available -> reimburse for excess mints and allow for lower mint count
              // to avoid a failed tx
              if(maxMintsPerWallet > 0)
              {
                  require(mintsPerWallet[msg.sender] < maxMintsPerWallet, "Exceeds number of mints per wallet");
                  
                  if(mintsPerWallet[msg.sender] + numberOfTokens > maxMintsPerWallet)
                  {
                      numberOfTokens = maxMintsPerWallet - mintsPerWallet[msg.sender];
                  }            
              }
       
              if( (_mintedTokens + numberOfTokens > maxTokens) )
              {
                  numberOfTokens = maxTokens - _mintedTokens;
              }
              uint256 reimbursement = origMintCount - numberOfTokens; 
              uint256 reimbursementPrice = reimbursement * nftPrice;
              mintsPerWallet[msg.sender] += numberOfTokens;
              _mint(msg.sender, numberOfTokens);
              
              // cap reimbursement at msg.value in case something goes wrong
              if( 0 < reimbursementPrice && reimbursementPrice < msg.value)
              {
                  (bool sent, ) = msg.sender.call{value: reimbursementPrice}("");
                  require(sent, "Failed to send Ether");
              }
              
              return _mintedTokens;
          }
          
          // transfer ownership of the smart contract
          function transferOwnership(address newOwner) onlyOwner external returns(address)
          {
              require(newOwner != address(0), "Cannot set zero address as owner!");
              owner = newOwner;
              emit OwnershipTransferred(msg.sender, newOwner);
              return(owner);
          }
          // only owner or Fair.xyz - withdraw contract balance to owner wallet. 6% primary sale fee to Fair.xyz
          function withdraw()
              public
              payable
          {
              require(msg.sender == owner || msg.sender == viewWithdraw(), "Not owner or Fair.XYZ!");
              require(msg.sender == tx.origin, "Cannot withdraw from a contract");
              uint256 contractBalance = address(this).balance;
              (bool sent, ) = viewWithdraw().call{value: contractBalance*3/50}("");
              require(sent, "Failed to send Ether");
              uint256 remainingContractBalance = address(this).balance;
              payable(owner).transfer(remainingContractBalance);
          }
      }// SPDX-License-Identifier: MIT
      // @ Fair.xyz dev
      pragma solidity 0.8.7;
      import "IERC721.sol";
      import "IERC721Receiver.sol";
      import "IERC721Metadata.sol";
      import "Address.sol";
      import "Context.sol";
      import "Strings.sol";
      import "ERC165.sol";
      import "ERC2981.sol";
      interface IFairXYZWallets {
          function viewSigner() view external returns(address);
          function viewWithdraw() view external returns(address);
          function viewPreapproved(address address_) view external returns(bool);
          function viewPathURI(string memory pathURI_) view external returns(string memory);
      }
      /**
       * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
       * the Metadata extension, with modifications by the Fair.xyz team, thus setting the ERC721xyz standard
       */
      contract ERC721xyz is Context, ERC165, IERC721, ERC2981, IERC721Metadata {
          using Address for address;
          using Strings for uint256;
          // Token name
          string private _name;
          // Token symbol
          string private _symbol;
          // Token mint count
          uint256 public _mintedTokens;
          // Token burnt count
          uint256 internal _burntTokens;
          // Mapping from token ID to owner address
          mapping(uint256 => address) private _owners;
          // Mapping from token ID to original owner address
          mapping(uint256 => address) private _origOwners;
          // Burnt tokens
          mapping(uint256 => bool) private _burnedTokens;
          // 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(ERC2981, ERC165, IERC165) returns (bool) {
              return
                  interfaceId == type(IERC2981).interfaceId ||
                  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 Returns number of minted Tokens
           */
          function viewMinted() public view virtual returns(uint256) {
              return _mintedTokens;
          }
          // return all tokens
          function totalSupply() public view virtual returns(uint256) {
              return _mintedTokens - _burntTokens;
          }
          /**
           * @dev Mints a batch of `tokenIds` and transfers it to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           *
           * Emits {Transfer} events.
           */
          function _mint(address to, uint256 numberOfTokens) internal virtual {
              require(to != address(0), "ERC721: mint to the zero address");
              uint256 orig_count = _mintedTokens;
        
              _mintedTokens += numberOfTokens;
              _beforeTokenTransfer(address(0), to, _mintedTokens);
              _balances[to] += numberOfTokens;
              
              _origOwners[_mintedTokens] = to;
              uint256 loop_ = orig_count + numberOfTokens + 1; 
              for (uint i = orig_count + 1; i < loop_ ; ) {
                      emit Transfer(address(0), to, i);
                      ++i;
                  }        
              _afterTokenTransfer(address(0), to, _mintedTokens);
          }
          /**
           * @dev Returns owner of token ID.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              require(_exists(tokenId), "ERC721xyz: Query for non existent token!");
              
              uint256 counter = tokenId;
              
              if(_owners[tokenId] == address(0))
              {
                  while (true) {
                              
                              address firstOwner = _origOwners[counter];
                              if (firstOwner != address(0)) {
                                  return firstOwner;
                              }
                              ++counter;
                  }
              } else {
                  return _owners[tokenId];
              }
          }
          /**
           * @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 Combines path URI, base URI and pre-reveal URI for the full metadata journey on Fair.xyz
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
              string memory pathURI = _pathURI();
              string memory baseURI = _baseURI();
              string memory preRevealURI = _preRevealURI();
              
              if (bytes(pathURI).length == 0)
              {
                  return preRevealURI; 
              }
              else
              {
                  return string(abi.encodePacked(pathURI, baseURI, tokenId.toString()));
              }
          }
          /**
           * @dev Base URI for computing {tokenURI}. If the pathURI is set, the resulting URI for each
           * token will be the concatenation of the `baseURI`, the `pathURI` and the `tokenId`. Empty
           * by default, can be overridden in child contracts.
           */
          function _baseURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev FairXYZ - URI for computing {tokenURI}. If set, the resulting URI for each
           * token will be the concatenation of the `baseURI`, the `pathURI` and the `tokenId`. Empty
           * by default, can be overridden in child contracts.
           */
          function _pathURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev FairXYZ - URI to be shown during pre-reveal of a collection
           */
          function _preRevealURI() public view virtual returns (string memory) {
              return "";
          }
          /**
           * @dev See {IERC721-approve}.
           */
          function approve(address to, uint256 tokenId) public virtual override {
              address owner = ERC721xyz.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 {
              _setApprovalForAll(_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) {
              if(_burnedTokens[tokenId]) return false;
              return (0 < tokenId && tokenId <= _mintedTokens);
          }
          /**
           * @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 = ERC721xyz.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 tokenCount) internal virtual {
              _safeMint(to, tokenCount, "");
          }
          /**
           * @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 tokenCount,
              bytes memory _data
          ) internal virtual {
              _mint(to, tokenCount);
              require(
                  _checkOnERC721Received(address(0), to, _mintedTokens, _data),
                  "ERC721: transfer to non ERC721Receiver implementer"
              );
          }
          /**
           * @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 {
              require(_exists(tokenId), "ERC721xyz: Query for nonexistent token!");
              address owner = ERC721xyz.ownerOf(tokenId);
              _beforeTokenTransfer(owner, address(0), tokenId);
              // Clear approvals
              _approve(address(0), tokenId);
              _balances[owner] -= 1;
              _burnedTokens[tokenId] = true;
              _burntTokens += 1;
              emit Transfer(owner, address(0), tokenId);
              _afterTokenTransfer(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(ERC721xyz.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
              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);
              _afterTokenTransfer(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(ERC721xyz.ownerOf(tokenId), to, tokenId);
          }
          /**
           * @dev Approve `operator` to operate on all of `owner` tokens
           *
           * Emits a {ApprovalForAll} event.
           */
          function _setApprovalForAll(
              address owner,
              address operator,
              bool approved
          ) internal virtual {
              require(owner != operator, "ERC721: approve to caller");
              _operatorApprovals[owner][operator] = approved;
              emit ApprovalForAll(owner, operator, approved);
          }
          /**
           * @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 {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _afterTokenTransfer(
              address from,
              address to,
              uint256 tokenId
          ) internal virtual {}
      }// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
      pragma solidity ^0.8.0;
      import "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`.
           *
           * 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;
          /**
           * @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 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 the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
      pragma solidity ^0.8.0;
      /**
       * @title ERC721 token receiver interface
       * @dev Interface for any contract that wants to support safeTransfers
       * from ERC721 asset contracts.
       */
      interface IERC721Receiver {
          /**
           * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
           * by `operator` from `from`, this function is called.
           *
           * It must return its Solidity selector to confirm the token transfer.
           * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
           *
           * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
           */
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
      pragma solidity ^0.8.0;
      import "IERC721.sol";
      /**
       * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
       * @dev See https://eips.ethereum.org/EIPS/eip-721
       */
      interface IERC721Metadata is IERC721 {
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @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
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 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
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
      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
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      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
      // OpenZeppelin Contracts (last updated v4.6.0) (token/common/ERC2981.sol)
      pragma solidity ^0.8.0;
      import "IERC2981.sol";
      import "ERC165.sol";
      /**
       * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
       *
       * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
       * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
       *
       * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
       * fee is specified in basis points by default.
       *
       * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
       * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
       * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
       *
       * _Available since v4.5._
       */
      abstract contract ERC2981 is IERC2981, ERC165 {
          struct RoyaltyInfo {
              address receiver;
              uint96 royaltyFraction;
          }
          RoyaltyInfo private _defaultRoyaltyInfo;
          mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
              return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @inheritdoc IERC2981
           */
          function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
              RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
              if (royalty.receiver == address(0)) {
                  royalty = _defaultRoyaltyInfo;
              }
              uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
              return (royalty.receiver, royaltyAmount);
          }
          /**
           * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
           * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
           * override.
           */
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /**
           * @dev Sets the royalty information that all ids in this contract will default to.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: invalid receiver");
              _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Removes default royalty information.
           */
          function _deleteDefaultRoyalty() internal virtual {
              delete _defaultRoyaltyInfo;
          }
          /**
           * @dev Sets the royalty information for a specific token id, overriding the global default.
           *
           * Requirements:
           *
           * - `tokenId` must be already minted.
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setTokenRoyalty(
              uint256 tokenId,
              address receiver,
              uint96 feeNumerator
          ) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: Invalid parameters");
              _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Resets royalty information for the token id back to the global default.
           */
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              delete _tokenRoyaltyInfo[tokenId];
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
      pragma solidity ^0.8.0;
      import "IERC165.sol";
      /**
       * @dev Interface for the NFT Royalty Standard.
       *
       * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
       * support for royalty payments across all NFT marketplaces and ecosystem participants.
       *
       * _Available since v4.5._
       */
      interface IERC2981 is IERC165 {
          /**
           * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
           * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
           */
          function royaltyInfo(uint256 tokenId, uint256 salePrice)
              external
              view
              returns (address receiver, uint256 royaltyAmount);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "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
      // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              } else if (error == RecoverError.InvalidSignatureV) {
                  revert("ECDSA: invalid signature 'v' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              // Check the signature length
              // - case 65: r,s,v signature (standard)
              // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  assembly {
                      r := mload(add(signature, 0x20))
                      vs := mload(add(signature, 0x40))
                  }
                  return tryRecover(hash, r, vs);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              if (v != 27 && v != 28) {
                  return (address(0), RecoverError.InvalidSignatureV);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      

      File 3 of 3: FairXYZWallets
      // SPDX-License-Identifier: MIT
      // @ Fair.xyz dev
      pragma solidity 0.8.7;
      import "Ownable.sol";
      contract FairXYZWallets is Ownable{
          
          address internal signerAddress;
          address internal withdrawAddress;
          mapping(address => bool) internal preApprovedAddresses;
          mapping(string => string) internal URIReveal; 
          mapping(string => bool) internal lockedURIReveal;
          event NewSignerWallet(address indexed newSignerAddress);
          event NewWithdrawWallet(address indexed newWithdrawAddress);
          constructor(address addressForSigner, address addressForWithdraw, address[] memory preApprovedList){
              
              require(preApprovedList.length <= 5, "Cannot set too many pre-approved addresses!");
              require(addressForSigner != address(0), "Cannot be zero address");
              require(addressForWithdraw != address(0), "Cannot be zero address");
              signerAddress = addressForSigner;
              withdrawAddress = addressForWithdraw;
              for(uint i = 0; i < preApprovedList.length; )
              {
                  addPreapproved(preApprovedList[i]);
                  ++i;
              }
          }
          function viewPathURI(string memory pathURI) view external returns(string memory) 
          {
              return URIReveal[pathURI];
          }
          function viewSigner() view external returns(address)
          {
              return(signerAddress);
          }
          function viewWithdraw() view external returns(address)
          {
              return(withdrawAddress);
          }
          function revealPathURI(string memory pathURI, string memory revealURI) external onlyOwner returns(string memory)
          {
              require(!lockedURIReveal[pathURI], "Path URI has been locked!");
              URIReveal[pathURI] = revealURI;
              return(revealURI);
          }
          function lockURIReveal(string memory pathURI) external onlyOwner
          {
              require(!lockedURIReveal[pathURI], "Path URI has been locked!");
              lockedURIReveal[pathURI] = true;
          }
          function changeSigner(address newAddress) external onlyOwner returns(address)
          {
              signerAddress = newAddress;
              emit NewSignerWallet(signerAddress);
              return signerAddress;
          }
          function changeWithdraw(address newAddress) external onlyOwner returns(address)
          {
              withdrawAddress = newAddress;
              emit NewWithdrawWallet(signerAddress);
              return withdrawAddress;
          }
          function viewPreapproved(address address_) external view returns(bool)
          {
              return preApprovedAddresses[address_];
          }
          // Gas-free listings on OpenSea and LooksRare. Can only be called from constructor
          function addPreapproved(address preapprovedAddress) private onlyOwner returns(address)
          {
              require(preapprovedAddress != address(0), "Cannot be zero address");
              preApprovedAddresses[preapprovedAddress] = true;
              return preapprovedAddress;
          }
          function removePreapproved(address preapprovedAddress) external onlyOwner returns(address)
          {
              require(preApprovedAddresses[preapprovedAddress]);
              preApprovedAddresses[preapprovedAddress] = false;
              return(preapprovedAddress);
          }
      }// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev 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 {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }