ETH Price: $2,437.13 (+1.31%)

Transaction Decoder

Block:
16464357 at Jan-22-2023 07:12:59 PM +UTC
Transaction Fee:
0.010815151637712124 ETH $26.36
Gas Used:
487,709 Gas / 22.175419436 Gwei

Emitted Events:

367 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1359 )
368 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1360 )
369 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1361 )
370 DropERC721.TokensClaimed( claimConditionIndex=0, claimer=0x1cb91f88395c25921f140649bc52a45622d047a6, receiver=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, startTokenId=1359, quantityClaimed=3 )
371 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1362 )
372 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1363 )
373 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1364 )
374 DropERC721.TokensClaimed( claimConditionIndex=0, claimer=0x0723eb751fd68a1ee5c7227efdd0fa510937a8e8, receiver=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, startTokenId=1362, quantityClaimed=3 )
375 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1365 )
376 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1366 )
377 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1367 )
378 DropERC721.TokensClaimed( claimConditionIndex=0, claimer=0x2d8b14a1f07d3fcc0fe8160332e87e2fd06f29f9, receiver=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, startTokenId=1365, quantityClaimed=3 )
379 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1368 )
380 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1369 )
381 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1370 )
382 DropERC721.TokensClaimed( claimConditionIndex=0, claimer=0x3a611ef03c9a42fa608020d41d5d18277a5c224a, receiver=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, startTokenId=1368, quantityClaimed=3 )
383 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1371 )
384 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1372 )
385 DropERC721.Transfer( from=0x00000000...000000000, to=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, tokenId=1373 )
386 DropERC721.TokensClaimed( claimConditionIndex=0, claimer=0x00f7c687756364750607131cb1d3ca2e6c67874b, receiver=[Sender] 0xe595cc35728f7c6ea40d4f282170e64e411d82da, startTokenId=1371, quantityClaimed=3 )

Account State Difference:

  Address   Before After State Difference Code
0x0F6845c1...11C9Cd549
(builder0x69)
2.11014854433299462 Eth2.11127515212299462 Eth0.00112660779
0xE595cc35...E411d82DA
0.066031086651822235 Eth
Nonce: 1301
0.055215935014110111 Eth
Nonce: 1302
0.010815151637712124

Execution Trace

0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.64889e83( )
  • 0x1cb91f88395c25921f140649bc52a45622d047a6.1f42de11( )
    • 0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.1f42de11( )
    • 0x0723eb751fd68a1ee5c7227efdd0fa510937a8e8.1f42de11( )
      • 0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.1f42de11( )
      • 0x2d8b14a1f07d3fcc0fe8160332e87e2fd06f29f9.1f42de11( )
        • 0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.1f42de11( )
        • 0x3a611ef03c9a42fa608020d41d5d18277a5c224a.1f42de11( )
          • 0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.1f42de11( )
          • 0x00f7c687756364750607131cb1d3ca2e6c67874b.1f42de11( )
            • 0x3a46fc6e25fc8413aea02cb2e9e96b9dd2e4f8a6.1f42de11( )
              File 1 of 2: DropERC721
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.11;
              //  ==========  External imports    ==========
              import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
              import "../eip/ERC721AVirtualApprove.sol";
              //  ==========  Internal imports    ==========
              import "../openzeppelin-presets/metatx/ERC2771ContextUpgradeable.sol";
              import "../lib/CurrencyTransferLib.sol";
              //  ==========  Features    ==========
              import "../extension/ContractMetadata.sol";
              import "../extension/PlatformFee.sol";
              import "../extension/Royalty.sol";
              import "../extension/PrimarySale.sol";
              import "../extension/Ownable.sol";
              import "../extension/DelayedReveal.sol";
              import "../extension/LazyMint.sol";
              import "../extension/PermissionsEnumerable.sol";
              import "../extension/Drop.sol";
              // OpenSea operator filter
              import "../extension/DefaultOperatorFiltererUpgradeable.sol";
              contract DropERC721 is
                  Initializable,
                  ContractMetadata,
                  PlatformFee,
                  Royalty,
                  PrimarySale,
                  Ownable,
                  DelayedReveal,
                  LazyMint,
                  PermissionsEnumerable,
                  Drop,
                  ERC2771ContextUpgradeable,
                  MulticallUpgradeable,
                  DefaultOperatorFiltererUpgradeable,
                  ERC721AUpgradeable
              {
                  using StringsUpgradeable for uint256;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                  bytes32 private transferRole;
                  /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                  bytes32 private minterRole;
                  /// @dev Max bps in the thirdweb system.
                  uint256 private constant MAX_BPS = 10_000;
                  /// @dev Global max total supply of NFTs.
                  uint256 public maxTotalSupply;
                  /// @dev Emitted when the global max supply of tokens is updated.
                  event MaxTotalSupplyUpdated(uint256 maxTotalSupply);
                  /*///////////////////////////////////////////////////////////////
                                  Constructor + initializer logic
                  //////////////////////////////////////////////////////////////*/
                  constructor() initializer {}
                  /// @dev Initiliazes the contract, like a constructor.
                  function initialize(
                      address _defaultAdmin,
                      string memory _name,
                      string memory _symbol,
                      string memory _contractURI,
                      address[] memory _trustedForwarders,
                      address _saleRecipient,
                      address _royaltyRecipient,
                      uint128 _royaltyBps,
                      uint128 _platformFeeBps,
                      address _platformFeeRecipient
                  ) external initializer {
                      bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                      bytes32 _minterRole = keccak256("MINTER_ROLE");
                      // Initialize inherited contracts, most base-like -> most derived.
                      __ERC2771Context_init(_trustedForwarders);
                      __ERC721A_init(_name, _symbol);
                      __DefaultOperatorFilterer_init();
                      _setupContractURI(_contractURI);
                      _setupOwner(_defaultAdmin);
                      _setOperatorRestriction(true);
                      _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                      _setupRole(_minterRole, _defaultAdmin);
                      _setupRole(_transferRole, _defaultAdmin);
                      _setupRole(_transferRole, address(0));
                      _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                      _setupPrimarySaleRecipient(_saleRecipient);
                      transferRole = _transferRole;
                      minterRole = _minterRole;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      ERC 165 / 721 / 2981 logic
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns the URI for a given tokenId.
                  function tokenURI(uint256 _tokenId) public view override returns (string memory) {
                      (uint256 batchId, ) = _getBatchId(_tokenId);
                      string memory batchUri = _getBaseURI(_tokenId);
                      if (isEncryptedBatch(batchId)) {
                          return string(abi.encodePacked(batchUri, "0"));
                      } else {
                          return string(abi.encodePacked(batchUri, _tokenId.toString()));
                      }
                  }
                  /// @dev See ERC 165
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721AUpgradeable, IERC165)
                      returns (bool)
                  {
                      return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Contract identifiers
                  //////////////////////////////////////////////////////////////*/
                  function contractType() external pure returns (bytes32) {
                      return bytes32("DropERC721");
                  }
                  function contractVersion() external pure returns (uint8) {
                      return uint8(4);
                  }
                  /*///////////////////////////////////////////////////////////////
                                  Lazy minting + delayed-reveal logic
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs.
                   *       The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`.
                   */
                  function lazyMint(
                      uint256 _amount,
                      string calldata _baseURIForTokens,
                      bytes calldata _data
                  ) public override returns (uint256 batchId) {
                      if (_data.length > 0) {
                          (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
                          if (encryptedURI.length != 0 && provenanceHash != "") {
                              _setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
                          }
                      }
                      return super.lazyMint(_amount, _baseURIForTokens, _data);
                  }
                  /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
                  function reveal(uint256 _index, bytes calldata _key)
                      external
                      onlyRole(minterRole)
                      returns (string memory revealedURI)
                  {
                      uint256 batchId = getBatchIdAtIndex(_index);
                      revealedURI = getRevealURI(batchId, _key);
                      _setEncryptedData(batchId, "");
                      _setBaseURI(batchId, revealedURI);
                      emit TokenURIRevealed(_index, revealedURI);
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Setter functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Lets a contract admin set the global maximum supply for collection's NFTs.
                  function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      maxTotalSupply = _maxTotalSupply;
                      emit MaxTotalSupplyUpdated(_maxTotalSupply);
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Runs before every `claim` function call.
                  function _beforeClaim(
                      address,
                      uint256 _quantity,
                      address,
                      uint256,
                      AllowlistProof calldata,
                      bytes memory
                  ) internal view override {
                      require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens");
                      require(maxTotalSupply == 0 || _currentIndex + _quantity <= maxTotalSupply, "exceed max total supply.");
                  }
                  /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                  function _collectPriceOnClaim(
                      address _primarySaleRecipient,
                      uint256 _quantityToClaim,
                      address _currency,
                      uint256 _pricePerToken
                  ) internal override {
                      if (_pricePerToken == 0) {
                          return;
                      }
                      (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                      address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
                      uint256 totalPrice = _quantityToClaim * _pricePerToken;
                      uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                      if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                          if (msg.value != totalPrice) {
                              revert("!Price");
                          }
                      }
                      CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                      CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees);
                  }
                  /// @dev Transfers the NFTs being claimed.
                  function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed)
                      internal
                      override
                      returns (uint256 startTokenId)
                  {
                      startTokenId = _currentIndex;
                      _safeMint(_to, _quantityBeingClaimed);
                  }
                  /// @dev Checks whether platform fee info can be set in the given execution context.
                  function _canSetPlatformFeeInfo() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether primary sale recipient can be set in the given execution context.
                  function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether owner can be set in the given execution context.
                  function _canSetOwner() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether royalty info can be set in the given execution context.
                  function _canSetRoyaltyInfo() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether platform fee info can be set in the given execution context.
                  function _canSetClaimConditions() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Returns whether lazy minting can be done in the given execution context.
                  function _canLazyMint() internal view virtual override returns (bool) {
                      return hasRole(minterRole, _msgSender());
                  }
                  /// @dev Returns whether operator restriction can be set in the given execution context.
                  function _canSetOperatorRestriction() internal virtual override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Miscellaneous
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function totalMinted() external view returns (uint256) {
                      unchecked {
                          return _currentIndex - _startTokenId();
                      }
                  }
                  /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                  function nextTokenIdToMint() external view returns (uint256) {
                      return nextTokenIdToLazyMint;
                  }
                  /// @dev The next token ID of the NFT that can be claimed.
                  function nextTokenIdToClaim() external view returns (uint256) {
                      return _currentIndex;
                  }
                  /// @dev Burns `tokenId`. See {ERC721-_burn}.
                  function burn(uint256 tokenId) external virtual {
                      // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals.
                      _burn(tokenId, true);
                  }
                  /// @dev See {ERC721-_beforeTokenTransfer}.
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual override {
                      super._beforeTokenTransfers(from, to, startTokenId, quantity);
                      // if transfer is restricted on the contract, we still want to allow burning and minting
                      if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                          if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) {
                              revert("!Transfer-Role");
                          }
                      }
                  }
                  /// @dev See {ERC721-setApprovalForAll}.
                  function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
                      super.setApprovalForAll(operator, approved);
                  }
                  /// @dev See {ERC721-approve}.
                  function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
                      super.approve(operator, tokenId);
                  }
                  /// @dev See {ERC721-_transferFrom}.
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.transferFrom(from, to, tokenId);
                  }
                  /// @dev See {ERC721-_safeTransferFrom}.
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId);
                  }
                  /// @dev See {ERC721-_safeTransferFrom}.
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId, data);
                  }
                  function _dropMsgSender() internal view virtual override returns (address) {
                      return _msgSender();
                  }
                  function _msgSender()
                      internal
                      view
                      virtual
                      override(ContextUpgradeable, ERC2771ContextUpgradeable)
                      returns (address sender)
                  {
                      return ERC2771ContextUpgradeable._msgSender();
                  }
                  function _msgData()
                      internal
                      view
                      virtual
                      override(ContextUpgradeable, ERC2771ContextUpgradeable)
                      returns (bytes calldata)
                  {
                      return ERC2771ContextUpgradeable._msgData();
                  }
              }
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              ////////// CHANGELOG: turn `approve` to virtual //////////
              pragma solidity ^0.8.4;
              import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
               * the Metadata extension. Built to optimize for lower gas during batch mints.
               *
               * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
               *
               * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
               *
               * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
               */
              contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721AUpgradeable {
                  using AddressUpgradeable for address;
                  using StringsUpgradeable for uint256;
                  // The tokenId of the next token to be minted.
                  uint256 internal _currentIndex;
                  // The number of tokens burned.
                  uint256 internal _burnCounter;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to ownership details
                  // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
                  mapping(uint256 => TokenOwnership) internal _ownerships;
                  // Mapping owner address to address data
                  mapping(address => AddressData) private _addressData;
                  // 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;
                  function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializing {
                      __ERC721A_init_unchained(name_, symbol_);
                  }
                  function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                      _name = name_;
                      _symbol = symbol_;
                      _currentIndex = _startTokenId();
                  }
                  /**
                   * To change the starting tokenId, please override this function.
                   */
                  function _startTokenId() internal view virtual returns (uint256) {
                      return 0;
                  }
                  /**
                   * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
                   */
                  function totalSupply() public view override returns (uint256) {
                      // Counter underflow is impossible as _burnCounter cannot be incremented
                      // more than _currentIndex - _startTokenId() times
                      unchecked {
                          return _currentIndex - _burnCounter - _startTokenId();
                      }
                  }
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function _totalMinted() internal view returns (uint256) {
                      // Counter underflow is impossible as _currentIndex does not decrement,
                      // and it is initialized to _startTokenId()
                      unchecked {
                          return _currentIndex - _startTokenId();
                      }
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC165Upgradeable, IERC165Upgradeable)
                      returns (bool)
                  {
                      return
                          interfaceId == type(IERC721Upgradeable).interfaceId ||
                          interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view override returns (uint256) {
                      if (owner == address(0)) revert BalanceQueryForZeroAddress();
                      return uint256(_addressData[owner].balance);
                  }
                  /**
                   * Returns the number of tokens minted by `owner`.
                   */
                  function _numberMinted(address owner) internal view returns (uint256) {
                      return uint256(_addressData[owner].numberMinted);
                  }
                  /**
                   * Returns the number of tokens burned by or on behalf of `owner`.
                   */
                  function _numberBurned(address owner) internal view returns (uint256) {
                      return uint256(_addressData[owner].numberBurned);
                  }
                  /**
                   * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                   */
                  function _getAux(address owner) internal view returns (uint64) {
                      return _addressData[owner].aux;
                  }
                  /**
                   * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                   * If there are multiple variables, please pack them into a uint64.
                   */
                  function _setAux(address owner, uint64 aux) internal {
                      _addressData[owner].aux = aux;
                  }
                  /**
                   * Gas spent here starts off proportional to the maximum mint batch size.
                   * It gradually moves to O(1) as tokens get transferred around in the collection over time.
                   */
                  function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < _currentIndex) {
                                  TokenOwnership memory ownership = _ownerships[curr];
                                  if (!ownership.burned) {
                                      if (ownership.addr != address(0)) {
                                          return ownership;
                                      }
                                      // Invariant:
                                      // There will always be an ownership that has an address and is not burned
                                      // before an ownership that does not have an address and is not burned.
                                      // Hence, curr will not underflow.
                                      while (true) {
                                          curr--;
                                          ownership = _ownerships[curr];
                                          if (ownership.addr != address(0)) {
                                              return ownership;
                                          }
                                      }
                                  }
                              }
                      }
                      revert OwnerQueryForNonexistentToken();
                  }
                  /**
                   * @dev See {IERC721-ownerOf}.
                   */
                  function ownerOf(uint256 tokenId) public view override returns (address) {
                      return _ownershipOf(tokenId).addr;
                  }
                  /**
                   * @dev See {IERC721Metadata-name}.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC721Metadata-tokenURI}.
                   */
                  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                      if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                      string memory baseURI = _baseURI();
                      return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                  }
                  /**
                   * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                   * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                   * by default, can be overriden in child contracts.
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return "";
                  }
                  /**
                   * @dev See {IERC721-approve}.
                   */
                  function approve(address to, uint256 tokenId) public virtual override {
                      address owner = ERC721AUpgradeable.ownerOf(tokenId);
                      if (to == owner) revert ApprovalToCurrentOwner();
                      if (_msgSender() != owner)
                          if (!isApprovedForAll(owner, _msgSender())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                      _approve(to, tokenId, owner);
                  }
                  /**
                   * @dev See {IERC721-getApproved}.
                   */
                  function getApproved(uint256 tokenId) public view override returns (address) {
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return _tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      if (operator == _msgSender()) revert ApproveToCaller();
                      _operatorApprovals[_msgSender()][operator] = approved;
                      emit ApprovalForAll(_msgSender(), operator, approved);
                  }
                  /**
                   * @dev See {IERC721-isApprovedForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public virtual override {
                      _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 {
                      _transfer(from, to, tokenId);
                      if (to.isContract())
                          if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                              revert TransferToNonERC721ReceiverImplementer();
                          }
                  }
                  /**
                   * @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`),
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
                  }
                  /**
                   * @dev Equivalent to `_safeMint(to, quantity, '')`.
                   */
                  function _safeMint(address to, uint256 quantity) internal {
                      _safeMint(to, quantity, "");
                  }
                  /**
                   * @dev Safely mints `quantity` tokens and transfers them to `to`.
                   *
                   * Requirements:
                   *
                   * - If `to` refers to a smart contract, it must implement
                   *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
                   * - `quantity` must be greater than 0.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeMint(
                      address to,
                      uint256 quantity,
                      bytes memory _data
                  ) internal {
                      uint256 startTokenId = _currentIndex;
                      if (to == address(0)) revert MintToZeroAddress();
                      if (quantity == 0) revert MintZeroQuantity();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are incredibly unrealistic.
                      // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                      // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                      unchecked {
                          _addressData[to].balance += uint64(quantity);
                          _addressData[to].numberMinted += uint64(quantity);
                          _ownerships[startTokenId].addr = to;
                          _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          if (to.isContract()) {
                              do {
                                  emit Transfer(address(0), to, updatedIndex);
                                  if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) {
                                      revert TransferToNonERC721ReceiverImplementer();
                                  }
                              } while (updatedIndex < end);
                              // Reentrancy protection
                              if (_currentIndex != startTokenId) revert();
                          } else {
                              do {
                                  emit Transfer(address(0), to, updatedIndex++);
                              } while (updatedIndex < end);
                          }
                          _currentIndex = updatedIndex;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, quantity);
                  }
                  /**
                   * @dev Mints `quantity` tokens and transfers them to `to`.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `quantity` must be greater than 0.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _mint(address to, uint256 quantity) internal {
                      uint256 startTokenId = _currentIndex;
                      if (to == address(0)) revert MintToZeroAddress();
                      if (quantity == 0) revert MintZeroQuantity();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are incredibly unrealistic.
                      // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                      // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                      unchecked {
                          _addressData[to].balance += uint64(quantity);
                          _addressData[to].numberMinted += uint64(quantity);
                          _ownerships[startTokenId].addr = to;
                          _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          do {
                              emit Transfer(address(0), to, updatedIndex++);
                          } while (updatedIndex < end);
                          _currentIndex = updatedIndex;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, quantity);
                  }
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *
                   * 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
                  ) private {
                      TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                      if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
                      bool isApprovedOrOwner = (_msgSender() == from ||
                          isApprovedForAll(from, _msgSender()) ||
                          getApproved(tokenId) == _msgSender());
                      if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                      if (to == address(0)) revert TransferToZeroAddress();
                      _beforeTokenTransfers(from, to, tokenId, 1);
                      // Clear approvals from the previous owner
                      _approve(address(0), tokenId, from);
                      // Underflow of the sender's balance is impossible because we check for
                      // ownership above and the recipient's balance can't realistically overflow.
                      // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                      unchecked {
                          _addressData[from].balance -= 1;
                          _addressData[to].balance += 1;
                          TokenOwnership storage currSlot = _ownerships[tokenId];
                          currSlot.addr = to;
                          currSlot.startTimestamp = uint64(block.timestamp);
                          // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
                          // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                          uint256 nextTokenId = tokenId + 1;
                          TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                          if (nextSlot.addr == address(0)) {
                              // This will suffice for checking _exists(nextTokenId),
                              // as a burned slot cannot contain the zero address.
                              if (nextTokenId != _currentIndex) {
                                  nextSlot.addr = from;
                                  nextSlot.startTimestamp = prevOwnership.startTimestamp;
                              }
                          }
                      }
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfers(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Equivalent to `_burn(tokenId, false)`.
                   */
                  function _burn(uint256 tokenId) internal virtual {
                      _burn(tokenId, false);
                  }
                  /**
                   * @dev Destroys `tokenId`.
                   * The approval is cleared when the token is burned.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                      TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                      address from = prevOwnership.addr;
                      if (approvalCheck) {
                          bool isApprovedOrOwner = (_msgSender() == from ||
                              isApprovedForAll(from, _msgSender()) ||
                              getApproved(tokenId) == _msgSender());
                          if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                      }
                      _beforeTokenTransfers(from, address(0), tokenId, 1);
                      // Clear approvals from the previous owner
                      _approve(address(0), tokenId, from);
                      // Underflow of the sender's balance is impossible because we check for
                      // ownership above and the recipient's balance can't realistically overflow.
                      // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                      unchecked {
                          AddressData storage addressData = _addressData[from];
                          addressData.balance -= 1;
                          addressData.numberBurned += 1;
                          // Keep track of who burned the token, and the timestamp of burning.
                          TokenOwnership storage currSlot = _ownerships[tokenId];
                          currSlot.addr = from;
                          currSlot.startTimestamp = uint64(block.timestamp);
                          currSlot.burned = true;
                          // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
                          // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                          uint256 nextTokenId = tokenId + 1;
                          TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                          if (nextSlot.addr == address(0)) {
                              // This will suffice for checking _exists(nextTokenId),
                              // as a burned slot cannot contain the zero address.
                              if (nextTokenId != _currentIndex) {
                                  nextSlot.addr = from;
                                  nextSlot.startTimestamp = prevOwnership.startTimestamp;
                              }
                          }
                      }
                      emit Transfer(from, address(0), tokenId);
                      _afterTokenTransfers(from, address(0), tokenId, 1);
                      // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                      unchecked {
                          _burnCounter++;
                      }
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits a {Approval} event.
                   */
                  function _approve(
                      address to,
                      uint256 tokenId,
                      address owner
                  ) private {
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
                  /**
                   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target 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 _checkContractOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) private returns (bool) {
                      try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (
                          bytes4 retval
                      ) {
                          return retval == IERC721ReceiverUpgradeable(to).onERC721Received.selector;
                      } catch (bytes memory reason) {
                          if (reason.length == 0) {
                              revert TransferToNonERC721ReceiverImplementer();
                          } else {
                              assembly {
                                  revert(add(32, reason), mload(reason))
                              }
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
                   * And also called before burning one token.
                   *
                   * startTokenId - the first token id to be transferred
                   * quantity - the amount to be transferred
                   *
                   * 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, `tokenId` will be burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
                   * minting.
                   * And also called after one token has been burned.
                   *
                   * startTokenId - the first token id to be transferred
                   * quantity - the amount to be transferred
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                   * transferred to `to`.
                   * - When `from` is zero, `tokenId` has been minted for `to`.
                   * - When `to` is zero, `tokenId` has been burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _afterTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[42] private __gap;
              }
              // 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
               * [EIP](https://eips.ethereum.org/EIPS/eip-165).
               *
               * 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
                   * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                   * 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: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              interface IERC20 {
                  function totalSupply() external view returns (uint256);
                  function balanceOf(address who) external view returns (uint256);
                  function allowance(address owner, address spender) external view returns (uint256);
                  function transfer(address to, uint256 value) external returns (bool);
                  function approve(address spender, uint256 value) external returns (bool);
                  function transferFrom(
                      address from,
                      address to,
                      uint256 value
                  ) external returns (bool);
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: Apache 2.0
              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 payed in that same unit of exchange.
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice)
                      external
                      view
                      returns (address receiver, uint256 royaltyAmount);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  @title   Batch-mint Metadata
               *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
               *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
               *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
               */
              contract BatchMintMetadata {
                  /// @dev Largest tokenId of each batch of tokens with the same baseURI.
                  uint256[] private batchIds;
                  /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                  mapping(uint256 => string) private baseURI;
                  /**
                   *  @notice         Returns the count of batches of NFTs.
                   *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                   *                  See {batchIds}.
                   */
                  function getBaseURICount() public view returns (uint256) {
                      return batchIds.length;
                  }
                  /**
                   *  @notice         Returns the ID for the batch of tokens the given tokenId belongs to.
                   *  @dev            See {getBaseURICount}.
                   *  @param _index   ID of a token.
                   */
                  function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                      if (_index >= getBaseURICount()) {
                          revert("Invalid index");
                      }
                      return batchIds[_index];
                  }
                  /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                  function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                      uint256 numOfTokenBatches = getBaseURICount();
                      uint256[] memory indices = batchIds;
                      for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                          if (_tokenId < indices[i]) {
                              index = i;
                              batchId = indices[i];
                              return (batchId, index);
                          }
                      }
                      revert("Invalid tokenId");
                  }
                  /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                  function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                      uint256 numOfTokenBatches = getBaseURICount();
                      uint256[] memory indices = batchIds;
                      for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                          if (_tokenId < indices[i]) {
                              return baseURI[indices[i]];
                          }
                      }
                      revert("Invalid tokenId");
                  }
                  /// @dev Sets the base URI for the batch of tokens with the given batchId.
                  function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                      baseURI[_batchId] = _baseURI;
                  }
                  /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                  function _batchMintMetadata(
                      uint256 _startId,
                      uint256 _amountToMint,
                      string memory _baseURIForTokens
                  ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                      batchId = _startId + _amountToMint;
                      nextTokenIdToMint = batchId;
                      batchIds.push(batchId);
                      baseURI[batchId] = _baseURIForTokens;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IContractMetadata.sol";
              /**
               *  @title   Contract Metadata
               *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
               *           for you contract.
               *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
               */
              abstract contract ContractMetadata is IContractMetadata {
                  /// @notice Returns the contract metadata URI.
                  string public override contractURI;
                  /**
                   *  @notice         Lets a contract admin set the URI for contract-level metadata.
                   *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                   *                  See {_canSetContractURI}.
                   *                  Emits {ContractURIUpdated Event}.
                   *
                   *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   */
                  function setContractURI(string memory _uri) external override {
                      if (!_canSetContractURI()) {
                          revert("Not authorized");
                      }
                      _setupContractURI(_uri);
                  }
                  /// @dev Lets a contract admin set the URI for contract-level metadata.
                  function _setupContractURI(string memory _uri) internal {
                      string memory prevURI = contractURI;
                      contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
                  /// @dev Returns whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import { OperatorFiltererUpgradeable } from "./OperatorFiltererUpgradeable.sol";
              abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                  address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                  function __DefaultOperatorFilterer_init() internal {
                      OperatorFiltererUpgradeable.__OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IDelayedReveal.sol";
              /**
               *  @title   Delayed Reveal
               *  @notice  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
               *           'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
               */
              abstract contract DelayedReveal is IDelayedReveal {
                  /// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.
                  mapping(uint256 => bytes) public encryptedData;
                  /// @dev Sets the delayed reveal data for a batchId.
                  function _setEncryptedData(uint256 _batchId, bytes memory _encryptedData) internal {
                      encryptedData[_batchId] = _encryptedData;
                  }
                  /**
                   *  @notice             Returns revealed URI for a batch of NFTs.
                   *  @dev                Reveal encrypted base URI for `_batchId` with caller/admin's `_key` used for encryption.
                   *                      Reverts if there's no encrypted URI for `_batchId`.
                   *                      See {encryptDecrypt}.
                   *
                   *  @param _batchId     ID of the batch for which URI is being revealed.
                   *  @param _key         Secure key used by caller/admin for encryption of baseURI.
                   *
                   *  @return revealedURI Decrypted base URI.
                   */
                  function getRevealURI(uint256 _batchId, bytes calldata _key) public view returns (string memory revealedURI) {
                      bytes memory data = encryptedData[_batchId];
                      if (data.length == 0) {
                          revert("Nothing to reveal");
                      }
                      (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(data, (bytes, bytes32));
                      revealedURI = string(encryptDecrypt(encryptedURI, _key));
                      require(keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) == provenanceHash, "Incorrect key");
                  }
                  /**
                   *  @notice         Encrypt/decrypt data on chain.
                   *  @dev            Encrypt/decrypt given `data` with `key`. Uses inline assembly.
                   *                  See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
                   *
                   *  @param data     Bytes of data to encrypt/decrypt.
                   *  @param key      Secure key used by caller for encryption/decryption.
                   *
                   *  @return result  Output after encryption/decryption of given data.
                   */
                  function encryptDecrypt(bytes memory data, bytes calldata key) public pure override returns (bytes memory result) {
                      // Store data length on stack for later use
                      uint256 length = data.length;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Set result to free memory pointer
                          result := mload(0x40)
                          // Increase free memory pointer by lenght + 32
                          mstore(0x40, add(add(result, length), 32))
                          // Set result length
                          mstore(result, length)
                      }
                      // Iterate over the data stepping by 32 bytes
                      for (uint256 i = 0; i < length; i += 32) {
                          // Generate hash of the key and offset
                          bytes32 hash = keccak256(abi.encodePacked(key, i));
                          bytes32 chunk;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              // Read 32-bytes data chunk
                              chunk := mload(add(data, add(i, 32)))
                          }
                          // XOR the chunk with hash
                          chunk ^= hash;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              // Write 32-byte encrypted chunk
                              mstore(add(result, add(i, 32)), chunk)
                          }
                      }
                  }
                  /**
                   *  @notice         Returns whether the relvant batch of NFTs is subject to a delayed reveal.
                   *  @dev            Returns `true` if `_batchId`'s base URI is encrypted.
                   *  @param _batchId ID of a batch of NFTs.
                   */
                  function isEncryptedBatch(uint256 _batchId) public view returns (bool) {
                      return encryptedData[_batchId].length > 0;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IDrop.sol";
              import "../lib/MerkleProof.sol";
              abstract contract Drop is IDrop {
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @dev The active conditions for claiming tokens.
                  ClaimConditionList public claimCondition;
                  /*///////////////////////////////////////////////////////////////
                                          Drop logic
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Lets an account claim tokens.
                  function claim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) public payable virtual override {
                      _beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                      uint256 activeConditionId = getActiveClaimConditionId();
                      verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof);
                      // Update contract state.
                      claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
                      claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                      // If there's a price, collect price.
                      _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken);
                      // Mint the relevant tokens to claimer.
                      uint256 startTokenId = _transferTokensOnClaim(_receiver, _quantity);
                      emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
                      _afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                  }
                  /// @dev Lets a contract admin set claim conditions.
                  function setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility)
                      external
                      virtual
                      override
                  {
                      if (!_canSetClaimConditions()) {
                          revert("Not authorized");
                      }
                      uint256 existingStartIndex = claimCondition.currentStartId;
                      uint256 existingPhaseCount = claimCondition.count;
                      /**
                       *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                       *
                       *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                       *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                       *  by `supplyClaimedByWallet`.
                       */
                      uint256 newStartIndex = existingStartIndex;
                      if (_resetClaimEligibility) {
                          newStartIndex = existingStartIndex + existingPhaseCount;
                      }
                      claimCondition.count = _conditions.length;
                      claimCondition.currentStartId = newStartIndex;
                      uint256 lastConditionStartTimestamp;
                      for (uint256 i = 0; i < _conditions.length; i++) {
                          require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                          uint256 supplyClaimedAlready = claimCondition.conditions[newStartIndex + i].supplyClaimed;
                          if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                              revert("max supply claimed");
                          }
                          claimCondition.conditions[newStartIndex + i] = _conditions[i];
                          claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                          lastConditionStartTimestamp = _conditions[i].startTimestamp;
                      }
                      /**
                       *  Gas refunds (as much as possible)
                       *
                       *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                       *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                       *
                       *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                       *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                       *  by the conditions in `_conditions`.
                       */
                      if (_resetClaimEligibility) {
                          for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                              delete claimCondition.conditions[i];
                          }
                      } else {
                          if (existingPhaseCount > _conditions.length) {
                              for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                  delete claimCondition.conditions[newStartIndex + i];
                              }
                          }
                      }
                      emit ClaimConditionsUpdated(_conditions, _resetClaimEligibility);
                  }
                  /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                  function verifyClaim(
                      uint256 _conditionId,
                      address _claimer,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof
                  ) public view returns (bool isOverride) {
                      ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId];
                      uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                      uint256 claimPrice = currentClaimPhase.pricePerToken;
                      address claimCurrency = currentClaimPhase.currency;
                      if (currentClaimPhase.merkleRoot != bytes32(0)) {
                          (isOverride, ) = MerkleProof.verify(
                              _allowlistProof.proof,
                              currentClaimPhase.merkleRoot,
                              keccak256(
                                  abi.encodePacked(
                                      _claimer,
                                      _allowlistProof.quantityLimitPerWallet,
                                      _allowlistProof.pricePerToken,
                                      _allowlistProof.currency
                                  )
                              )
                          );
                      }
                      if (isOverride) {
                          claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                              ? _allowlistProof.quantityLimitPerWallet
                              : claimLimit;
                          claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                              ? _allowlistProof.pricePerToken
                              : claimPrice;
                          claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                              ? _allowlistProof.currency
                              : claimCurrency;
                      }
                      uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                      if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                          revert("!PriceOrCurrency");
                      }
                      if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                          revert("!Qty");
                      }
                      if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                          revert("!MaxSupply");
                      }
                      if (currentClaimPhase.startTimestamp > block.timestamp) {
                          revert("cant claim yet");
                      }
                  }
                  /// @dev At any given moment, returns the uid for the active claim condition.
                  function getActiveClaimConditionId() public view returns (uint256) {
                      for (uint256 i = claimCondition.currentStartId + claimCondition.count; i > claimCondition.currentStartId; i--) {
                          if (block.timestamp >= claimCondition.conditions[i - 1].startTimestamp) {
                              return i - 1;
                          }
                      }
                      revert("!CONDITION.");
                  }
                  /// @dev Returns the claim condition at the given uid.
                  function getClaimConditionById(uint256 _conditionId) external view returns (ClaimCondition memory condition) {
                      condition = claimCondition.conditions[_conditionId];
                  }
                  /// @dev Returns the supply claimed by claimer for a given conditionId.
                  function getSupplyClaimedByWallet(uint256 _conditionId, address _claimer)
                      public
                      view
                      returns (uint256 supplyClaimedByWallet)
                  {
                      supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                  }
                  /*////////////////////////////////////////////////////////////////////
                      Optional hooks that can be implemented in the derived contract
                  ///////////////////////////////////////////////////////////////////*/
                  /// @dev Exposes the ability to override the msg sender.
                  function _dropMsgSender() internal virtual returns (address) {
                      return msg.sender;
                  }
                  /// @dev Runs before every `claim` function call.
                  function _beforeClaim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) internal virtual {}
                  /// @dev Runs after every `claim` function call.
                  function _afterClaim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) internal virtual {}
                  /*///////////////////////////////////////////////////////////////
                      Virtual functions: to be implemented in derived contract
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                  function _collectPriceOnClaim(
                      address _primarySaleRecipient,
                      uint256 _quantityToClaim,
                      address _currency,
                      uint256 _pricePerToken
                  ) internal virtual;
                  /// @dev Transfers the NFTs being claimed.
                  function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed)
                      internal
                      virtual
                      returns (uint256 startTokenId);
                  /// @dev Determine what wallet can update claim conditions
                  function _canSetClaimConditions() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/ILazyMint.sol";
              import "./BatchMintMetadata.sol";
              /**
               *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
               *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
               *  minting a non-zero balance of NFTs of those tokenIds.
               */
              abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                  /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                  uint256 internal nextTokenIdToLazyMint;
                  /**
                   *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                   *
                   *  @param _amount           The number of NFTs to lazy mint.
                   *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                   *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                   *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                   *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                   */
                  function lazyMint(
                      uint256 _amount,
                      string calldata _baseURIForTokens,
                      bytes calldata _data
                  ) public virtual override returns (uint256 batchId) {
                      if (!_canLazyMint()) {
                          revert("Not authorized");
                      }
                      if (_amount == 0) {
                          revert("0 amt");
                      }
                      uint256 startId = nextTokenIdToLazyMint;
                      (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                      emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                      return batchId;
                  }
                  /// @dev Returns whether lazy minting can be performed in the given execution context.
                  function _canLazyMint() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "./interface/IOperatorFilterToggle.sol";
              abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                  bool public operatorRestriction;
                  function setOperatorRestriction(bool _restriction) external {
                      require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                      _setOperatorRestriction(_restriction);
                  }
                  function _setOperatorRestriction(bool _restriction) internal {
                      operatorRestriction = _restriction;
                      emit OperatorRestriction(_restriction);
                  }
                  function _canSetOperatorRestriction() internal virtual returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import "./interface/IOperatorFilterRegistry.sol";
              import "./OperatorFilterToggle.sol";
              abstract contract OperatorFiltererUpgradeable is OperatorFilterToggle {
                  error OperatorNotAllowed(address operator);
                  IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                  function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) internal {
                      // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                      // will not revert, but the contract will need to be registered with the registry once it is deployed in
                      // order for the modifier to filter addresses.
                      if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                          if (!OPERATOR_FILTER_REGISTRY.isRegistered(address(this))) {
                              if (subscribe) {
                                  OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                              } else {
                                  if (subscriptionOrRegistrantToCopy != address(0)) {
                                      OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                                  } else {
                                      OPERATOR_FILTER_REGISTRY.register(address(this));
                                  }
                              }
                          }
                      }
                  }
                  modifier onlyAllowedOperator(address from) virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (operatorRestriction) {
                          if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                              // Allow spending tokens from addresses with balance
                              // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                              // from an EOA.
                              if (from == msg.sender) {
                                  _;
                                  return;
                              }
                              if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), msg.sender)) {
                                  revert OperatorNotAllowed(msg.sender);
                              }
                          }
                      }
                      _;
                  }
                  modifier onlyAllowedOperatorApproval(address operator) virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (operatorRestriction) {
                          if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                              if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                                  revert OperatorNotAllowed(operator);
                              }
                          }
                      }
                      _;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IOwnable.sol";
              /**
               *  @title   Ownable
               *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
               *           information about who the contract's owner is.
               */
              abstract contract Ownable is IOwnable {
                  /// @dev Owner of the contract (purpose: OpenSea compatibility)
                  address private _owner;
                  /// @dev Reverts if caller is not the owner.
                  modifier onlyOwner() {
                      if (msg.sender != _owner) {
                          revert("Not authorized");
                      }
                      _;
                  }
                  /**
                   *  @notice Returns the owner of the contract.
                   */
                  function owner() public view override returns (address) {
                      return _owner;
                  }
                  /**
                   *  @notice Lets an authorized wallet set a new owner for the contract.
                   *  @param _newOwner The address to set as the new owner of the contract.
                   */
                  function setOwner(address _newOwner) external override {
                      if (!_canSetOwner()) {
                          revert("Not authorized");
                      }
                      _setupOwner(_newOwner);
                  }
                  /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                  function _setupOwner(address _newOwner) internal {
                      address _prevOwner = _owner;
                      _owner = _newOwner;
                      emit OwnerUpdated(_prevOwner, _newOwner);
                  }
                  /// @dev Returns whether owner can be set in the given execution context.
                  function _canSetOwner() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPermissions.sol";
              import "../lib/TWStrings.sol";
              /**
               *  @title   Permissions
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
               */
              contract Permissions is IPermissions {
                  /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                  mapping(bytes32 => mapping(address => bool)) private _hasRole;
                  /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                  mapping(bytes32 => bytes32) private _getRoleAdmin;
                  /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role, msg.sender);
                      _;
                  }
                  /**
                   *  @notice         Checks whether an account has a particular role.
                   *  @dev            Returns `true` if `account` has been granted `role`.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account for which the role is being checked.
                   */
                  function hasRole(bytes32 role, address account) public view override returns (bool) {
                      return _hasRole[role][account];
                  }
                  /**
                   *  @notice         Checks whether an account has a particular role;
                   *                  role restrictions can be swtiched on and off.
                   *
                   *  @dev            Returns `true` if `account` has been granted `role`.
                   *                  Role restrictions can be swtiched on and off:
                   *                      - If address(0) has ROLE, then the ROLE restrictions
                   *                        don't apply.
                   *                      - If address(0) does not have ROLE, then the ROLE
                   *                        restrictions will apply.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account for which the role is being checked.
                   */
                  function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                      if (!_hasRole[role][address(0)]) {
                          return _hasRole[role][account];
                      }
                      return true;
                  }
                  /**
                   *  @notice         Returns the admin role that controls the specified role.
                   *  @dev            See {grantRole} and {revokeRole}.
                   *                  To change a role's admin, use {_setRoleAdmin}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   */
                  function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                      return _getRoleAdmin[role];
                  }
                  /**
                   *  @notice         Grants a role to an account, if not previously granted.
                   *  @dev            Caller must have admin role for the `role`.
                   *                  Emits {RoleGranted Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account to which the role is being granted.
                   */
                  function grantRole(bytes32 role, address account) public virtual override {
                      _checkRole(_getRoleAdmin[role], msg.sender);
                      if (_hasRole[role][account]) {
                          revert("Can only grant to non holders");
                      }
                      _setupRole(role, account);
                  }
                  /**
                   *  @notice         Revokes role from an account.
                   *  @dev            Caller must have admin role for the `role`.
                   *                  Emits {RoleRevoked Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account from which the role is being revoked.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override {
                      _checkRole(_getRoleAdmin[role], msg.sender);
                      _revokeRole(role, account);
                  }
                  /**
                   *  @notice         Revokes role from the account.
                   *  @dev            Caller must have the `role`, with caller being the same as `account`.
                   *                  Emits {RoleRevoked Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account from which the role is being revoked.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      if (msg.sender != account) {
                          revert("Can only renounce for self");
                      }
                      _revokeRole(role, account);
                  }
                  /// @dev Sets `adminRole` as `role`'s admin role.
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = _getRoleAdmin[role];
                      _getRoleAdmin[role] = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _hasRole[role][account] = true;
                      emit RoleGranted(role, account, msg.sender);
                  }
                  /// @dev Revokes `role` from `account`
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      _checkRole(role, account);
                      delete _hasRole[role][account];
                      emit RoleRevoked(role, account, msg.sender);
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!_hasRole[role][account]) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "Permissions: account ",
                                      TWStrings.toHexString(uint160(account), 20),
                                      " is missing role ",
                                      TWStrings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                      if (!hasRoleWithSwitch(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "Permissions: account ",
                                      TWStrings.toHexString(uint160(account), 20),
                                      " is missing role ",
                                      TWStrings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPermissionsEnumerable.sol";
              import "./Permissions.sol";
              /**
               *  @title   PermissionsEnumerable
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
               *           Also provides interfaces to view all members with a given role, and total count of members.
               */
              contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                  /**
                   *  @notice A data structure to store data of members for a given role.
                   *
                   *  @param index    Current index in the list of accounts that have a role.
                   *  @param members  map from index => address of account that has a role
                   *  @param indexOf  map from address => index which the account has.
                   */
                  struct RoleMembers {
                      uint256 index;
                      mapping(uint256 => address) members;
                      mapping(address => uint256) indexOf;
                  }
                  /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                  mapping(bytes32 => RoleMembers) private roleMembers;
                  /**
                   *  @notice         Returns the role-member from a list of members for a role,
                   *                  at a given index.
                   *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                   *                  See struct {RoleMembers}, and mapping {roleMembers}
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param index    Index in list of current members for the role.
                   *
                   *  @return member  Address of account that has `role`
                   */
                  function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                      uint256 currentIndex = roleMembers[role].index;
                      uint256 check;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (roleMembers[role].members[i] != address(0)) {
                              if (check == index) {
                                  member = roleMembers[role].members[i];
                                  return member;
                              }
                              check += 1;
                          } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                              check += 1;
                          }
                      }
                  }
                  /**
                   *  @notice         Returns total number of accounts that have a role.
                   *  @dev            Returns `count` of accounts that have `role`.
                   *                  See struct {RoleMembers}, and mapping {roleMembers}
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *
                   *  @return count   Total number of accounts that have `role`
                   */
                  function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                      uint256 currentIndex = roleMembers[role].index;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (roleMembers[role].members[i] != address(0)) {
                              count += 1;
                          }
                      }
                      if (hasRole(role, address(0))) {
                          count += 1;
                      }
                  }
                  /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                  ///      See {_removeMember}
                  function _revokeRole(bytes32 role, address account) internal override {
                      super._revokeRole(role, account);
                      _removeMember(role, account);
                  }
                  /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                  ///      See {_addMember}
                  function _setupRole(bytes32 role, address account) internal override {
                      super._setupRole(role, account);
                      _addMember(role, account);
                  }
                  /// @dev adds `account` to {roleMembers}, for `role`
                  function _addMember(bytes32 role, address account) internal {
                      uint256 idx = roleMembers[role].index;
                      roleMembers[role].index += 1;
                      roleMembers[role].members[idx] = account;
                      roleMembers[role].indexOf[account] = idx;
                  }
                  /// @dev removes `account` from {roleMembers}, for `role`
                  function _removeMember(bytes32 role, address account) internal {
                      uint256 idx = roleMembers[role].indexOf[account];
                      delete roleMembers[role].members[idx];
                      delete roleMembers[role].indexOf[account];
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPlatformFee.sol";
              /**
               *  @title   Platform Fee
               *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
               *           that uses information about platform fees, if desired.
               */
              abstract contract PlatformFee is IPlatformFee {
                  /// @dev The address that receives all platform fees from all sales.
                  address private platformFeeRecipient;
                  /// @dev The % of primary sales collected as platform fees.
                  uint16 private platformFeeBps;
                  /// @dev Returns the platform fee recipient and bps.
                  function getPlatformFeeInfo() public view override returns (address, uint16) {
                      return (platformFeeRecipient, uint16(platformFeeBps));
                  }
                  /**
                   *  @notice         Updates the platform fee recipient and bps.
                   *  @dev            Caller should be authorized to set platform fee info.
                   *                  See {_canSetPlatformFeeInfo}.
                   *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                   *
                   *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                   *  @param _platformFeeBps         Updated platformFeeBps.
                   */
                  function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                      if (!_canSetPlatformFeeInfo()) {
                          revert("Not authorized");
                      }
                      _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                  }
                  /// @dev Lets a contract admin update the platform fee recipient and bps
                  function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                      if (_platformFeeBps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      platformFeeBps = uint16(_platformFeeBps);
                      platformFeeRecipient = _platformFeeRecipient;
                      emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                  }
                  /// @dev Returns whether platform fee info can be set in the given execution context.
                  function _canSetPlatformFeeInfo() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPrimarySale.sol";
              /**
               *  @title   Primary Sale
               *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
               *           primary sales, if desired.
               */
              abstract contract PrimarySale is IPrimarySale {
                  /// @dev The address that receives all primary sales value.
                  address private recipient;
                  /// @dev Returns primary sale recipient address.
                  function primarySaleRecipient() public view override returns (address) {
                      return recipient;
                  }
                  /**
                   *  @notice         Updates primary sale recipient.
                   *  @dev            Caller should be authorized to set primary sales info.
                   *                  See {_canSetPrimarySaleRecipient}.
                   *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                   *
                   *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                   */
                  function setPrimarySaleRecipient(address _saleRecipient) external override {
                      if (!_canSetPrimarySaleRecipient()) {
                          revert("Not authorized");
                      }
                      _setupPrimarySaleRecipient(_saleRecipient);
                  }
                  /// @dev Lets a contract admin set the recipient for all primary sales.
                  function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                      recipient = _saleRecipient;
                      emit PrimarySaleRecipientUpdated(_saleRecipient);
                  }
                  /// @dev Returns whether primary sale recipient can be set in the given execution context.
                  function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IRoyalty.sol";
              /**
               *  @title   Royalty
               *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
               *           that uses information about royalty fees, if desired.
               *
               *  @dev     The `Royalty` contract is ERC2981 compliant.
               */
              abstract contract Royalty is IRoyalty {
                  /// @dev The (default) address that receives all royalty value.
                  address private royaltyRecipient;
                  /// @dev The (default) % of a sale to take as royalty (in basis points).
                  uint16 private royaltyBps;
                  /// @dev Token ID => royalty recipient and bps for token
                  mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                  /**
                   *  @notice   View royalty info for a given token and sale price.
                   *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                   *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                   *  @param salePrice        Sale price of the token.
                   *
                   *  @return receiver        Address of royalty recipient account.
                   *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice)
                      external
                      view
                      virtual
                      override
                      returns (address receiver, uint256 royaltyAmount)
                  {
                      (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                      receiver = recipient;
                      royaltyAmount = (salePrice * bps) / 10_000;
                  }
                  /**
                   *  @notice          View royalty info for a given token.
                   *  @dev             Returns royalty recipient and bps for `_tokenId`.
                   *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                   */
                  function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                      RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                      return
                          royaltyForToken.recipient == address(0)
                              ? (royaltyRecipient, uint16(royaltyBps))
                              : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                  }
                  /**
                   *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                   */
                  function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                      return (royaltyRecipient, uint16(royaltyBps));
                  }
                  /**
                   *  @notice         Updates default royalty recipient and bps.
                   *  @dev            Caller should be authorized to set royalty info.
                   *                  See {_canSetRoyaltyInfo}.
                   *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                   *
                   *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                   *  @param _royaltyBps         Updated royalty bps.
                   */
                  function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                      if (!_canSetRoyaltyInfo()) {
                          revert("Not authorized");
                      }
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                  }
                  /// @dev Lets a contract admin update the default royalty recipient and bps.
                  function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                      if (_royaltyBps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      royaltyRecipient = _royaltyRecipient;
                      royaltyBps = uint16(_royaltyBps);
                      emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                  }
                  /**
                   *  @notice         Updates default royalty recipient and bps for a particular token.
                   *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                   *                  See {_canSetRoyaltyInfo}.
                   *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                   *
                   *  @param _recipient   Address to be set as royalty recipient for given token Id.
                   *  @param _bps         Updated royalty bps for the token Id.
                   */
                  function setRoyaltyInfoForToken(
                      uint256 _tokenId,
                      address _recipient,
                      uint256 _bps
                  ) external override {
                      if (!_canSetRoyaltyInfo()) {
                          revert("Not authorized");
                      }
                      _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                  }
                  /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                  function _setupRoyaltyInfoForToken(
                      uint256 _tokenId,
                      address _recipient,
                      uint256 _bps
                  ) internal {
                      if (_bps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                      emit RoyaltyForToken(_tokenId, _recipient, _bps);
                  }
                  /// @dev Returns whether royalty info can be set in the given execution context.
                  function _canSetRoyaltyInfo() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IClaimCondition {
                  /**
                   *  @notice The criteria that make up a claim condition.
                   *
                   *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                   *                                        The same claim condition applies until the `startTimestamp`
                   *                                        of the next claim condition.
                   *
                   *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                   *                                        the claim condition.
                   *
                   *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                   *                                        under the claim condition.
                   *
                   *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                   *
                   *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                   *                                        condition.
                   *
                   *  @param pricePerToken                  The price required to pay per token claimed.
                   *
                   *  @param currency                       The currency in which the `pricePerToken` must be paid.
                   *
                   *  @param metadata                       Claim condition metadata.
                   */
                  struct ClaimCondition {
                      uint256 startTimestamp;
                      uint256 maxClaimableSupply;
                      uint256 supplyClaimed;
                      uint256 quantityLimitPerWallet;
                      bytes32 merkleRoot;
                      uint256 pricePerToken;
                      address currency;
                      string metadata;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IClaimCondition.sol";
              /**
               *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IClaimConditionMultiPhase is IClaimCondition {
                  /**
                   *  @notice The set of all claim conditions, at any given moment.
                   *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                   *
                   *  @param currentStartId           The uid for the first claim condition amongst the current set of
                   *                                  claim conditions. The uid for each next claim condition is one
                   *                                  more than the previous claim condition's uid.
                   *
                   *  @param count                    The total number of phases / claim conditions in the list
                   *                                  of claim conditions.
                   *
                   *  @param conditions                   The claim conditions at a given uid. Claim conditions
                   *                                  are ordered in an ascending order by their `startTimestamp`.
                   *
                   *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                   */
                  struct ClaimConditionList {
                      uint256 currentStartId;
                      uint256 count;
                      mapping(uint256 => ClaimCondition) conditions;
                      mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
               *  for you contract.
               *
               *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
               */
              interface IContractMetadata {
                  /// @dev Returns the metadata URI of the contract.
                  function contractURI() external view returns (string memory);
                  /**
                   *  @dev Sets contract URI for the storefront-level metadata of the contract.
                   *       Only module admin can call this function.
                   */
                  function setContractURI(string calldata _uri) external;
                  /// @dev Emitted when the contract URI is updated.
                  event ContractURIUpdated(string prevURI, string newURI);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
               *  'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
               */
              interface IDelayedReveal {
                  /// @dev Emitted when tokens are revealed.
                  event TokenURIRevealed(uint256 indexed index, string revealedURI);
                  /**
                   *  @notice Reveals a batch of delayed reveal NFTs.
                   *
                   *  @param identifier The ID for the batch of delayed-reveal NFTs to reveal.
                   *
                   *  @param key        The key with which the base URI for the relevant batch of NFTs was encrypted.
                   */
                  function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI);
                  /**
                   *  @notice Performs XOR encryption/decryption.
                   *
                   *  @param data The data to encrypt. In the case of delayed-reveal NFTs, this is the "revealed" state
                   *              base URI of the relevant batch of NFTs.
                   *
                   *  @param key  The key with which to encrypt data
                   */
                  function encryptDecrypt(bytes memory data, bytes calldata key) external pure returns (bytes memory result);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IClaimConditionMultiPhase.sol";
              /**
               *  The interface `IDrop` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IDrop is IClaimConditionMultiPhase {
                  /**
                   *  @param proof Prood of concerned wallet's inclusion in an allowlist.
                   *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                   *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                   *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                   */
                  struct AllowlistProof {
                      bytes32[] proof;
                      uint256 quantityLimitPerWallet;
                      uint256 pricePerToken;
                      address currency;
                  }
                  /// @notice Emitted when tokens are claimed via `claim`.
                  event TokensClaimed(
                      uint256 indexed claimConditionIndex,
                      address indexed claimer,
                      address indexed receiver,
                      uint256 startTokenId,
                      uint256 quantityClaimed
                  );
                  /// @notice Emitted when the contract's claim conditions are updated.
                  event ClaimConditionsUpdated(ClaimCondition[] claimConditions, bool resetEligibility);
                  /**
                   *  @notice Lets an account claim a given quantity of NFTs.
                   *
                   *  @param receiver                       The receiver of the NFTs to claim.
                   *  @param quantity                       The quantity of NFTs to claim.
                   *  @param currency                       The currency in which to pay for the claim.
                   *  @param pricePerToken                  The price per token to pay for the claim.
                   *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                   *                                        of the claim conditions that apply.
                   *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                   */
                  function claim(
                      address receiver,
                      uint256 quantity,
                      address currency,
                      uint256 pricePerToken,
                      AllowlistProof calldata allowlistProof,
                      bytes memory data
                  ) external payable;
                  /**
                   *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                   *
                   *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                   *
                   *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                   *                                  in the new claim conditions being set.
                   *
                   */
                  function setClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
               *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
               *  minting a non-zero balance of NFTs of those tokenIds.
               */
              interface ILazyMint {
                  /// @dev Emitted when tokens are lazy minted.
                  event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                  /**
                   *  @notice Lazy mints a given amount of NFTs.
                   *
                   *  @param amount           The number of NFTs to lazy mint.
                   *
                   *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                   *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                   *
                   *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                   *
                   *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                   */
                  function lazyMint(
                      uint256 amount,
                      string calldata baseURIForTokens,
                      bytes calldata extraData
                  ) external returns (uint256 batchId);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              interface IOperatorFilterRegistry {
                  function isOperatorAllowed(address registrant, address operator) external view returns (bool);
                  function register(address registrant) external;
                  function registerAndSubscribe(address registrant, address subscription) external;
                  function registerAndCopyEntries(address registrant, address registrantToCopy) external;
                  function unregister(address addr) external;
                  function updateOperator(
                      address registrant,
                      address operator,
                      bool filtered
                  ) external;
                  function updateOperators(
                      address registrant,
                      address[] calldata operators,
                      bool filtered
                  ) external;
                  function updateCodeHash(
                      address registrant,
                      bytes32 codehash,
                      bool filtered
                  ) external;
                  function updateCodeHashes(
                      address registrant,
                      bytes32[] calldata codeHashes,
                      bool filtered
                  ) external;
                  function subscribe(address registrant, address registrantToSubscribe) external;
                  function unsubscribe(address registrant, bool copyExistingEntries) external;
                  function subscriptionOf(address addr) external returns (address registrant);
                  function subscribers(address registrant) external returns (address[] memory);
                  function subscriberAt(address registrant, uint256 index) external returns (address);
                  function copyEntriesOf(address registrant, address registrantToCopy) external;
                  function isOperatorFiltered(address registrant, address operator) external returns (bool);
                  function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
                  function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
                  function filteredOperators(address addr) external returns (address[] memory);
                  function filteredCodeHashes(address addr) external returns (bytes32[] memory);
                  function filteredOperatorAt(address registrant, uint256 index) external returns (address);
                  function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
                  function isRegistered(address addr) external returns (bool);
                  function codeHashOf(address addr) external returns (bytes32);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              interface IOperatorFilterToggle {
                  event OperatorRestriction(bool restriction);
                  function operatorRestriction() external view returns (bool);
                  function setOperatorRestriction(bool restriction) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
               *  information about who the contract's owner is.
               */
              interface IOwnable {
                  /// @dev Returns the owner of the contract.
                  function owner() external view returns (address);
                  /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                  function setOwner(address _newOwner) external;
                  /// @dev Emitted when a new Owner is set.
                  event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IPermissions {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IPermissions.sol";
              /**
               * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
               */
              interface IPermissionsEnumerable is IPermissions {
                  /**
                   * @dev Returns one of the accounts that have `role`. `index` must be a
                   * value between 0 and {getRoleMemberCount}, non-inclusive.
                   *
                   * Role bearers are not sorted in any particular way, and their ordering may
                   * change at any point.
                   *
                   * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                   * you perform all queries on the same block. See the following
                   * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                   * for more information.
                   */
                  function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                  /**
                   * @dev Returns the number of accounts that have `role`. Can be used
                   * together with {getRoleMember} to enumerate all bearers of a role.
                   */
                  function getRoleMemberCount(bytes32 role) external view returns (uint256);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
               *  that uses information about platform fees, if desired.
               */
              interface IPlatformFee {
                  /// @dev Returns the platform fee bps and recipient.
                  function getPlatformFeeInfo() external view returns (address, uint16);
                  /// @dev Lets a module admin update the fees on primary sales.
                  function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                  /// @dev Emitted when fee on primary sales is updated.
                  event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
               *  primary sales, if desired.
               */
              interface IPrimarySale {
                  /// @dev The adress that receives all primary sales value.
                  function primarySaleRecipient() external view returns (address);
                  /// @dev Lets a module admin set the default recipient of all primary sales.
                  function setPrimarySaleRecipient(address _saleRecipient) external;
                  /// @dev Emitted when a new sale recipient is set.
                  event PrimarySaleRecipientUpdated(address indexed recipient);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "../../eip/interface/IERC2981.sol";
              /**
               *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
               *  that uses information about royalty fees, if desired.
               *
               *  The `Royalty` contract is ERC2981 compliant.
               */
              interface IRoyalty is IERC2981 {
                  struct RoyaltyInfo {
                      address recipient;
                      uint256 bps;
                  }
                  /// @dev Returns the royalty recipient and fee bps.
                  function getDefaultRoyaltyInfo() external view returns (address, uint16);
                  /// @dev Lets a module admin update the royalty bps and recipient.
                  function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                  /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                  function setRoyaltyInfoForToken(
                      uint256 tokenId,
                      address recipient,
                      uint256 bps
                  ) external;
                  /// @dev Returns the royalty recipient for a particular token Id.
                  function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                  /// @dev Emitted when royalty info is updated.
                  event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                  /// @dev Emitted when royalty recipient for tokenId is set
                  event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              interface IWETH {
                  function deposit() external payable;
                  function withdraw(uint256 amount) external;
                  function transfer(address to, uint256 value) external returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              // Helper interfaces
              import { IWETH } from "../interfaces/IWETH.sol";
              import "../openzeppelin-presets/token/ERC20/utils/SafeERC20.sol";
              library CurrencyTransferLib {
                  using SafeERC20 for IERC20;
                  /// @dev The address interpreted as native token of the chain.
                  address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                  /// @dev Transfers a given amount of currency.
                  function transferCurrency(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount
                  ) internal {
                      if (_amount == 0) {
                          return;
                      }
                      if (_currency == NATIVE_TOKEN) {
                          safeTransferNativeToken(_to, _amount);
                      } else {
                          safeTransferERC20(_currency, _from, _to, _amount);
                      }
                  }
                  /// @dev Transfers a given amount of currency. (With native token wrapping)
                  function transferCurrencyWithWrapper(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount,
                      address _nativeTokenWrapper
                  ) internal {
                      if (_amount == 0) {
                          return;
                      }
                      if (_currency == NATIVE_TOKEN) {
                          if (_from == address(this)) {
                              // withdraw from weth then transfer withdrawn native token to recipient
                              IWETH(_nativeTokenWrapper).withdraw(_amount);
                              safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                          } else if (_to == address(this)) {
                              // store native currency in weth
                              require(_amount == msg.value, "msg.value != amount");
                              IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                          } else {
                              safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                          }
                      } else {
                          safeTransferERC20(_currency, _from, _to, _amount);
                      }
                  }
                  /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                  function safeTransferERC20(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount
                  ) internal {
                      if (_from == _to) {
                          return;
                      }
                      if (_from == address(this)) {
                          IERC20(_currency).safeTransfer(_to, _amount);
                      } else {
                          IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                      }
                  }
                  /// @dev Transfers `amount` of native token to `to`.
                  function safeTransferNativeToken(address to, uint256 value) internal {
                      // solhint-disable avoid-low-level-calls
                      // slither-disable-next-line low-level-calls
                      (bool success, ) = to.call{ value: value }("");
                      require(success, "native token transfer failed");
                  }
                  /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                  function safeTransferNativeTokenWithWrapper(
                      address to,
                      uint256 value,
                      address _nativeTokenWrapper
                  ) internal {
                      // solhint-disable avoid-low-level-calls
                      // slither-disable-next-line low-level-calls
                      (bool success, ) = to.call{ value: value }("");
                      if (!success) {
                          IWETH(_nativeTokenWrapper).deposit{ value: value }();
                          IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/utils/cryptography/MerkleProof.sol
              // Copied from https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
              pragma solidity ^0.8.0;
              /**
               * @dev These functions deal with verification of Merkle Trees proofs.
               *
               * The proofs can be generated using the JavaScript library
               * https://github.com/miguelmota/merkletreejs[merkletreejs].
               * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
               *
               * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
               *
               * Source: https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
               */
              library MerkleProof {
                  /**
                   * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                   * defined by `root`. For this, a `proof` must be provided, containing
                   * sibling hashes on the branch from the leaf to the root of the tree. Each
                   * pair of leaves and each pair of pre-images are assumed to be sorted.
                   */
                  function verify(
                      bytes32[] memory proof,
                      bytes32 root,
                      bytes32 leaf
                  ) internal pure returns (bool, uint256) {
                      bytes32 computedHash = leaf;
                      uint256 index = 0;
                      for (uint256 i = 0; i < proof.length; i++) {
                          index *= 2;
                          bytes32 proofElement = proof[i];
                          if (computedHash <= proofElement) {
                              // Hash(current computed hash + current element of the proof)
                              computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                          } else {
                              // Hash(current element of the proof + current computed hash)
                              computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                              index += 1;
                          }
                      }
                      // Check if the computed hash (root) is equal to the provided root
                      return (computedHash == root, index);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library TWAddress {
                  /**
                   * @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.
                   *
                   * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) 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/Strings.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev String operations.
               */
              library TWStrings {
                  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.0 (metatx/ERC2771Context.sol)
              pragma solidity ^0.8.11;
              import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              /**
               * @dev Context variant with ERC2771 support.
               */
              abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                  mapping(address => bool) private _trustedForwarder;
                  function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                      __Context_init_unchained();
                      __ERC2771Context_init_unchained(trustedForwarder);
                  }
                  function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                      for (uint256 i = 0; i < trustedForwarder.length; i++) {
                          _trustedForwarder[trustedForwarder[i]] = true;
                      }
                  }
                  function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                      return _trustedForwarder[forwarder];
                  }
                  function _msgSender() internal view virtual override returns (address sender) {
                      if (isTrustedForwarder(msg.sender)) {
                          // The assembly code is more direct than the Solidity version using `abi.decode`.
                          assembly {
                              sender := shr(96, calldataload(sub(calldatasize(), 20)))
                          }
                      } else {
                          return super._msgSender();
                      }
                  }
                  function _msgData() internal view virtual override returns (bytes calldata) {
                      if (isTrustedForwarder(msg.sender)) {
                          return msg.data[:msg.data.length - 20];
                      } else {
                          return super._msgData();
                      }
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../../../../eip/interface/IERC20.sol";
              import "../../../../lib/TWAddress.sol";
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using TWAddress for address;
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
                  /**
                   * @dev Deprecated. This function has issues similar to the ones found in
                   * {IERC20-approve}, and its usage is discouraged.
                   *
                   * Whenever possible, use {safeIncreaseAllowance} and
                   * {safeDecreaseAllowance} instead.
                   */
                  function safeApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
                  function safeIncreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 newAllowance = token.allowance(address(this), spender) + value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
                  function safeDecreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      unchecked {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                          uint256 newAllowance = oldAllowance - value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                      // the target address contains contract code and also asserts for success in the low-level call.
                      bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
              pragma solidity ^0.8.0;
              import "../utils/introspection/IERC165Upgradeable.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 IERC2981Upgradeable is IERC165Upgradeable {
                  /**
                   * @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 (last updated v4.7.0) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.2;
              import "../../utils/AddressUpgradeable.sol";
              /**
               * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
               * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
               * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
               * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
               *
               * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
               * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
               * case an upgrade adds a module that needs to be initialized.
               *
               * For example:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * contract MyToken is ERC20Upgradeable {
               *     function initialize() initializer public {
               *         __ERC20_init("MyToken", "MTK");
               *     }
               * }
               * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
               *     function initializeV2() reinitializer(2) public {
               *         __ERC20Permit_init("MyToken");
               *     }
               * }
               * ```
               *
               * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
               * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
               *
               * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
               * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
               *
               * [CAUTION]
               * ====
               * Avoid leaving a contract uninitialized.
               *
               * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
               * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
               * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * /// @custom:oz-upgrades-unsafe-allow constructor
               * constructor() {
               *     _disableInitializers();
               * }
               * ```
               * ====
               */
              abstract contract Initializable {
                  /**
                   * @dev Indicates that the contract has been initialized.
                   * @custom:oz-retyped-from bool
                   */
                  uint8 private _initialized;
                  /**
                   * @dev Indicates that the contract is in the process of being initialized.
                   */
                  bool private _initializing;
                  /**
                   * @dev Triggered when the contract has been initialized or reinitialized.
                   */
                  event Initialized(uint8 version);
                  /**
                   * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                   * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                   */
                  modifier initializer() {
                      bool isTopLevelCall = !_initializing;
                      require(
                          (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                          "Initializable: contract is already initialized"
                      );
                      _initialized = 1;
                      if (isTopLevelCall) {
                          _initializing = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          _initializing = false;
                          emit Initialized(1);
                      }
                  }
                  /**
                   * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                   * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                   * used to initialize parent contracts.
                   *
                   * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                   * initialization step. This is essential to configure modules that are added through upgrades and that require
                   * initialization.
                   *
                   * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                   * a contract, executing them in the right order is up to the developer or operator.
                   */
                  modifier reinitializer(uint8 version) {
                      require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                      _initialized = version;
                      _initializing = true;
                      _;
                      _initializing = false;
                      emit Initialized(version);
                  }
                  /**
                   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                   * {initializer} and {reinitializer} modifiers, directly or indirectly.
                   */
                  modifier onlyInitializing() {
                      require(_initializing, "Initializable: contract is not initializing");
                      _;
                  }
                  /**
                   * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                   * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                   * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                   * through proxies.
                   */
                  function _disableInitializers() internal virtual {
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized < type(uint8).max) {
                          _initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
              }
              // 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 IERC721ReceiverUpgradeable {
                  /**
                   * @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 (last updated v4.7.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165Upgradeable.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721Upgradeable is IERC165Upgradeable {
                  /**
                   * @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 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 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721Upgradeable.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                  /**
                   * @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.7.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library AddressUpgradeable {
                  /**
                   * @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 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
                              /// @solidity memory-safe-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;
              import "../proxy/utils/Initializable.sol";
              /**
               * @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 ContextUpgradeable is Initializable {
                  function __Context_init() internal onlyInitializing {
                  }
                  function __Context_init_unchained() internal onlyInitializing {
                  }
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
              pragma solidity ^0.8.0;
              import "./AddressUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              abstract contract MulticallUpgradeable is Initializable {
                  function __Multicall_init() internal onlyInitializing {
                  }
                  function __Multicall_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev Receives and executes a batch of function calls on this contract.
                   */
                  function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                      results = new bytes[](data.length);
                      for (uint256 i = 0; i < data.length; i++) {
                          results[i] = _functionDelegateCall(address(this), data[i]);
                      }
                      return results;
                  }
                  /**
                   * @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) private returns (bytes memory) {
                      require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev String operations.
               */
              library StringsUpgradeable {
                  bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      // Inspired by OraclizeAPI's implementation - MIT licence
                      // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                      if (value == 0) {
                          return "0";
                      }
                      uint256 temp = value;
                      uint256 digits;
                      while (temp != 0) {
                          digits++;
                          temp /= 10;
                      }
                      bytes memory buffer = new bytes(digits);
                      while (value != 0) {
                          digits -= 1;
                          buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                          value /= 10;
                      }
                      return string(buffer);
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      if (value == 0) {
                          return "0x00";
                      }
                      uint256 temp = value;
                      uint256 length = 0;
                      while (temp != 0) {
                          length++;
                          temp >>= 8;
                      }
                      return toHexString(value, length);
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _HEX_SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
                  function __ERC165_init() internal onlyInitializing {
                  }
                  function __ERC165_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165Upgradeable).interfaceId;
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // 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 IERC165Upgradeable {
                  /**
                   * @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
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol";
              /**
               * @dev Interface of an ERC721A compliant contract.
               */
              interface IERC721AUpgradeable is IERC721Upgradeable, IERC721MetadataUpgradeable {
                  /**
                   * The caller must own the token or be an approved operator.
                   */
                  error ApprovalCallerNotOwnerNorApproved();
                  /**
                   * The token does not exist.
                   */
                  error ApprovalQueryForNonexistentToken();
                  /**
                   * The caller cannot approve to their own address.
                   */
                  error ApproveToCaller();
                  /**
                   * The caller cannot approve to the current owner.
                   */
                  error ApprovalToCurrentOwner();
                  /**
                   * Cannot query the balance for the zero address.
                   */
                  error BalanceQueryForZeroAddress();
                  /**
                   * Cannot mint to the zero address.
                   */
                  error MintToZeroAddress();
                  /**
                   * The quantity of tokens minted must be more than zero.
                   */
                  error MintZeroQuantity();
                  /**
                   * The token does not exist.
                   */
                  error OwnerQueryForNonexistentToken();
                  /**
                   * The caller must own the token or be an approved operator.
                   */
                  error TransferCallerNotOwnerNorApproved();
                  /**
                   * The token must be owned by `from`.
                   */
                  error TransferFromIncorrectOwner();
                  /**
                   * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
                   */
                  error TransferToNonERC721ReceiverImplementer();
                  /**
                   * Cannot transfer to the zero address.
                   */
                  error TransferToZeroAddress();
                  /**
                   * The token does not exist.
                   */
                  error URIQueryForNonexistentToken();
                  // Compiler will pack this into a single 256bit word.
                  struct TokenOwnership {
                      // The address of the owner.
                      address addr;
                      // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                      uint64 startTimestamp;
                      // Whether the token has been burned.
                      bool burned;
                  }
                  // Compiler will pack this into a single 256bit word.
                  struct AddressData {
                      // Realistically, 2**64-1 is more than enough.
                      uint64 balance;
                      // Keeps track of mint count with minimal overhead for tokenomics.
                      uint64 numberMinted;
                      // Keeps track of burn count with minimal overhead for tokenomics.
                      uint64 numberBurned;
                      // For miscellaneous variable(s) pertaining to the address
                      // (e.g. number of whitelist mint slots used).
                      // If there are multiple variables, please pack them into a uint64.
                      uint64 aux;
                  }
                  /**
                   * @dev Returns the total amount of tokens stored by the contract.
                   * 
                   * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
                   */
                  function totalSupply() external view returns (uint256);
              }
              

              File 2 of 2: DropERC721
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.11;
              //  ==========  External imports    ==========
              import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol";
              import "../eip/ERC721AVirtualApprove.sol";
              //  ==========  Internal imports    ==========
              import "../openzeppelin-presets/metatx/ERC2771ContextUpgradeable.sol";
              import "../lib/CurrencyTransferLib.sol";
              //  ==========  Features    ==========
              import "../extension/ContractMetadata.sol";
              import "../extension/PlatformFee.sol";
              import "../extension/Royalty.sol";
              import "../extension/PrimarySale.sol";
              import "../extension/Ownable.sol";
              import "../extension/DelayedReveal.sol";
              import "../extension/LazyMint.sol";
              import "../extension/PermissionsEnumerable.sol";
              import "../extension/Drop.sol";
              // OpenSea operator filter
              import "../extension/DefaultOperatorFiltererUpgradeable.sol";
              contract DropERC721 is
                  Initializable,
                  ContractMetadata,
                  PlatformFee,
                  Royalty,
                  PrimarySale,
                  Ownable,
                  DelayedReveal,
                  LazyMint,
                  PermissionsEnumerable,
                  Drop,
                  ERC2771ContextUpgradeable,
                  MulticallUpgradeable,
                  DefaultOperatorFiltererUpgradeable,
                  ERC721AUpgradeable
              {
                  using StringsUpgradeable for uint256;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.
                  bytes32 private transferRole;
                  /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.
                  bytes32 private minterRole;
                  /// @dev Max bps in the thirdweb system.
                  uint256 private constant MAX_BPS = 10_000;
                  /// @dev Global max total supply of NFTs.
                  uint256 public maxTotalSupply;
                  /// @dev Emitted when the global max supply of tokens is updated.
                  event MaxTotalSupplyUpdated(uint256 maxTotalSupply);
                  /*///////////////////////////////////////////////////////////////
                                  Constructor + initializer logic
                  //////////////////////////////////////////////////////////////*/
                  constructor() initializer {}
                  /// @dev Initiliazes the contract, like a constructor.
                  function initialize(
                      address _defaultAdmin,
                      string memory _name,
                      string memory _symbol,
                      string memory _contractURI,
                      address[] memory _trustedForwarders,
                      address _saleRecipient,
                      address _royaltyRecipient,
                      uint128 _royaltyBps,
                      uint128 _platformFeeBps,
                      address _platformFeeRecipient
                  ) external initializer {
                      bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                      bytes32 _minterRole = keccak256("MINTER_ROLE");
                      // Initialize inherited contracts, most base-like -> most derived.
                      __ERC2771Context_init(_trustedForwarders);
                      __ERC721A_init(_name, _symbol);
                      __DefaultOperatorFilterer_init();
                      _setupContractURI(_contractURI);
                      _setupOwner(_defaultAdmin);
                      _setOperatorRestriction(true);
                      _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
                      _setupRole(_minterRole, _defaultAdmin);
                      _setupRole(_transferRole, _defaultAdmin);
                      _setupRole(_transferRole, address(0));
                      _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                      _setupPrimarySaleRecipient(_saleRecipient);
                      transferRole = _transferRole;
                      minterRole = _minterRole;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      ERC 165 / 721 / 2981 logic
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns the URI for a given tokenId.
                  function tokenURI(uint256 _tokenId) public view override returns (string memory) {
                      (uint256 batchId, ) = _getBatchId(_tokenId);
                      string memory batchUri = _getBaseURI(_tokenId);
                      if (isEncryptedBatch(batchId)) {
                          return string(abi.encodePacked(batchUri, "0"));
                      } else {
                          return string(abi.encodePacked(batchUri, _tokenId.toString()));
                      }
                  }
                  /// @dev See ERC 165
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721AUpgradeable, IERC165)
                      returns (bool)
                  {
                      return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Contract identifiers
                  //////////////////////////////////////////////////////////////*/
                  function contractType() external pure returns (bytes32) {
                      return bytes32("DropERC721");
                  }
                  function contractVersion() external pure returns (uint8) {
                      return uint8(4);
                  }
                  /*///////////////////////////////////////////////////////////////
                                  Lazy minting + delayed-reveal logic
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs.
                   *       The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`.
                   */
                  function lazyMint(
                      uint256 _amount,
                      string calldata _baseURIForTokens,
                      bytes calldata _data
                  ) public override returns (uint256 batchId) {
                      if (_data.length > 0) {
                          (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32));
                          if (encryptedURI.length != 0 && provenanceHash != "") {
                              _setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
                          }
                      }
                      return super.lazyMint(_amount, _baseURIForTokens, _data);
                  }
                  /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
                  function reveal(uint256 _index, bytes calldata _key)
                      external
                      onlyRole(minterRole)
                      returns (string memory revealedURI)
                  {
                      uint256 batchId = getBatchIdAtIndex(_index);
                      revealedURI = getRevealURI(batchId, _key);
                      _setEncryptedData(batchId, "");
                      _setBaseURI(batchId, revealedURI);
                      emit TokenURIRevealed(_index, revealedURI);
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Setter functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Lets a contract admin set the global maximum supply for collection's NFTs.
                  function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      maxTotalSupply = _maxTotalSupply;
                      emit MaxTotalSupplyUpdated(_maxTotalSupply);
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Runs before every `claim` function call.
                  function _beforeClaim(
                      address,
                      uint256 _quantity,
                      address,
                      uint256,
                      AllowlistProof calldata,
                      bytes memory
                  ) internal view override {
                      require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens");
                      require(maxTotalSupply == 0 || _currentIndex + _quantity <= maxTotalSupply, "exceed max total supply.");
                  }
                  /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                  function _collectPriceOnClaim(
                      address _primarySaleRecipient,
                      uint256 _quantityToClaim,
                      address _currency,
                      uint256 _pricePerToken
                  ) internal override {
                      if (_pricePerToken == 0) {
                          return;
                      }
                      (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
                      address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient;
                      uint256 totalPrice = _quantityToClaim * _pricePerToken;
                      uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
                      if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
                          if (msg.value != totalPrice) {
                              revert("!Price");
                          }
                      }
                      CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
                      CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees);
                  }
                  /// @dev Transfers the NFTs being claimed.
                  function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed)
                      internal
                      override
                      returns (uint256 startTokenId)
                  {
                      startTokenId = _currentIndex;
                      _safeMint(_to, _quantityBeingClaimed);
                  }
                  /// @dev Checks whether platform fee info can be set in the given execution context.
                  function _canSetPlatformFeeInfo() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether primary sale recipient can be set in the given execution context.
                  function _canSetPrimarySaleRecipient() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether owner can be set in the given execution context.
                  function _canSetOwner() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether royalty info can be set in the given execution context.
                  function _canSetRoyaltyInfo() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Checks whether platform fee info can be set in the given execution context.
                  function _canSetClaimConditions() internal view override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /// @dev Returns whether lazy minting can be done in the given execution context.
                  function _canLazyMint() internal view virtual override returns (bool) {
                      return hasRole(minterRole, _msgSender());
                  }
                  /// @dev Returns whether operator restriction can be set in the given execution context.
                  function _canSetOperatorRestriction() internal virtual override returns (bool) {
                      return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Miscellaneous
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function totalMinted() external view returns (uint256) {
                      unchecked {
                          return _currentIndex - _startTokenId();
                      }
                  }
                  /// @dev The tokenId of the next NFT that will be minted / lazy minted.
                  function nextTokenIdToMint() external view returns (uint256) {
                      return nextTokenIdToLazyMint;
                  }
                  /// @dev The next token ID of the NFT that can be claimed.
                  function nextTokenIdToClaim() external view returns (uint256) {
                      return _currentIndex;
                  }
                  /// @dev Burns `tokenId`. See {ERC721-_burn}.
                  function burn(uint256 tokenId) external virtual {
                      // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals.
                      _burn(tokenId, true);
                  }
                  /// @dev See {ERC721-_beforeTokenTransfer}.
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual override {
                      super._beforeTokenTransfers(from, to, startTokenId, quantity);
                      // if transfer is restricted on the contract, we still want to allow burning and minting
                      if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) {
                          if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) {
                              revert("!Transfer-Role");
                          }
                      }
                  }
                  /// @dev See {ERC721-setApprovalForAll}.
                  function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
                      super.setApprovalForAll(operator, approved);
                  }
                  /// @dev See {ERC721-approve}.
                  function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
                      super.approve(operator, tokenId);
                  }
                  /// @dev See {ERC721-_transferFrom}.
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.transferFrom(from, to, tokenId);
                  }
                  /// @dev See {ERC721-_safeTransferFrom}.
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId);
                  }
                  /// @dev See {ERC721-_safeTransferFrom}.
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) public override(ERC721AUpgradeable) onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId, data);
                  }
                  function _dropMsgSender() internal view virtual override returns (address) {
                      return _msgSender();
                  }
                  function _msgSender()
                      internal
                      view
                      virtual
                      override(ContextUpgradeable, ERC2771ContextUpgradeable)
                      returns (address sender)
                  {
                      return ERC2771ContextUpgradeable._msgSender();
                  }
                  function _msgData()
                      internal
                      view
                      virtual
                      override(ContextUpgradeable, ERC2771ContextUpgradeable)
                      returns (bytes calldata)
                  {
                      return ERC2771ContextUpgradeable._msgData();
                  }
              }
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              ////////// CHANGELOG: turn `approve` to virtual //////////
              pragma solidity ^0.8.4;
              import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
               * the Metadata extension. Built to optimize for lower gas during batch mints.
               *
               * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
               *
               * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
               *
               * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
               */
              contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721AUpgradeable {
                  using AddressUpgradeable for address;
                  using StringsUpgradeable for uint256;
                  // The tokenId of the next token to be minted.
                  uint256 internal _currentIndex;
                  // The number of tokens burned.
                  uint256 internal _burnCounter;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to ownership details
                  // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
                  mapping(uint256 => TokenOwnership) internal _ownerships;
                  // Mapping owner address to address data
                  mapping(address => AddressData) private _addressData;
                  // 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;
                  function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializing {
                      __ERC721A_init_unchained(name_, symbol_);
                  }
                  function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                      _name = name_;
                      _symbol = symbol_;
                      _currentIndex = _startTokenId();
                  }
                  /**
                   * To change the starting tokenId, please override this function.
                   */
                  function _startTokenId() internal view virtual returns (uint256) {
                      return 0;
                  }
                  /**
                   * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
                   */
                  function totalSupply() public view override returns (uint256) {
                      // Counter underflow is impossible as _burnCounter cannot be incremented
                      // more than _currentIndex - _startTokenId() times
                      unchecked {
                          return _currentIndex - _burnCounter - _startTokenId();
                      }
                  }
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function _totalMinted() internal view returns (uint256) {
                      // Counter underflow is impossible as _currentIndex does not decrement,
                      // and it is initialized to _startTokenId()
                      unchecked {
                          return _currentIndex - _startTokenId();
                      }
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC165Upgradeable, IERC165Upgradeable)
                      returns (bool)
                  {
                      return
                          interfaceId == type(IERC721Upgradeable).interfaceId ||
                          interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view override returns (uint256) {
                      if (owner == address(0)) revert BalanceQueryForZeroAddress();
                      return uint256(_addressData[owner].balance);
                  }
                  /**
                   * Returns the number of tokens minted by `owner`.
                   */
                  function _numberMinted(address owner) internal view returns (uint256) {
                      return uint256(_addressData[owner].numberMinted);
                  }
                  /**
                   * Returns the number of tokens burned by or on behalf of `owner`.
                   */
                  function _numberBurned(address owner) internal view returns (uint256) {
                      return uint256(_addressData[owner].numberBurned);
                  }
                  /**
                   * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                   */
                  function _getAux(address owner) internal view returns (uint64) {
                      return _addressData[owner].aux;
                  }
                  /**
                   * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
                   * If there are multiple variables, please pack them into a uint64.
                   */
                  function _setAux(address owner, uint64 aux) internal {
                      _addressData[owner].aux = aux;
                  }
                  /**
                   * Gas spent here starts off proportional to the maximum mint batch size.
                   * It gradually moves to O(1) as tokens get transferred around in the collection over time.
                   */
                  function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < _currentIndex) {
                                  TokenOwnership memory ownership = _ownerships[curr];
                                  if (!ownership.burned) {
                                      if (ownership.addr != address(0)) {
                                          return ownership;
                                      }
                                      // Invariant:
                                      // There will always be an ownership that has an address and is not burned
                                      // before an ownership that does not have an address and is not burned.
                                      // Hence, curr will not underflow.
                                      while (true) {
                                          curr--;
                                          ownership = _ownerships[curr];
                                          if (ownership.addr != address(0)) {
                                              return ownership;
                                          }
                                      }
                                  }
                              }
                      }
                      revert OwnerQueryForNonexistentToken();
                  }
                  /**
                   * @dev See {IERC721-ownerOf}.
                   */
                  function ownerOf(uint256 tokenId) public view override returns (address) {
                      return _ownershipOf(tokenId).addr;
                  }
                  /**
                   * @dev See {IERC721Metadata-name}.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC721Metadata-tokenURI}.
                   */
                  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                      if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
                      string memory baseURI = _baseURI();
                      return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                  }
                  /**
                   * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                   * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                   * by default, can be overriden in child contracts.
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return "";
                  }
                  /**
                   * @dev See {IERC721-approve}.
                   */
                  function approve(address to, uint256 tokenId) public virtual override {
                      address owner = ERC721AUpgradeable.ownerOf(tokenId);
                      if (to == owner) revert ApprovalToCurrentOwner();
                      if (_msgSender() != owner)
                          if (!isApprovedForAll(owner, _msgSender())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                      _approve(to, tokenId, owner);
                  }
                  /**
                   * @dev See {IERC721-getApproved}.
                   */
                  function getApproved(uint256 tokenId) public view override returns (address) {
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return _tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      if (operator == _msgSender()) revert ApproveToCaller();
                      _operatorApprovals[_msgSender()][operator] = approved;
                      emit ApprovalForAll(_msgSender(), operator, approved);
                  }
                  /**
                   * @dev See {IERC721-isApprovedForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public virtual override {
                      _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 {
                      _transfer(from, to, tokenId);
                      if (to.isContract())
                          if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                              revert TransferToNonERC721ReceiverImplementer();
                          }
                  }
                  /**
                   * @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`),
                   */
                  function _exists(uint256 tokenId) internal view returns (bool) {
                      return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
                  }
                  /**
                   * @dev Equivalent to `_safeMint(to, quantity, '')`.
                   */
                  function _safeMint(address to, uint256 quantity) internal {
                      _safeMint(to, quantity, "");
                  }
                  /**
                   * @dev Safely mints `quantity` tokens and transfers them to `to`.
                   *
                   * Requirements:
                   *
                   * - If `to` refers to a smart contract, it must implement
                   *   {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
                   * - `quantity` must be greater than 0.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeMint(
                      address to,
                      uint256 quantity,
                      bytes memory _data
                  ) internal {
                      uint256 startTokenId = _currentIndex;
                      if (to == address(0)) revert MintToZeroAddress();
                      if (quantity == 0) revert MintZeroQuantity();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are incredibly unrealistic.
                      // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                      // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                      unchecked {
                          _addressData[to].balance += uint64(quantity);
                          _addressData[to].numberMinted += uint64(quantity);
                          _ownerships[startTokenId].addr = to;
                          _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          if (to.isContract()) {
                              do {
                                  emit Transfer(address(0), to, updatedIndex);
                                  if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) {
                                      revert TransferToNonERC721ReceiverImplementer();
                                  }
                              } while (updatedIndex < end);
                              // Reentrancy protection
                              if (_currentIndex != startTokenId) revert();
                          } else {
                              do {
                                  emit Transfer(address(0), to, updatedIndex++);
                              } while (updatedIndex < end);
                          }
                          _currentIndex = updatedIndex;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, quantity);
                  }
                  /**
                   * @dev Mints `quantity` tokens and transfers them to `to`.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `quantity` must be greater than 0.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _mint(address to, uint256 quantity) internal {
                      uint256 startTokenId = _currentIndex;
                      if (to == address(0)) revert MintToZeroAddress();
                      if (quantity == 0) revert MintZeroQuantity();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are incredibly unrealistic.
                      // balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
                      // updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
                      unchecked {
                          _addressData[to].balance += uint64(quantity);
                          _addressData[to].numberMinted += uint64(quantity);
                          _ownerships[startTokenId].addr = to;
                          _ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          do {
                              emit Transfer(address(0), to, updatedIndex++);
                          } while (updatedIndex < end);
                          _currentIndex = updatedIndex;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, quantity);
                  }
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *
                   * 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
                  ) private {
                      TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                      if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
                      bool isApprovedOrOwner = (_msgSender() == from ||
                          isApprovedForAll(from, _msgSender()) ||
                          getApproved(tokenId) == _msgSender());
                      if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                      if (to == address(0)) revert TransferToZeroAddress();
                      _beforeTokenTransfers(from, to, tokenId, 1);
                      // Clear approvals from the previous owner
                      _approve(address(0), tokenId, from);
                      // Underflow of the sender's balance is impossible because we check for
                      // ownership above and the recipient's balance can't realistically overflow.
                      // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                      unchecked {
                          _addressData[from].balance -= 1;
                          _addressData[to].balance += 1;
                          TokenOwnership storage currSlot = _ownerships[tokenId];
                          currSlot.addr = to;
                          currSlot.startTimestamp = uint64(block.timestamp);
                          // If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
                          // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                          uint256 nextTokenId = tokenId + 1;
                          TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                          if (nextSlot.addr == address(0)) {
                              // This will suffice for checking _exists(nextTokenId),
                              // as a burned slot cannot contain the zero address.
                              if (nextTokenId != _currentIndex) {
                                  nextSlot.addr = from;
                                  nextSlot.startTimestamp = prevOwnership.startTimestamp;
                              }
                          }
                      }
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfers(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Equivalent to `_burn(tokenId, false)`.
                   */
                  function _burn(uint256 tokenId) internal virtual {
                      _burn(tokenId, false);
                  }
                  /**
                   * @dev Destroys `tokenId`.
                   * The approval is cleared when the token is burned.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
                      TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
                      address from = prevOwnership.addr;
                      if (approvalCheck) {
                          bool isApprovedOrOwner = (_msgSender() == from ||
                              isApprovedForAll(from, _msgSender()) ||
                              getApproved(tokenId) == _msgSender());
                          if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
                      }
                      _beforeTokenTransfers(from, address(0), tokenId, 1);
                      // Clear approvals from the previous owner
                      _approve(address(0), tokenId, from);
                      // Underflow of the sender's balance is impossible because we check for
                      // ownership above and the recipient's balance can't realistically overflow.
                      // Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
                      unchecked {
                          AddressData storage addressData = _addressData[from];
                          addressData.balance -= 1;
                          addressData.numberBurned += 1;
                          // Keep track of who burned the token, and the timestamp of burning.
                          TokenOwnership storage currSlot = _ownerships[tokenId];
                          currSlot.addr = from;
                          currSlot.startTimestamp = uint64(block.timestamp);
                          currSlot.burned = true;
                          // If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
                          // Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
                          uint256 nextTokenId = tokenId + 1;
                          TokenOwnership storage nextSlot = _ownerships[nextTokenId];
                          if (nextSlot.addr == address(0)) {
                              // This will suffice for checking _exists(nextTokenId),
                              // as a burned slot cannot contain the zero address.
                              if (nextTokenId != _currentIndex) {
                                  nextSlot.addr = from;
                                  nextSlot.startTimestamp = prevOwnership.startTimestamp;
                              }
                          }
                      }
                      emit Transfer(from, address(0), tokenId);
                      _afterTokenTransfers(from, address(0), tokenId, 1);
                      // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                      unchecked {
                          _burnCounter++;
                      }
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits a {Approval} event.
                   */
                  function _approve(
                      address to,
                      uint256 tokenId,
                      address owner
                  ) private {
                      _tokenApprovals[tokenId] = to;
                      emit Approval(owner, to, tokenId);
                  }
                  /**
                   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target 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 _checkContractOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) private returns (bool) {
                      try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (
                          bytes4 retval
                      ) {
                          return retval == IERC721ReceiverUpgradeable(to).onERC721Received.selector;
                      } catch (bytes memory reason) {
                          if (reason.length == 0) {
                              revert TransferToNonERC721ReceiverImplementer();
                          } else {
                              assembly {
                                  revert(add(32, reason), mload(reason))
                              }
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
                   * And also called before burning one token.
                   *
                   * startTokenId - the first token id to be transferred
                   * quantity - the amount to be transferred
                   *
                   * 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, `tokenId` will be burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
                   * minting.
                   * And also called after one token has been burned.
                   *
                   * startTokenId - the first token id to be transferred
                   * quantity - the amount to be transferred
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                   * transferred to `to`.
                   * - When `from` is zero, `tokenId` has been minted for `to`.
                   * - When `to` is zero, `tokenId` has been burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _afterTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[42] private __gap;
              }
              // 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
               * [EIP](https://eips.ethereum.org/EIPS/eip-165).
               *
               * 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
                   * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
                   * 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: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              interface IERC20 {
                  function totalSupply() external view returns (uint256);
                  function balanceOf(address who) external view returns (uint256);
                  function allowance(address owner, address spender) external view returns (uint256);
                  function transfer(address to, uint256 value) external returns (bool);
                  function approve(address spender, uint256 value) external returns (bool);
                  function transferFrom(
                      address from,
                      address to,
                      uint256 value
                  ) external returns (bool);
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: Apache 2.0
              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 payed in that same unit of exchange.
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice)
                      external
                      view
                      returns (address receiver, uint256 royaltyAmount);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  @title   Batch-mint Metadata
               *  @notice  The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
               *           using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
               *           base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
               */
              contract BatchMintMetadata {
                  /// @dev Largest tokenId of each batch of tokens with the same baseURI.
                  uint256[] private batchIds;
                  /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.
                  mapping(uint256 => string) private baseURI;
                  /**
                   *  @notice         Returns the count of batches of NFTs.
                   *  @dev            Each batch of tokens has an in ID and an associated `baseURI`.
                   *                  See {batchIds}.
                   */
                  function getBaseURICount() public view returns (uint256) {
                      return batchIds.length;
                  }
                  /**
                   *  @notice         Returns the ID for the batch of tokens the given tokenId belongs to.
                   *  @dev            See {getBaseURICount}.
                   *  @param _index   ID of a token.
                   */
                  function getBatchIdAtIndex(uint256 _index) public view returns (uint256) {
                      if (_index >= getBaseURICount()) {
                          revert("Invalid index");
                      }
                      return batchIds[_index];
                  }
                  /// @dev Returns the id for the batch of tokens the given tokenId belongs to.
                  function _getBatchId(uint256 _tokenId) internal view returns (uint256 batchId, uint256 index) {
                      uint256 numOfTokenBatches = getBaseURICount();
                      uint256[] memory indices = batchIds;
                      for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                          if (_tokenId < indices[i]) {
                              index = i;
                              batchId = indices[i];
                              return (batchId, index);
                          }
                      }
                      revert("Invalid tokenId");
                  }
                  /// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.
                  function _getBaseURI(uint256 _tokenId) internal view returns (string memory) {
                      uint256 numOfTokenBatches = getBaseURICount();
                      uint256[] memory indices = batchIds;
                      for (uint256 i = 0; i < numOfTokenBatches; i += 1) {
                          if (_tokenId < indices[i]) {
                              return baseURI[indices[i]];
                          }
                      }
                      revert("Invalid tokenId");
                  }
                  /// @dev Sets the base URI for the batch of tokens with the given batchId.
                  function _setBaseURI(uint256 _batchId, string memory _baseURI) internal {
                      baseURI[_batchId] = _baseURI;
                  }
                  /// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.
                  function _batchMintMetadata(
                      uint256 _startId,
                      uint256 _amountToMint,
                      string memory _baseURIForTokens
                  ) internal returns (uint256 nextTokenIdToMint, uint256 batchId) {
                      batchId = _startId + _amountToMint;
                      nextTokenIdToMint = batchId;
                      batchIds.push(batchId);
                      baseURI[batchId] = _baseURIForTokens;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IContractMetadata.sol";
              /**
               *  @title   Contract Metadata
               *  @notice  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
               *           for you contract.
               *           Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
               */
              abstract contract ContractMetadata is IContractMetadata {
                  /// @notice Returns the contract metadata URI.
                  string public override contractURI;
                  /**
                   *  @notice         Lets a contract admin set the URI for contract-level metadata.
                   *  @dev            Caller should be authorized to setup contractURI, e.g. contract admin.
                   *                  See {_canSetContractURI}.
                   *                  Emits {ContractURIUpdated Event}.
                   *
                   *  @param _uri     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   */
                  function setContractURI(string memory _uri) external override {
                      if (!_canSetContractURI()) {
                          revert("Not authorized");
                      }
                      _setupContractURI(_uri);
                  }
                  /// @dev Lets a contract admin set the URI for contract-level metadata.
                  function _setupContractURI(string memory _uri) internal {
                      string memory prevURI = contractURI;
                      contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
                  /// @dev Returns whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import { OperatorFiltererUpgradeable } from "./OperatorFiltererUpgradeable.sol";
              abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                  address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                  function __DefaultOperatorFilterer_init() internal {
                      OperatorFiltererUpgradeable.__OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IDelayedReveal.sol";
              /**
               *  @title   Delayed Reveal
               *  @notice  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
               *           'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
               */
              abstract contract DelayedReveal is IDelayedReveal {
                  /// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.
                  mapping(uint256 => bytes) public encryptedData;
                  /// @dev Sets the delayed reveal data for a batchId.
                  function _setEncryptedData(uint256 _batchId, bytes memory _encryptedData) internal {
                      encryptedData[_batchId] = _encryptedData;
                  }
                  /**
                   *  @notice             Returns revealed URI for a batch of NFTs.
                   *  @dev                Reveal encrypted base URI for `_batchId` with caller/admin's `_key` used for encryption.
                   *                      Reverts if there's no encrypted URI for `_batchId`.
                   *                      See {encryptDecrypt}.
                   *
                   *  @param _batchId     ID of the batch for which URI is being revealed.
                   *  @param _key         Secure key used by caller/admin for encryption of baseURI.
                   *
                   *  @return revealedURI Decrypted base URI.
                   */
                  function getRevealURI(uint256 _batchId, bytes calldata _key) public view returns (string memory revealedURI) {
                      bytes memory data = encryptedData[_batchId];
                      if (data.length == 0) {
                          revert("Nothing to reveal");
                      }
                      (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(data, (bytes, bytes32));
                      revealedURI = string(encryptDecrypt(encryptedURI, _key));
                      require(keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) == provenanceHash, "Incorrect key");
                  }
                  /**
                   *  @notice         Encrypt/decrypt data on chain.
                   *  @dev            Encrypt/decrypt given `data` with `key`. Uses inline assembly.
                   *                  See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
                   *
                   *  @param data     Bytes of data to encrypt/decrypt.
                   *  @param key      Secure key used by caller for encryption/decryption.
                   *
                   *  @return result  Output after encryption/decryption of given data.
                   */
                  function encryptDecrypt(bytes memory data, bytes calldata key) public pure override returns (bytes memory result) {
                      // Store data length on stack for later use
                      uint256 length = data.length;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Set result to free memory pointer
                          result := mload(0x40)
                          // Increase free memory pointer by lenght + 32
                          mstore(0x40, add(add(result, length), 32))
                          // Set result length
                          mstore(result, length)
                      }
                      // Iterate over the data stepping by 32 bytes
                      for (uint256 i = 0; i < length; i += 32) {
                          // Generate hash of the key and offset
                          bytes32 hash = keccak256(abi.encodePacked(key, i));
                          bytes32 chunk;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              // Read 32-bytes data chunk
                              chunk := mload(add(data, add(i, 32)))
                          }
                          // XOR the chunk with hash
                          chunk ^= hash;
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              // Write 32-byte encrypted chunk
                              mstore(add(result, add(i, 32)), chunk)
                          }
                      }
                  }
                  /**
                   *  @notice         Returns whether the relvant batch of NFTs is subject to a delayed reveal.
                   *  @dev            Returns `true` if `_batchId`'s base URI is encrypted.
                   *  @param _batchId ID of a batch of NFTs.
                   */
                  function isEncryptedBatch(uint256 _batchId) public view returns (bool) {
                      return encryptedData[_batchId].length > 0;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IDrop.sol";
              import "../lib/MerkleProof.sol";
              abstract contract Drop is IDrop {
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @dev The active conditions for claiming tokens.
                  ClaimConditionList public claimCondition;
                  /*///////////////////////////////////////////////////////////////
                                          Drop logic
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Lets an account claim tokens.
                  function claim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) public payable virtual override {
                      _beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                      uint256 activeConditionId = getActiveClaimConditionId();
                      verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof);
                      // Update contract state.
                      claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
                      claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
                      // If there's a price, collect price.
                      _collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken);
                      // Mint the relevant tokens to claimer.
                      uint256 startTokenId = _transferTokensOnClaim(_receiver, _quantity);
                      emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
                      _afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
                  }
                  /// @dev Lets a contract admin set claim conditions.
                  function setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility)
                      external
                      virtual
                      override
                  {
                      if (!_canSetClaimConditions()) {
                          revert("Not authorized");
                      }
                      uint256 existingStartIndex = claimCondition.currentStartId;
                      uint256 existingPhaseCount = claimCondition.count;
                      /**
                       *  The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
                       *
                       *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                       *  conditions in `_conditions`, effectively resetting the restrictions on claims expressed
                       *  by `supplyClaimedByWallet`.
                       */
                      uint256 newStartIndex = existingStartIndex;
                      if (_resetClaimEligibility) {
                          newStartIndex = existingStartIndex + existingPhaseCount;
                      }
                      claimCondition.count = _conditions.length;
                      claimCondition.currentStartId = newStartIndex;
                      uint256 lastConditionStartTimestamp;
                      for (uint256 i = 0; i < _conditions.length; i++) {
                          require(i == 0 || lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
                          uint256 supplyClaimedAlready = claimCondition.conditions[newStartIndex + i].supplyClaimed;
                          if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
                              revert("max supply claimed");
                          }
                          claimCondition.conditions[newStartIndex + i] = _conditions[i];
                          claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
                          lastConditionStartTimestamp = _conditions[i].startTimestamp;
                      }
                      /**
                       *  Gas refunds (as much as possible)
                       *
                       *  If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
                       *  conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
                       *
                       *  If `_resetClaimEligibility == false`, and there are more existing claim conditions
                       *  than in `_conditions`, we delete the existing claim conditions that don't get replaced
                       *  by the conditions in `_conditions`.
                       */
                      if (_resetClaimEligibility) {
                          for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
                              delete claimCondition.conditions[i];
                          }
                      } else {
                          if (existingPhaseCount > _conditions.length) {
                              for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
                                  delete claimCondition.conditions[newStartIndex + i];
                              }
                          }
                      }
                      emit ClaimConditionsUpdated(_conditions, _resetClaimEligibility);
                  }
                  /// @dev Checks a request to claim NFTs against the active claim condition's criteria.
                  function verifyClaim(
                      uint256 _conditionId,
                      address _claimer,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof
                  ) public view returns (bool isOverride) {
                      ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId];
                      uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
                      uint256 claimPrice = currentClaimPhase.pricePerToken;
                      address claimCurrency = currentClaimPhase.currency;
                      if (currentClaimPhase.merkleRoot != bytes32(0)) {
                          (isOverride, ) = MerkleProof.verify(
                              _allowlistProof.proof,
                              currentClaimPhase.merkleRoot,
                              keccak256(
                                  abi.encodePacked(
                                      _claimer,
                                      _allowlistProof.quantityLimitPerWallet,
                                      _allowlistProof.pricePerToken,
                                      _allowlistProof.currency
                                  )
                              )
                          );
                      }
                      if (isOverride) {
                          claimLimit = _allowlistProof.quantityLimitPerWallet != 0
                              ? _allowlistProof.quantityLimitPerWallet
                              : claimLimit;
                          claimPrice = _allowlistProof.pricePerToken != type(uint256).max
                              ? _allowlistProof.pricePerToken
                              : claimPrice;
                          claimCurrency = _allowlistProof.pricePerToken != type(uint256).max && _allowlistProof.currency != address(0)
                              ? _allowlistProof.currency
                              : claimCurrency;
                      }
                      uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                      if (_currency != claimCurrency || _pricePerToken != claimPrice) {
                          revert("!PriceOrCurrency");
                      }
                      if (_quantity == 0 || (_quantity + supplyClaimedByWallet > claimLimit)) {
                          revert("!Qty");
                      }
                      if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
                          revert("!MaxSupply");
                      }
                      if (currentClaimPhase.startTimestamp > block.timestamp) {
                          revert("cant claim yet");
                      }
                  }
                  /// @dev At any given moment, returns the uid for the active claim condition.
                  function getActiveClaimConditionId() public view returns (uint256) {
                      for (uint256 i = claimCondition.currentStartId + claimCondition.count; i > claimCondition.currentStartId; i--) {
                          if (block.timestamp >= claimCondition.conditions[i - 1].startTimestamp) {
                              return i - 1;
                          }
                      }
                      revert("!CONDITION.");
                  }
                  /// @dev Returns the claim condition at the given uid.
                  function getClaimConditionById(uint256 _conditionId) external view returns (ClaimCondition memory condition) {
                      condition = claimCondition.conditions[_conditionId];
                  }
                  /// @dev Returns the supply claimed by claimer for a given conditionId.
                  function getSupplyClaimedByWallet(uint256 _conditionId, address _claimer)
                      public
                      view
                      returns (uint256 supplyClaimedByWallet)
                  {
                      supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
                  }
                  /*////////////////////////////////////////////////////////////////////
                      Optional hooks that can be implemented in the derived contract
                  ///////////////////////////////////////////////////////////////////*/
                  /// @dev Exposes the ability to override the msg sender.
                  function _dropMsgSender() internal virtual returns (address) {
                      return msg.sender;
                  }
                  /// @dev Runs before every `claim` function call.
                  function _beforeClaim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) internal virtual {}
                  /// @dev Runs after every `claim` function call.
                  function _afterClaim(
                      address _receiver,
                      uint256 _quantity,
                      address _currency,
                      uint256 _pricePerToken,
                      AllowlistProof calldata _allowlistProof,
                      bytes memory _data
                  ) internal virtual {}
                  /*///////////////////////////////////////////////////////////////
                      Virtual functions: to be implemented in derived contract
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Collects and distributes the primary sale value of NFTs being claimed.
                  function _collectPriceOnClaim(
                      address _primarySaleRecipient,
                      uint256 _quantityToClaim,
                      address _currency,
                      uint256 _pricePerToken
                  ) internal virtual;
                  /// @dev Transfers the NFTs being claimed.
                  function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed)
                      internal
                      virtual
                      returns (uint256 startTokenId);
                  /// @dev Determine what wallet can update claim conditions
                  function _canSetClaimConditions() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/ILazyMint.sol";
              import "./BatchMintMetadata.sol";
              /**
               *  The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
               *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
               *  minting a non-zero balance of NFTs of those tokenIds.
               */
              abstract contract LazyMint is ILazyMint, BatchMintMetadata {
                  /// @notice The tokenId assigned to the next new NFT to be lazy minted.
                  uint256 internal nextTokenIdToLazyMint;
                  /**
                   *  @notice                  Lets an authorized address lazy mint a given amount of NFTs.
                   *
                   *  @param _amount           The number of NFTs to lazy mint.
                   *  @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                   *                           of those NFTs is `${baseURIForTokens}/${tokenId}`.
                   *  @param _data             Additional bytes data to be used at the discretion of the consumer of the contract.
                   *  @return batchId          A unique integer identifier for the batch of NFTs lazy minted together.
                   */
                  function lazyMint(
                      uint256 _amount,
                      string calldata _baseURIForTokens,
                      bytes calldata _data
                  ) public virtual override returns (uint256 batchId) {
                      if (!_canLazyMint()) {
                          revert("Not authorized");
                      }
                      if (_amount == 0) {
                          revert("0 amt");
                      }
                      uint256 startId = nextTokenIdToLazyMint;
                      (nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
                      emit TokensLazyMinted(startId, startId + _amount - 1, _baseURIForTokens, _data);
                      return batchId;
                  }
                  /// @dev Returns whether lazy minting can be performed in the given execution context.
                  function _canLazyMint() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "./interface/IOperatorFilterToggle.sol";
              abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                  bool public operatorRestriction;
                  function setOperatorRestriction(bool _restriction) external {
                      require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                      _setOperatorRestriction(_restriction);
                  }
                  function _setOperatorRestriction(bool _restriction) internal {
                      operatorRestriction = _restriction;
                      emit OperatorRestriction(_restriction);
                  }
                  function _canSetOperatorRestriction() internal virtual returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import "./interface/IOperatorFilterRegistry.sol";
              import "./OperatorFilterToggle.sol";
              abstract contract OperatorFiltererUpgradeable is OperatorFilterToggle {
                  error OperatorNotAllowed(address operator);
                  IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                  function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) internal {
                      // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
                      // will not revert, but the contract will need to be registered with the registry once it is deployed in
                      // order for the modifier to filter addresses.
                      if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                          if (!OPERATOR_FILTER_REGISTRY.isRegistered(address(this))) {
                              if (subscribe) {
                                  OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
                              } else {
                                  if (subscriptionOrRegistrantToCopy != address(0)) {
                                      OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                                  } else {
                                      OPERATOR_FILTER_REGISTRY.register(address(this));
                                  }
                              }
                          }
                      }
                  }
                  modifier onlyAllowedOperator(address from) virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (operatorRestriction) {
                          if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                              // Allow spending tokens from addresses with balance
                              // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                              // from an EOA.
                              if (from == msg.sender) {
                                  _;
                                  return;
                              }
                              if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), msg.sender)) {
                                  revert OperatorNotAllowed(msg.sender);
                              }
                          }
                      }
                      _;
                  }
                  modifier onlyAllowedOperatorApproval(address operator) virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (operatorRestriction) {
                          if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                              if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                                  revert OperatorNotAllowed(operator);
                              }
                          }
                      }
                      _;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IOwnable.sol";
              /**
               *  @title   Ownable
               *  @notice  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
               *           information about who the contract's owner is.
               */
              abstract contract Ownable is IOwnable {
                  /// @dev Owner of the contract (purpose: OpenSea compatibility)
                  address private _owner;
                  /// @dev Reverts if caller is not the owner.
                  modifier onlyOwner() {
                      if (msg.sender != _owner) {
                          revert("Not authorized");
                      }
                      _;
                  }
                  /**
                   *  @notice Returns the owner of the contract.
                   */
                  function owner() public view override returns (address) {
                      return _owner;
                  }
                  /**
                   *  @notice Lets an authorized wallet set a new owner for the contract.
                   *  @param _newOwner The address to set as the new owner of the contract.
                   */
                  function setOwner(address _newOwner) external override {
                      if (!_canSetOwner()) {
                          revert("Not authorized");
                      }
                      _setupOwner(_newOwner);
                  }
                  /// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
                  function _setupOwner(address _newOwner) internal {
                      address _prevOwner = _owner;
                      _owner = _newOwner;
                      emit OwnerUpdated(_prevOwner, _newOwner);
                  }
                  /// @dev Returns whether owner can be set in the given execution context.
                  function _canSetOwner() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPermissions.sol";
              import "../lib/TWStrings.sol";
              /**
               *  @title   Permissions
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
               */
              contract Permissions is IPermissions {
                  /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                  mapping(bytes32 => mapping(address => bool)) private _hasRole;
                  /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                  mapping(bytes32 => bytes32) private _getRoleAdmin;
                  /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role, msg.sender);
                      _;
                  }
                  /**
                   *  @notice         Checks whether an account has a particular role.
                   *  @dev            Returns `true` if `account` has been granted `role`.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account for which the role is being checked.
                   */
                  function hasRole(bytes32 role, address account) public view override returns (bool) {
                      return _hasRole[role][account];
                  }
                  /**
                   *  @notice         Checks whether an account has a particular role;
                   *                  role restrictions can be swtiched on and off.
                   *
                   *  @dev            Returns `true` if `account` has been granted `role`.
                   *                  Role restrictions can be swtiched on and off:
                   *                      - If address(0) has ROLE, then the ROLE restrictions
                   *                        don't apply.
                   *                      - If address(0) does not have ROLE, then the ROLE
                   *                        restrictions will apply.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account for which the role is being checked.
                   */
                  function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
                      if (!_hasRole[role][address(0)]) {
                          return _hasRole[role][account];
                      }
                      return true;
                  }
                  /**
                   *  @notice         Returns the admin role that controls the specified role.
                   *  @dev            See {grantRole} and {revokeRole}.
                   *                  To change a role's admin, use {_setRoleAdmin}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   */
                  function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
                      return _getRoleAdmin[role];
                  }
                  /**
                   *  @notice         Grants a role to an account, if not previously granted.
                   *  @dev            Caller must have admin role for the `role`.
                   *                  Emits {RoleGranted Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account to which the role is being granted.
                   */
                  function grantRole(bytes32 role, address account) public virtual override {
                      _checkRole(_getRoleAdmin[role], msg.sender);
                      if (_hasRole[role][account]) {
                          revert("Can only grant to non holders");
                      }
                      _setupRole(role, account);
                  }
                  /**
                   *  @notice         Revokes role from an account.
                   *  @dev            Caller must have admin role for the `role`.
                   *                  Emits {RoleRevoked Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account from which the role is being revoked.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override {
                      _checkRole(_getRoleAdmin[role], msg.sender);
                      _revokeRole(role, account);
                  }
                  /**
                   *  @notice         Revokes role from the account.
                   *  @dev            Caller must have the `role`, with caller being the same as `account`.
                   *                  Emits {RoleRevoked Event}.
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param account  Address of the account from which the role is being revoked.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      if (msg.sender != account) {
                          revert("Can only renounce for self");
                      }
                      _revokeRole(role, account);
                  }
                  /// @dev Sets `adminRole` as `role`'s admin role.
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = _getRoleAdmin[role];
                      _getRoleAdmin[role] = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _hasRole[role][account] = true;
                      emit RoleGranted(role, account, msg.sender);
                  }
                  /// @dev Revokes `role` from `account`
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      _checkRole(role, account);
                      delete _hasRole[role][account];
                      emit RoleRevoked(role, account, msg.sender);
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!_hasRole[role][account]) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "Permissions: account ",
                                      TWStrings.toHexString(uint160(account), 20),
                                      " is missing role ",
                                      TWStrings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
                      if (!hasRoleWithSwitch(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "Permissions: account ",
                                      TWStrings.toHexString(uint160(account), 20),
                                      " is missing role ",
                                      TWStrings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPermissionsEnumerable.sol";
              import "./Permissions.sol";
              /**
               *  @title   PermissionsEnumerable
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms.
               *           Also provides interfaces to view all members with a given role, and total count of members.
               */
              contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                  /**
                   *  @notice A data structure to store data of members for a given role.
                   *
                   *  @param index    Current index in the list of accounts that have a role.
                   *  @param members  map from index => address of account that has a role
                   *  @param indexOf  map from address => index which the account has.
                   */
                  struct RoleMembers {
                      uint256 index;
                      mapping(uint256 => address) members;
                      mapping(address => uint256) indexOf;
                  }
                  /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                  mapping(bytes32 => RoleMembers) private roleMembers;
                  /**
                   *  @notice         Returns the role-member from a list of members for a role,
                   *                  at a given index.
                   *  @dev            Returns `member` who has `role`, at `index` of role-members list.
                   *                  See struct {RoleMembers}, and mapping {roleMembers}
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *  @param index    Index in list of current members for the role.
                   *
                   *  @return member  Address of account that has `role`
                   */
                  function getRoleMember(bytes32 role, uint256 index) external view override returns (address member) {
                      uint256 currentIndex = roleMembers[role].index;
                      uint256 check;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (roleMembers[role].members[i] != address(0)) {
                              if (check == index) {
                                  member = roleMembers[role].members[i];
                                  return member;
                              }
                              check += 1;
                          } else if (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
                              check += 1;
                          }
                      }
                  }
                  /**
                   *  @notice         Returns total number of accounts that have a role.
                   *  @dev            Returns `count` of accounts that have `role`.
                   *                  See struct {RoleMembers}, and mapping {roleMembers}
                   *
                   *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
                   *
                   *  @return count   Total number of accounts that have `role`
                   */
                  function getRoleMemberCount(bytes32 role) external view override returns (uint256 count) {
                      uint256 currentIndex = roleMembers[role].index;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (roleMembers[role].members[i] != address(0)) {
                              count += 1;
                          }
                      }
                      if (hasRole(role, address(0))) {
                          count += 1;
                      }
                  }
                  /// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}
                  ///      See {_removeMember}
                  function _revokeRole(bytes32 role, address account) internal override {
                      super._revokeRole(role, account);
                      _removeMember(role, account);
                  }
                  /// @dev Grants `role` to `account`, and adds `account` to {roleMembers}
                  ///      See {_addMember}
                  function _setupRole(bytes32 role, address account) internal override {
                      super._setupRole(role, account);
                      _addMember(role, account);
                  }
                  /// @dev adds `account` to {roleMembers}, for `role`
                  function _addMember(bytes32 role, address account) internal {
                      uint256 idx = roleMembers[role].index;
                      roleMembers[role].index += 1;
                      roleMembers[role].members[idx] = account;
                      roleMembers[role].indexOf[account] = idx;
                  }
                  /// @dev removes `account` from {roleMembers}, for `role`
                  function _removeMember(bytes32 role, address account) internal {
                      uint256 idx = roleMembers[role].indexOf[account];
                      delete roleMembers[role].members[idx];
                      delete roleMembers[role].indexOf[account];
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPlatformFee.sol";
              /**
               *  @title   Platform Fee
               *  @notice  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
               *           that uses information about platform fees, if desired.
               */
              abstract contract PlatformFee is IPlatformFee {
                  /// @dev The address that receives all platform fees from all sales.
                  address private platformFeeRecipient;
                  /// @dev The % of primary sales collected as platform fees.
                  uint16 private platformFeeBps;
                  /// @dev Returns the platform fee recipient and bps.
                  function getPlatformFeeInfo() public view override returns (address, uint16) {
                      return (platformFeeRecipient, uint16(platformFeeBps));
                  }
                  /**
                   *  @notice         Updates the platform fee recipient and bps.
                   *  @dev            Caller should be authorized to set platform fee info.
                   *                  See {_canSetPlatformFeeInfo}.
                   *                  Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
                   *
                   *  @param _platformFeeRecipient   Address to be set as new platformFeeRecipient.
                   *  @param _platformFeeBps         Updated platformFeeBps.
                   */
                  function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override {
                      if (!_canSetPlatformFeeInfo()) {
                          revert("Not authorized");
                      }
                      _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
                  }
                  /// @dev Lets a contract admin update the platform fee recipient and bps
                  function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal {
                      if (_platformFeeBps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      platformFeeBps = uint16(_platformFeeBps);
                      platformFeeRecipient = _platformFeeRecipient;
                      emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
                  }
                  /// @dev Returns whether platform fee info can be set in the given execution context.
                  function _canSetPlatformFeeInfo() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IPrimarySale.sol";
              /**
               *  @title   Primary Sale
               *  @notice  Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
               *           primary sales, if desired.
               */
              abstract contract PrimarySale is IPrimarySale {
                  /// @dev The address that receives all primary sales value.
                  address private recipient;
                  /// @dev Returns primary sale recipient address.
                  function primarySaleRecipient() public view override returns (address) {
                      return recipient;
                  }
                  /**
                   *  @notice         Updates primary sale recipient.
                   *  @dev            Caller should be authorized to set primary sales info.
                   *                  See {_canSetPrimarySaleRecipient}.
                   *                  Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
                   *
                   *  @param _saleRecipient   Address to be set as new recipient of primary sales.
                   */
                  function setPrimarySaleRecipient(address _saleRecipient) external override {
                      if (!_canSetPrimarySaleRecipient()) {
                          revert("Not authorized");
                      }
                      _setupPrimarySaleRecipient(_saleRecipient);
                  }
                  /// @dev Lets a contract admin set the recipient for all primary sales.
                  function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                      recipient = _saleRecipient;
                      emit PrimarySaleRecipientUpdated(_saleRecipient);
                  }
                  /// @dev Returns whether primary sale recipient can be set in the given execution context.
                  function _canSetPrimarySaleRecipient() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./interface/IRoyalty.sol";
              /**
               *  @title   Royalty
               *  @notice  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *           the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
               *           that uses information about royalty fees, if desired.
               *
               *  @dev     The `Royalty` contract is ERC2981 compliant.
               */
              abstract contract Royalty is IRoyalty {
                  /// @dev The (default) address that receives all royalty value.
                  address private royaltyRecipient;
                  /// @dev The (default) % of a sale to take as royalty (in basis points).
                  uint16 private royaltyBps;
                  /// @dev Token ID => royalty recipient and bps for token
                  mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
                  /**
                   *  @notice   View royalty info for a given token and sale price.
                   *  @dev      Returns royalty amount and recipient for `tokenId` and `salePrice`.
                   *  @param tokenId          The tokenID of the NFT for which to query royalty info.
                   *  @param salePrice        Sale price of the token.
                   *
                   *  @return receiver        Address of royalty recipient account.
                   *  @return royaltyAmount   Royalty amount calculated at current royaltyBps value.
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice)
                      external
                      view
                      virtual
                      override
                      returns (address receiver, uint256 royaltyAmount)
                  {
                      (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
                      receiver = recipient;
                      royaltyAmount = (salePrice * bps) / 10_000;
                  }
                  /**
                   *  @notice          View royalty info for a given token.
                   *  @dev             Returns royalty recipient and bps for `_tokenId`.
                   *  @param _tokenId  The tokenID of the NFT for which to query royalty info.
                   */
                  function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
                      RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
                      return
                          royaltyForToken.recipient == address(0)
                              ? (royaltyRecipient, uint16(royaltyBps))
                              : (royaltyForToken.recipient, uint16(royaltyForToken.bps));
                  }
                  /**
                   *  @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
                   */
                  function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
                      return (royaltyRecipient, uint16(royaltyBps));
                  }
                  /**
                   *  @notice         Updates default royalty recipient and bps.
                   *  @dev            Caller should be authorized to set royalty info.
                   *                  See {_canSetRoyaltyInfo}.
                   *                  Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
                   *
                   *  @param _royaltyRecipient   Address to be set as default royalty recipient.
                   *  @param _royaltyBps         Updated royalty bps.
                   */
                  function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
                      if (!_canSetRoyaltyInfo()) {
                          revert("Not authorized");
                      }
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                  }
                  /// @dev Lets a contract admin update the default royalty recipient and bps.
                  function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
                      if (_royaltyBps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      royaltyRecipient = _royaltyRecipient;
                      royaltyBps = uint16(_royaltyBps);
                      emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                  }
                  /**
                   *  @notice         Updates default royalty recipient and bps for a particular token.
                   *  @dev            Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
                   *                  See {_canSetRoyaltyInfo}.
                   *                  Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
                   *
                   *  @param _recipient   Address to be set as royalty recipient for given token Id.
                   *  @param _bps         Updated royalty bps for the token Id.
                   */
                  function setRoyaltyInfoForToken(
                      uint256 _tokenId,
                      address _recipient,
                      uint256 _bps
                  ) external override {
                      if (!_canSetRoyaltyInfo()) {
                          revert("Not authorized");
                      }
                      _setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
                  }
                  /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
                  function _setupRoyaltyInfoForToken(
                      uint256 _tokenId,
                      address _recipient,
                      uint256 _bps
                  ) internal {
                      if (_bps > 10_000) {
                          revert("Exceeds max bps");
                      }
                      royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
                      emit RoyaltyForToken(_tokenId, _recipient, _bps);
                  }
                  /// @dev Returns whether royalty info can be set in the given execution context.
                  function _canSetRoyaltyInfo() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IClaimCondition {
                  /**
                   *  @notice The criteria that make up a claim condition.
                   *
                   *  @param startTimestamp                 The unix timestamp after which the claim condition applies.
                   *                                        The same claim condition applies until the `startTimestamp`
                   *                                        of the next claim condition.
                   *
                   *  @param maxClaimableSupply             The maximum total number of tokens that can be claimed under
                   *                                        the claim condition.
                   *
                   *  @param supplyClaimed                  At any given point, the number of tokens that have been claimed
                   *                                        under the claim condition.
                   *
                   *  @param quantityLimitPerWallet         The maximum number of tokens that can be claimed by a wallet.
                   *
                   *  @param merkleRoot                     The allowlist of addresses that can claim tokens under the claim
                   *                                        condition.
                   *
                   *  @param pricePerToken                  The price required to pay per token claimed.
                   *
                   *  @param currency                       The currency in which the `pricePerToken` must be paid.
                   *
                   *  @param metadata                       Claim condition metadata.
                   */
                  struct ClaimCondition {
                      uint256 startTimestamp;
                      uint256 maxClaimableSupply;
                      uint256 supplyClaimed;
                      uint256 quantityLimitPerWallet;
                      bytes32 merkleRoot;
                      uint256 pricePerToken;
                      address currency;
                      string metadata;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IClaimCondition.sol";
              /**
               *  The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IClaimConditionMultiPhase is IClaimCondition {
                  /**
                   *  @notice The set of all claim conditions, at any given moment.
                   *  Claim Phase ID = [currentStartId, currentStartId + length - 1];
                   *
                   *  @param currentStartId           The uid for the first claim condition amongst the current set of
                   *                                  claim conditions. The uid for each next claim condition is one
                   *                                  more than the previous claim condition's uid.
                   *
                   *  @param count                    The total number of phases / claim conditions in the list
                   *                                  of claim conditions.
                   *
                   *  @param conditions                   The claim conditions at a given uid. Claim conditions
                   *                                  are ordered in an ascending order by their `startTimestamp`.
                   *
                   *  @param supplyClaimedByWallet    Map from a claim condition uid and account to supply claimed by account.
                   */
                  struct ClaimConditionList {
                      uint256 currentStartId;
                      uint256 count;
                      mapping(uint256 => ClaimCondition) conditions;
                      mapping(uint256 => mapping(address => uint256)) supplyClaimedByWallet;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
               *  for you contract.
               *
               *  Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
               */
              interface IContractMetadata {
                  /// @dev Returns the metadata URI of the contract.
                  function contractURI() external view returns (string memory);
                  /**
                   *  @dev Sets contract URI for the storefront-level metadata of the contract.
                   *       Only module admin can call this function.
                   */
                  function setContractURI(string calldata _uri) external;
                  /// @dev Emitted when the contract URI is updated.
                  event ContractURIUpdated(string prevURI, string newURI);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
               *  'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
               */
              interface IDelayedReveal {
                  /// @dev Emitted when tokens are revealed.
                  event TokenURIRevealed(uint256 indexed index, string revealedURI);
                  /**
                   *  @notice Reveals a batch of delayed reveal NFTs.
                   *
                   *  @param identifier The ID for the batch of delayed-reveal NFTs to reveal.
                   *
                   *  @param key        The key with which the base URI for the relevant batch of NFTs was encrypted.
                   */
                  function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI);
                  /**
                   *  @notice Performs XOR encryption/decryption.
                   *
                   *  @param data The data to encrypt. In the case of delayed-reveal NFTs, this is the "revealed" state
                   *              base URI of the relevant batch of NFTs.
                   *
                   *  @param key  The key with which to encrypt data
                   */
                  function encryptDecrypt(bytes memory data, bytes calldata key) external pure returns (bytes memory result);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IClaimConditionMultiPhase.sol";
              /**
               *  The interface `IDrop` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
               *
               *  An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
               *  A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
               *  or added to by the contract admin. At any moment, there is only one active claim condition.
               */
              interface IDrop is IClaimConditionMultiPhase {
                  /**
                   *  @param proof Prood of concerned wallet's inclusion in an allowlist.
                   *  @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
                   *  @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
                   *  @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
                   */
                  struct AllowlistProof {
                      bytes32[] proof;
                      uint256 quantityLimitPerWallet;
                      uint256 pricePerToken;
                      address currency;
                  }
                  /// @notice Emitted when tokens are claimed via `claim`.
                  event TokensClaimed(
                      uint256 indexed claimConditionIndex,
                      address indexed claimer,
                      address indexed receiver,
                      uint256 startTokenId,
                      uint256 quantityClaimed
                  );
                  /// @notice Emitted when the contract's claim conditions are updated.
                  event ClaimConditionsUpdated(ClaimCondition[] claimConditions, bool resetEligibility);
                  /**
                   *  @notice Lets an account claim a given quantity of NFTs.
                   *
                   *  @param receiver                       The receiver of the NFTs to claim.
                   *  @param quantity                       The quantity of NFTs to claim.
                   *  @param currency                       The currency in which to pay for the claim.
                   *  @param pricePerToken                  The price per token to pay for the claim.
                   *  @param allowlistProof                 The proof of the claimer's inclusion in the merkle root allowlist
                   *                                        of the claim conditions that apply.
                   *  @param data                           Arbitrary bytes data that can be leveraged in the implementation of this interface.
                   */
                  function claim(
                      address receiver,
                      uint256 quantity,
                      address currency,
                      uint256 pricePerToken,
                      AllowlistProof calldata allowlistProof,
                      bytes memory data
                  ) external payable;
                  /**
                   *  @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
                   *
                   *  @param phases                   Claim conditions in ascending order by `startTimestamp`.
                   *
                   *  @param resetClaimEligibility    Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
                   *                                  in the new claim conditions being set.
                   *
                   */
                  function setClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
               *  at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
               *  minting a non-zero balance of NFTs of those tokenIds.
               */
              interface ILazyMint {
                  /// @dev Emitted when tokens are lazy minted.
                  event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
                  /**
                   *  @notice Lazy mints a given amount of NFTs.
                   *
                   *  @param amount           The number of NFTs to lazy mint.
                   *
                   *  @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
                   *                          of those NFTs is `${baseURIForTokens}/${tokenId}`.
                   *
                   *  @param extraData        Additional bytes data to be used at the discretion of the consumer of the contract.
                   *
                   *  @return batchId         A unique integer identifier for the batch of NFTs lazy minted together.
                   */
                  function lazyMint(
                      uint256 amount,
                      string calldata baseURIForTokens,
                      bytes calldata extraData
                  ) external returns (uint256 batchId);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              interface IOperatorFilterRegistry {
                  function isOperatorAllowed(address registrant, address operator) external view returns (bool);
                  function register(address registrant) external;
                  function registerAndSubscribe(address registrant, address subscription) external;
                  function registerAndCopyEntries(address registrant, address registrantToCopy) external;
                  function unregister(address addr) external;
                  function updateOperator(
                      address registrant,
                      address operator,
                      bool filtered
                  ) external;
                  function updateOperators(
                      address registrant,
                      address[] calldata operators,
                      bool filtered
                  ) external;
                  function updateCodeHash(
                      address registrant,
                      bytes32 codehash,
                      bool filtered
                  ) external;
                  function updateCodeHashes(
                      address registrant,
                      bytes32[] calldata codeHashes,
                      bool filtered
                  ) external;
                  function subscribe(address registrant, address registrantToSubscribe) external;
                  function unsubscribe(address registrant, bool copyExistingEntries) external;
                  function subscriptionOf(address addr) external returns (address registrant);
                  function subscribers(address registrant) external returns (address[] memory);
                  function subscriberAt(address registrant, uint256 index) external returns (address);
                  function copyEntriesOf(address registrant, address registrantToCopy) external;
                  function isOperatorFiltered(address registrant, address operator) external returns (bool);
                  function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
                  function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
                  function filteredOperators(address addr) external returns (address[] memory);
                  function filteredCodeHashes(address addr) external returns (bytes32[] memory);
                  function filteredOperatorAt(address registrant, uint256 index) external returns (address);
                  function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
                  function isRegistered(address addr) external returns (bool);
                  function codeHashOf(address addr) external returns (bytes32);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              interface IOperatorFilterToggle {
                  event OperatorRestriction(bool restriction);
                  function operatorRestriction() external view returns (bool);
                  function setOperatorRestriction(bool restriction) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
               *  information about who the contract's owner is.
               */
              interface IOwnable {
                  /// @dev Returns the owner of the contract.
                  function owner() external view returns (address);
                  /// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
                  function setOwner(address _newOwner) external;
                  /// @dev Emitted when a new Owner is set.
                  event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IPermissions {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "./IPermissions.sol";
              /**
               * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
               */
              interface IPermissionsEnumerable is IPermissions {
                  /**
                   * @dev Returns one of the accounts that have `role`. `index` must be a
                   * value between 0 and {getRoleMemberCount}, non-inclusive.
                   *
                   * Role bearers are not sorted in any particular way, and their ordering may
                   * change at any point.
                   *
                   * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
                   * you perform all queries on the same block. See the following
                   * [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
                   * for more information.
                   */
                  function getRoleMember(bytes32 role, uint256 index) external view returns (address);
                  /**
                   * @dev Returns the number of accounts that have `role`. Can be used
                   * together with {getRoleMember} to enumerate all bearers of a role.
                   */
                  function getRoleMemberCount(bytes32 role) external view returns (uint256);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
               *  that uses information about platform fees, if desired.
               */
              interface IPlatformFee {
                  /// @dev Returns the platform fee bps and recipient.
                  function getPlatformFeeInfo() external view returns (address, uint16);
                  /// @dev Lets a module admin update the fees on primary sales.
                  function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
                  /// @dev Emitted when fee on primary sales is updated.
                  event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /**
               *  Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
               *  primary sales, if desired.
               */
              interface IPrimarySale {
                  /// @dev The adress that receives all primary sales value.
                  function primarySaleRecipient() external view returns (address);
                  /// @dev Lets a module admin set the default recipient of all primary sales.
                  function setPrimarySaleRecipient(address _saleRecipient) external;
                  /// @dev Emitted when a new sale recipient is set.
                  event PrimarySaleRecipientUpdated(address indexed recipient);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "../../eip/interface/IERC2981.sol";
              /**
               *  Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
               *  the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
               *  that uses information about royalty fees, if desired.
               *
               *  The `Royalty` contract is ERC2981 compliant.
               */
              interface IRoyalty is IERC2981 {
                  struct RoyaltyInfo {
                      address recipient;
                      uint256 bps;
                  }
                  /// @dev Returns the royalty recipient and fee bps.
                  function getDefaultRoyaltyInfo() external view returns (address, uint16);
                  /// @dev Lets a module admin update the royalty bps and recipient.
                  function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
                  /// @dev Lets a module admin set the royalty recipient for a particular token Id.
                  function setRoyaltyInfoForToken(
                      uint256 tokenId,
                      address recipient,
                      uint256 bps
                  ) external;
                  /// @dev Returns the royalty recipient for a particular token Id.
                  function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
                  /// @dev Emitted when royalty info is updated.
                  event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                  /// @dev Emitted when royalty recipient for tokenId is set
                  event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              interface IWETH {
                  function deposit() external payable;
                  function withdraw(uint256 amount) external;
                  function transfer(address to, uint256 value) external returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              // Helper interfaces
              import { IWETH } from "../interfaces/IWETH.sol";
              import "../openzeppelin-presets/token/ERC20/utils/SafeERC20.sol";
              library CurrencyTransferLib {
                  using SafeERC20 for IERC20;
                  /// @dev The address interpreted as native token of the chain.
                  address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                  /// @dev Transfers a given amount of currency.
                  function transferCurrency(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount
                  ) internal {
                      if (_amount == 0) {
                          return;
                      }
                      if (_currency == NATIVE_TOKEN) {
                          safeTransferNativeToken(_to, _amount);
                      } else {
                          safeTransferERC20(_currency, _from, _to, _amount);
                      }
                  }
                  /// @dev Transfers a given amount of currency. (With native token wrapping)
                  function transferCurrencyWithWrapper(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount,
                      address _nativeTokenWrapper
                  ) internal {
                      if (_amount == 0) {
                          return;
                      }
                      if (_currency == NATIVE_TOKEN) {
                          if (_from == address(this)) {
                              // withdraw from weth then transfer withdrawn native token to recipient
                              IWETH(_nativeTokenWrapper).withdraw(_amount);
                              safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                          } else if (_to == address(this)) {
                              // store native currency in weth
                              require(_amount == msg.value, "msg.value != amount");
                              IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
                          } else {
                              safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
                          }
                      } else {
                          safeTransferERC20(_currency, _from, _to, _amount);
                      }
                  }
                  /// @dev Transfer `amount` of ERC20 token from `from` to `to`.
                  function safeTransferERC20(
                      address _currency,
                      address _from,
                      address _to,
                      uint256 _amount
                  ) internal {
                      if (_from == _to) {
                          return;
                      }
                      if (_from == address(this)) {
                          IERC20(_currency).safeTransfer(_to, _amount);
                      } else {
                          IERC20(_currency).safeTransferFrom(_from, _to, _amount);
                      }
                  }
                  /// @dev Transfers `amount` of native token to `to`.
                  function safeTransferNativeToken(address to, uint256 value) internal {
                      // solhint-disable avoid-low-level-calls
                      // slither-disable-next-line low-level-calls
                      (bool success, ) = to.call{ value: value }("");
                      require(success, "native token transfer failed");
                  }
                  /// @dev Transfers `amount` of native token to `to`. (With native token wrapping)
                  function safeTransferNativeTokenWithWrapper(
                      address to,
                      uint256 value,
                      address _nativeTokenWrapper
                  ) internal {
                      // solhint-disable avoid-low-level-calls
                      // slither-disable-next-line low-level-calls
                      (bool success, ) = to.call{ value: value }("");
                      if (!success) {
                          IWETH(_nativeTokenWrapper).deposit{ value: value }();
                          IERC20(_nativeTokenWrapper).safeTransfer(to, value);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/utils/cryptography/MerkleProof.sol
              // Copied from https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
              pragma solidity ^0.8.0;
              /**
               * @dev These functions deal with verification of Merkle Trees proofs.
               *
               * The proofs can be generated using the JavaScript library
               * https://github.com/miguelmota/merkletreejs[merkletreejs].
               * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
               *
               * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
               *
               * Source: https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol
               */
              library MerkleProof {
                  /**
                   * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
                   * defined by `root`. For this, a `proof` must be provided, containing
                   * sibling hashes on the branch from the leaf to the root of the tree. Each
                   * pair of leaves and each pair of pre-images are assumed to be sorted.
                   */
                  function verify(
                      bytes32[] memory proof,
                      bytes32 root,
                      bytes32 leaf
                  ) internal pure returns (bool, uint256) {
                      bytes32 computedHash = leaf;
                      uint256 index = 0;
                      for (uint256 i = 0; i < proof.length; i++) {
                          index *= 2;
                          bytes32 proofElement = proof[i];
                          if (computedHash <= proofElement) {
                              // Hash(current computed hash + current element of the proof)
                              computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
                          } else {
                              // Hash(current element of the proof + current computed hash)
                              computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
                              index += 1;
                          }
                      }
                      // Check if the computed hash (root) is equal to the provided root
                      return (computedHash == root, index);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library TWAddress {
                  /**
                   * @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.
                   *
                   * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) 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/Strings.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev String operations.
               */
              library TWStrings {
                  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.0 (metatx/ERC2771Context.sol)
              pragma solidity ^0.8.11;
              import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              /**
               * @dev Context variant with ERC2771 support.
               */
              abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
                  mapping(address => bool) private _trustedForwarder;
                  function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                      __Context_init_unchained();
                      __ERC2771Context_init_unchained(trustedForwarder);
                  }
                  function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                      for (uint256 i = 0; i < trustedForwarder.length; i++) {
                          _trustedForwarder[trustedForwarder[i]] = true;
                      }
                  }
                  function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                      return _trustedForwarder[forwarder];
                  }
                  function _msgSender() internal view virtual override returns (address sender) {
                      if (isTrustedForwarder(msg.sender)) {
                          // The assembly code is more direct than the Solidity version using `abi.decode`.
                          assembly {
                              sender := shr(96, calldataload(sub(calldatasize(), 20)))
                          }
                      } else {
                          return super._msgSender();
                      }
                  }
                  function _msgData() internal view virtual override returns (bytes calldata) {
                      if (isTrustedForwarder(msg.sender)) {
                          return msg.data[:msg.data.length - 20];
                      } else {
                          return super._msgData();
                      }
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../../../../eip/interface/IERC20.sol";
              import "../../../../lib/TWAddress.sol";
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using TWAddress for address;
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
                  /**
                   * @dev Deprecated. This function has issues similar to the ones found in
                   * {IERC20-approve}, and its usage is discouraged.
                   *
                   * Whenever possible, use {safeIncreaseAllowance} and
                   * {safeDecreaseAllowance} instead.
                   */
                  function safeApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
                  function safeIncreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 newAllowance = token.allowance(address(this), spender) + value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
                  function safeDecreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      unchecked {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                          uint256 newAllowance = oldAllowance - value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                      // the target address contains contract code and also asserts for success in the low-level call.
                      bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
              pragma solidity ^0.8.0;
              import "../utils/introspection/IERC165Upgradeable.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 IERC2981Upgradeable is IERC165Upgradeable {
                  /**
                   * @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 (last updated v4.7.0) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.2;
              import "../../utils/AddressUpgradeable.sol";
              /**
               * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
               * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
               * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
               * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
               *
               * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
               * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
               * case an upgrade adds a module that needs to be initialized.
               *
               * For example:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * contract MyToken is ERC20Upgradeable {
               *     function initialize() initializer public {
               *         __ERC20_init("MyToken", "MTK");
               *     }
               * }
               * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
               *     function initializeV2() reinitializer(2) public {
               *         __ERC20Permit_init("MyToken");
               *     }
               * }
               * ```
               *
               * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
               * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
               *
               * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
               * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
               *
               * [CAUTION]
               * ====
               * Avoid leaving a contract uninitialized.
               *
               * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
               * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
               * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * /// @custom:oz-upgrades-unsafe-allow constructor
               * constructor() {
               *     _disableInitializers();
               * }
               * ```
               * ====
               */
              abstract contract Initializable {
                  /**
                   * @dev Indicates that the contract has been initialized.
                   * @custom:oz-retyped-from bool
                   */
                  uint8 private _initialized;
                  /**
                   * @dev Indicates that the contract is in the process of being initialized.
                   */
                  bool private _initializing;
                  /**
                   * @dev Triggered when the contract has been initialized or reinitialized.
                   */
                  event Initialized(uint8 version);
                  /**
                   * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                   * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`.
                   */
                  modifier initializer() {
                      bool isTopLevelCall = !_initializing;
                      require(
                          (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                          "Initializable: contract is already initialized"
                      );
                      _initialized = 1;
                      if (isTopLevelCall) {
                          _initializing = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          _initializing = false;
                          emit Initialized(1);
                      }
                  }
                  /**
                   * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                   * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                   * used to initialize parent contracts.
                   *
                   * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
                   * initialization step. This is essential to configure modules that are added through upgrades and that require
                   * initialization.
                   *
                   * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                   * a contract, executing them in the right order is up to the developer or operator.
                   */
                  modifier reinitializer(uint8 version) {
                      require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                      _initialized = version;
                      _initializing = true;
                      _;
                      _initializing = false;
                      emit Initialized(version);
                  }
                  /**
                   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                   * {initializer} and {reinitializer} modifiers, directly or indirectly.
                   */
                  modifier onlyInitializing() {
                      require(_initializing, "Initializable: contract is not initializing");
                      _;
                  }
                  /**
                   * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                   * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                   * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                   * through proxies.
                   */
                  function _disableInitializers() internal virtual {
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized < type(uint8).max) {
                          _initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
              }
              // 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 IERC721ReceiverUpgradeable {
                  /**
                   * @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 (last updated v4.7.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165Upgradeable.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721Upgradeable is IERC165Upgradeable {
                  /**
                   * @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 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 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721Upgradeable.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                  /**
                   * @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.7.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library AddressUpgradeable {
                  /**
                   * @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 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
                              /// @solidity memory-safe-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;
              import "../proxy/utils/Initializable.sol";
              /**
               * @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 ContextUpgradeable is Initializable {
                  function __Context_init() internal onlyInitializing {
                  }
                  function __Context_init_unchained() internal onlyInitializing {
                  }
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
              pragma solidity ^0.8.0;
              import "./AddressUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              abstract contract MulticallUpgradeable is Initializable {
                  function __Multicall_init() internal onlyInitializing {
                  }
                  function __Multicall_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev Receives and executes a batch of function calls on this contract.
                   */
                  function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
                      results = new bytes[](data.length);
                      for (uint256 i = 0; i < data.length; i++) {
                          results[i] = _functionDelegateCall(address(this), data[i]);
                      }
                      return results;
                  }
                  /**
                   * @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) private returns (bytes memory) {
                      require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev String operations.
               */
              library StringsUpgradeable {
                  bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      // Inspired by OraclizeAPI's implementation - MIT licence
                      // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
                      if (value == 0) {
                          return "0";
                      }
                      uint256 temp = value;
                      uint256 digits;
                      while (temp != 0) {
                          digits++;
                          temp /= 10;
                      }
                      bytes memory buffer = new bytes(digits);
                      while (value != 0) {
                          digits -= 1;
                          buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
                          value /= 10;
                      }
                      return string(buffer);
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      if (value == 0) {
                          return "0x00";
                      }
                      uint256 temp = value;
                      uint256 length = 0;
                      while (temp != 0) {
                          length++;
                          temp >>= 8;
                      }
                      return toHexString(value, length);
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _HEX_SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.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 ERC165Upgradeable is Initializable, IERC165Upgradeable {
                  function __ERC165_init() internal onlyInitializing {
                  }
                  function __ERC165_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165Upgradeable).interfaceId;
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // 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 IERC165Upgradeable {
                  /**
                   * @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
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
              import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721MetadataUpgradeable.sol";
              /**
               * @dev Interface of an ERC721A compliant contract.
               */
              interface IERC721AUpgradeable is IERC721Upgradeable, IERC721MetadataUpgradeable {
                  /**
                   * The caller must own the token or be an approved operator.
                   */
                  error ApprovalCallerNotOwnerNorApproved();
                  /**
                   * The token does not exist.
                   */
                  error ApprovalQueryForNonexistentToken();
                  /**
                   * The caller cannot approve to their own address.
                   */
                  error ApproveToCaller();
                  /**
                   * The caller cannot approve to the current owner.
                   */
                  error ApprovalToCurrentOwner();
                  /**
                   * Cannot query the balance for the zero address.
                   */
                  error BalanceQueryForZeroAddress();
                  /**
                   * Cannot mint to the zero address.
                   */
                  error MintToZeroAddress();
                  /**
                   * The quantity of tokens minted must be more than zero.
                   */
                  error MintZeroQuantity();
                  /**
                   * The token does not exist.
                   */
                  error OwnerQueryForNonexistentToken();
                  /**
                   * The caller must own the token or be an approved operator.
                   */
                  error TransferCallerNotOwnerNorApproved();
                  /**
                   * The token must be owned by `from`.
                   */
                  error TransferFromIncorrectOwner();
                  /**
                   * Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
                   */
                  error TransferToNonERC721ReceiverImplementer();
                  /**
                   * Cannot transfer to the zero address.
                   */
                  error TransferToZeroAddress();
                  /**
                   * The token does not exist.
                   */
                  error URIQueryForNonexistentToken();
                  // Compiler will pack this into a single 256bit word.
                  struct TokenOwnership {
                      // The address of the owner.
                      address addr;
                      // Keeps track of the start time of ownership with minimal overhead for tokenomics.
                      uint64 startTimestamp;
                      // Whether the token has been burned.
                      bool burned;
                  }
                  // Compiler will pack this into a single 256bit word.
                  struct AddressData {
                      // Realistically, 2**64-1 is more than enough.
                      uint64 balance;
                      // Keeps track of mint count with minimal overhead for tokenomics.
                      uint64 numberMinted;
                      // Keeps track of burn count with minimal overhead for tokenomics.
                      uint64 numberBurned;
                      // For miscellaneous variable(s) pertaining to the address
                      // (e.g. number of whitelist mint slots used).
                      // If there are multiple variables, please pack them into a uint64.
                      uint64 aux;
                  }
                  /**
                   * @dev Returns the total amount of tokens stored by the contract.
                   * 
                   * Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
                   */
                  function totalSupply() external view returns (uint256);
              }