ETH Price: $2,466.29 (+10.95%)

Transaction Decoder

Block:
19612144 at Apr-08-2024 04:30:23 PM +UTC
Transaction Fee:
0.008036074732869945 ETH $19.82
Gas Used:
183,545 Gas / 43.782585921 Gwei

Emitted Events:

128 TieredDrop.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000007a195e1d1d09e9a3bfb3e3be4e2d3b1462f7c6c8, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000d12 )
129 TieredDrop.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000007a195e1d1d09e9a3bfb3e3be4e2d3b1462f7c6c8, 0x00000000000000000000000056b145cc7aeb7b123b8f40df2d51d6feb2a27a26, 0x0000000000000000000000000000000000000000000000000000000000000d12 )
130 ERC1967Proxy.0x7dc5c0699ac8dd5250cbe368a2fc3b4a2daadb120ad07f6cccea29f83482686e( 0x7dc5c0699ac8dd5250cbe368a2fc3b4a2daadb120ad07f6cccea29f83482686e, a0652efcafa2efba08601b19d64db3e8e593259e9c1993b4210ee46b9f2f8fd2, 0000000000000000000d12007a195e1d1d09e9a3bfb3e3be4e2d3b1462f7c6c8, 000000004c0cdef73924000059325733eb952a92e069c87f0a6168b29e80627f, 000000000000000000000032a1640edd7b69a3bdf98cd9a6a61f663dcf6d2aa2 )

Account State Difference:

  Address   Before After State Difference Code
4.693654850191079187 Eth4.693838395191079187 Eth0.000183545
0x56B145CC...eb2a27A26
5.652631705838213096 Eth
Nonce: 1525
0.164595631105343151 Eth
Nonce: 1526
5.488036074732869945
0x59325733...29E80627f
0x7A195e1D...462f7c6c8 0.012185528509368118 Eth5.464785528509368118 Eth5.4526
0xA1640edd...DCf6D2Aa2 526.757366327632375349 Eth526.784766327632375349 Eth0.0274
0xb2ecfE4E...e2410CEA5
(Blur.io: Marketplace 3)

Execution Trace

ETH 5.48 ERC1967Proxy.70bce2d6( )
  • ETH 5.48 BlurExchangeV2.takeAskSingle( )
    • Null: 0x000...001.3119fa1c( )
    • Null: 0x000...001.03006ba6( )
    • Delegate.transfer( taker=0x56B145CC7AEb7b123B8f40dF2D51D6Feb2a27A26, orderType=0, transfers=, length=1 ) => ( successful=[true] )
      • TieredDrop.42842e0e( )
        • TieredDrop.42842e0e( )
          • 0x82102a85ca8b0ce926927170a725b68055f34f57.42842e0e( )
            • OperatorFilterRegistry.isOperatorAllowed( registrant=0x59325733eb952a92e069C87F0A6168b29E80627f, operator=0x2f18F339620a63e43f0839Eeb18D7de1e1Be4DfB ) => ( True )
            • OperatorFilterRegistry.isOperatorAllowed( registrant=0x59325733eb952a92e069C87F0A6168b29E80627f, operator=0x2f18F339620a63e43f0839Eeb18D7de1e1Be4DfB ) => ( True )
            • ETH 0.0274 0xa1640edd7b69a3bdf98cd9a6a61f663dcf6d2aa2.CALL( )
            • ETH 5.4526 0x7a195e1d1d09e9a3bfb3e3be4e2d3b1462f7c6c8.CALL( )
              File 1 of 6: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity 0.8.17;
              import "lib/openzeppelin-contracts/contracts/proxy/Proxy.sol";
              import "lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internal call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/IERC1967.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               */
              abstract contract ERC1967Upgrade is IERC1967 {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967 {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [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://consensys.net/diligence/blog/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.8.0/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 functionCallWithValue(target, data, 0, "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");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, 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) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, 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) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or 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 {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // 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 (last updated v4.7.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```solidity
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 2 of 6: TieredDrop
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              ////////// CHANGELOG: turn `approve` to virtual //////////
              import "../../eip/interface/IERC721A.sol";
              import "../../eip/interface/IERC721Receiver.sol";
              import "../../lib/TWAddress.sol";
              import "../../openzeppelin-presets/utils/Context.sol";
              import "../../lib/TWStrings.sol";
              import "../../eip/ERC165.sol";
              import "../extension/Initializable.sol";
              library ERC721AStorage {
                  bytes32 public constant ERC721A_STORAGE_POSITION = keccak256("erc721.a.storage");
                  struct Data {
                      // The tokenId of the next token to be minted.
                      uint256 _currentIndex;
                      // The number of tokens burned.
                      uint256 _burnCounter;
                      // Token name
                      string _name;
                      // Token symbol
                      string _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 => IERC721A.TokenOwnership) _ownerships;
                      // Mapping owner address to address data
                      mapping(address => IERC721A.AddressData) _addressData;
                      // Mapping from token ID to approved address
                      mapping(uint256 => address) _tokenApprovals;
                      // Mapping from owner to operator approvals
                      mapping(address => mapping(address => bool)) _operatorApprovals;
                  }
                  function erc721AStorage() internal pure returns (Data storage erc721AData) {
                      bytes32 position = ERC721A_STORAGE_POSITION;
                      assembly {
                          erc721AData.slot := position
                      }
                  }
              }
              /**
               * @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, Context, ERC165, IERC721A {
                  using TWAddress for address;
                  using TWStrings for uint256;
                  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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._name = name_;
                      data._symbol = symbol_;
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      // Counter underflow is impossible as _burnCounter cannot be incremented
                      // more than _currentIndex - _startTokenId() times
                      unchecked {
                          return data._currentIndex - data._burnCounter - _startTokenId();
                      }
                  }
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function _totalMinted() internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      // Counter underflow is impossible as _currentIndex does not decrement,
                      // and it is initialized to _startTokenId()
                      unchecked {
                          return data._currentIndex - _startTokenId();
                      }
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
                      return
                          interfaceId == type(IERC721).interfaceId ||
                          interfaceId == type(IERC721Metadata).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view override returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (owner == address(0)) revert BalanceQueryForZeroAddress();
                      return uint256(data._addressData[owner].balance);
                  }
                  /**
                   * Returns the number of tokens minted by `owner`.
                   */
                  function _numberMinted(address owner) internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return uint256(data._addressData[owner].numberMinted);
                  }
                  /**
                   * Returns the number of tokens burned by or on behalf of `owner`.
                   */
                  function _numberBurned(address owner) internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return uint256(data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < data._currentIndex) {
                                  TokenOwnership memory ownership = data._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 = data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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 virtual override returns (address) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return data._tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (operator == _msgSender()) revert ApproveToCaller();
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return _startTokenId() <= tokenId && tokenId < data._currentIndex && !data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 startTokenId = data._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 {
                          data._addressData[to].balance += uint64(quantity);
                          data._addressData[to].numberMinted += uint64(quantity);
                          data._ownerships[startTokenId].addr = to;
                          data._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 (data._currentIndex != startTokenId) revert();
                          } else {
                              do {
                                  emit Transfer(address(0), to, updatedIndex++);
                              } while (updatedIndex < end);
                          }
                          data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 startTokenId = data._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 {
                          data._addressData[to].balance += uint64(quantity);
                          data._addressData[to].numberMinted += uint64(quantity);
                          data._ownerships[startTokenId].addr = to;
                          data._ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          do {
                              emit Transfer(address(0), to, updatedIndex++);
                          } while (updatedIndex < end);
                          data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      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 {
                          data._addressData[from].balance -= 1;
                          data._addressData[to].balance += 1;
                          TokenOwnership storage currSlot = data._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 = data._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 != data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      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 = data._addressData[from];
                          addressData.balance -= 1;
                          addressData.numberBurned += 1;
                          // Keep track of who burned the token, and the timestamp of burning.
                          TokenOwnership storage currSlot = data._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 = data._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 != data._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 {
                          data._burnCounter++;
                      }
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits a {Approval} event.
                   */
                  function _approve(
                      address to,
                      uint256 tokenId,
                      address owner
                  ) private {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._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 IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                          return retval == IERC721Receiver(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 {}
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IContractMetadata.sol";
              /**
               *  @author  thirdweb.com
               *
               *  @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.
               */
              library ContractMetadataStorage {
                  bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION = keccak256("contract.metadata.storage");
                  struct Data {
                      /// @notice Returns the contract metadata URI.
                      string contractURI;
                  }
                  function contractMetadataStorage() internal pure returns (Data storage contractMetadataData) {
                      bytes32 position = CONTRACT_METADATA_STORAGE_POSITION;
                      assembly {
                          contractMetadataData.slot := position
                      }
                  }
              }
              abstract contract ContractMetadata is IContractMetadata {
                  /**
                   *  @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 {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      string memory prevURI = data.contractURI;
                      data.contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
                  /// @notice Returns the contract metadata URI.
                  function contractURI() public view virtual override returns (string memory) {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      return data.contractURI;
                  }
                  /// @dev Returns whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
              pragma solidity ^0.8.0;
              import "../../openzeppelin-presets/utils/cryptography/ECDSA.sol";
              import "./Initializable.sol";
              library EIP712Storage {
                  bytes32 public constant EIP712_STORAGE_POSITION = keccak256("eip712.storage");
                  struct Data {
                      /* solhint-disable var-name-mixedcase */
                      bytes32 _HASHED_NAME;
                      bytes32 _HASHED_VERSION;
                      /* solhint-enable var-name-mixedcase */
                  }
                  function eip712Storage() internal pure returns (Data storage eip712Data) {
                      bytes32 position = EIP712_STORAGE_POSITION;
                      assembly {
                          eip712Data.slot := position
                      }
                  }
              }
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * _Available since v3.4._
               *
               * @custom:storage-size 52
               */
              abstract contract EIP712Upgradeable is Initializable {
                  /* solhint-disable var-name-mixedcase */
                  bytes32 private constant _TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  /* solhint-enable var-name-mixedcase */
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                      __EIP712_init_unchained(name, version);
                  }
                  function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                      bytes32 hashedName = keccak256(bytes(name));
                      bytes32 hashedVersion = keccak256(bytes(version));
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      data._HASHED_NAME = hashedName;
                      data._HASHED_VERSION = hashedVersion;
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                  }
                  function _buildDomainSeparator(
                      bytes32 typeHash,
                      bytes32 nameHash,
                      bytes32 versionHash
                  ) private view returns (bytes32) {
                      return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev The hash of the name parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712NameHash() internal view virtual returns (bytes32) {
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      return data._HASHED_NAME;
                  }
                  /**
                   * @dev The hash of the version parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712VersionHash() internal view virtual returns (bytes32) {
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      return data._HASHED_VERSION;
                  }
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "../../extension/interface/IERC2771Context.sol";
              import "./Initializable.sol";
              /**
               * @dev Context variant with ERC2771 support.
               */
              library ERC2771ContextStorage {
                  bytes32 public constant ERC2771_CONTEXT_STORAGE_POSITION = keccak256("erc2771.context.storage");
                  struct Data {
                      mapping(address => bool) trustedForwarder;
                  }
                  function erc2771ContextStorage() internal pure returns (Data storage erc2771ContextData) {
                      bytes32 position = ERC2771_CONTEXT_STORAGE_POSITION;
                      assembly {
                          erc2771ContextData.slot := position
                      }
                  }
              }
              /**
               * @dev Context variant with ERC2771 support.
               */
              abstract contract ERC2771ContextUpgradeable is Initializable {
                  function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                      __ERC2771Context_init_unchained(trustedForwarder);
                  }
                  function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                      ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage();
                      for (uint256 i = 0; i < trustedForwarder.length; i++) {
                          data.trustedForwarder[trustedForwarder[i]] = true;
                      }
                  }
                  function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                      ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage();
                      return data.trustedForwarder[forwarder];
                  }
                  function _msgSender() internal view virtual 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 msg.sender;
                      }
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      if (isTrustedForwarder(msg.sender)) {
                          return msg.data[:msg.data.length - 20];
                      } else {
                          return msg.data;
                      }
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "../../lib/TWAddress.sol";
              library InitStorage {
                  /// @dev The location of the storage of the entrypoint contract's data.
                  bytes32 constant INIT_STORAGE_POSITION = keccak256("init.storage");
                  /// @dev Layout of the entrypoint contract's storage.
                  struct Data {
                      uint8 initialized;
                      bool initializing;
                  }
                  /// @dev Returns the entrypoint contract's data at the relevant storage location.
                  function initStorage() internal pure returns (Data storage initData) {
                      bytes32 position = INIT_STORAGE_POSITION;
                      assembly {
                          initData.slot := position
                      }
                  }
              }
              abstract contract Initializable {
                  /**
                   * @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() {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      bool isTopLevelCall = !_initializing;
                      require(
                          (isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1),
                          "Initializable: contract is already initialized"
                      );
                      data.initialized = 1;
                      if (isTopLevelCall) {
                          data.initializing = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          data.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) {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                      data.initialized = version;
                      data.initializing = true;
                      _;
                      data.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() {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      require(data.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 {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized < type(uint8).max) {
                          data.initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IOperatorFilterToggle.sol";
              library OperatorFilterToggleStorage {
                  bytes32 public constant OPERATOR_FILTER_TOGGLE_STORAGE_POSITION = keccak256("operator.filter.toggle.storage");
                  struct Data {
                      bool operatorRestriction;
                  }
                  function operatorFilterToggleStorage() internal pure returns (Data storage operatorFilterToggleData) {
                      bytes32 position = OPERATOR_FILTER_TOGGLE_STORAGE_POSITION;
                      assembly {
                          operatorFilterToggleData.slot := position
                      }
                  }
              }
              abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                  function operatorRestriction() external view override returns (bool) {
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      return data.operatorRestriction;
                  }
                  function setOperatorRestriction(bool _restriction) external {
                      require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                      _setOperatorRestriction(_restriction);
                  }
                  function _setOperatorRestriction(bool _restriction) internal {
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      data.operatorRestriction = _restriction;
                      emit OperatorRestriction(_restriction);
                  }
                  function _canSetOperatorRestriction() internal virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/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.
               */
              library OwnableStorage {
                  bytes32 public constant OWNABLE_STORAGE_POSITION = keccak256("ownable.storage");
                  struct Data {
                      /// @dev Owner of the contract (purpose: OpenSea compatibility)
                      address _owner;
                  }
                  function ownableStorage() internal pure returns (Data storage ownableData) {
                      bytes32 position = OWNABLE_STORAGE_POSITION;
                      assembly {
                          ownableData.slot := position
                      }
                  }
              }
              abstract contract Ownable is IOwnable {
                  /// @dev Reverts if caller is not the owner.
                  modifier onlyOwner() {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      if (msg.sender != data._owner) {
                          revert("Not authorized");
                      }
                      _;
                  }
                  /**
                   *  @notice Returns the owner of the contract.
                   */
                  function owner() public view override returns (address) {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      return data._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 {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      address _prevOwner = data._owner;
                      data._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;
              /// @author thirdweb
              import "../../extension/interface/IPermissions.sol";
              import "../../lib/TWStrings.sol";
              /**
               *  @title   Permissions
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
               */
              library PermissionsStorage {
                  bytes32 public constant PERMISSIONS_STORAGE_POSITION = keccak256("permissions.storage");
                  struct Data {
                      /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                      mapping(bytes32 => mapping(address => bool)) _hasRole;
                      /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                      mapping(bytes32 => bytes32) _getRoleAdmin;
                  }
                  function permissionsStorage() internal pure returns (Data storage permissionsData) {
                      bytes32 position = PERMISSIONS_STORAGE_POSITION;
                      assembly {
                          permissionsData.slot := position
                      }
                  }
              }
              contract Permissions is IPermissions {
                  /// @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, _msgSender());
                      _;
                  }
                  /**
                   *  @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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      if (!data._hasRole[role][address(0)]) {
                          return data._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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(data._getRoleAdmin[role], _msgSender());
                      if (data._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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(data._getRoleAdmin[role], _msgSender());
                      _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 (_msgSender() != 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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      bytes32 previousAdminRole = data._getRoleAdmin[role];
                      data._getRoleAdmin[role] = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      data._hasRole[role][account] = true;
                      emit RoleGranted(role, account, _msgSender());
                  }
                  /// @dev Revokes `role` from `account`
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(role, account);
                      delete data._hasRole[role][account];
                      emit RoleRevoked(role, account, _msgSender());
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      if (!data._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)
                                  )
                              )
                          );
                      }
                  }
                  function _msgSender() internal view virtual returns (address sender) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/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.
               */
              library PermissionsEnumerableStorage {
                  bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION = keccak256("permissions.enumerable.storage");
                  /**
                   *  @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;
                  }
                  struct Data {
                      /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                      mapping(bytes32 => RoleMembers) roleMembers;
                  }
                  function permissionsEnumerableStorage() internal pure returns (Data storage permissionsEnumerableData) {
                      bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION;
                      assembly {
                          permissionsEnumerableData.slot := position
                      }
                  }
              }
              contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                  /**
                   *  @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) {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 currentIndex = data.roleMembers[role].index;
                      uint256 check;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (data.roleMembers[role].members[i] != address(0)) {
                              if (check == index) {
                                  member = data.roleMembers[role].members[i];
                                  return member;
                              }
                              check += 1;
                          } else if (hasRole(role, address(0)) && i == data.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) {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 currentIndex = data.roleMembers[role].index;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (data.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 {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].index;
                      data.roleMembers[role].index += 1;
                      data.roleMembers[role].members[idx] = account;
                      data.roleMembers[role].indexOf[account] = idx;
                  }
                  /// @dev removes `account` from {roleMembers}, for `role`
                  function _removeMember(bytes32 role, address account) internal {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].indexOf[account];
                      delete data.roleMembers[role].members[idx];
                      delete data.roleMembers[role].indexOf[account];
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IPrimarySale.sol";
              library PrimarySaleStorage {
                  bytes32 public constant PRIMARY_SALE_STORAGE_POSITION = keccak256("primary.sale.storage");
                  struct Data {
                      address recipient;
                  }
                  function primarySaleStorage() internal pure returns (Data storage primarySaleData) {
                      bytes32 position = PRIMARY_SALE_STORAGE_POSITION;
                      assembly {
                          primarySaleData.slot := position
                      }
                  }
              }
              /**
               *  @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 Returns primary sale recipient address.
                  function primarySaleRecipient() public view override returns (address) {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      return data.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 {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      data.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;
              /// @author thirdweb
              import "../../extension/interface/IRoyalty.sol";
              library RoyaltyStorage {
                  bytes32 public constant ROYALTY_STORAGE_POSITION = keccak256("royalty.storage");
                  struct Data {
                      /// @dev The (default) address that receives all royalty value.
                      address royaltyRecipient;
                      /// @dev The (default) % of a sale to take as royalty (in basis points).
                      uint16 royaltyBps;
                      /// @dev Token ID => royalty recipient and bps for token
                      mapping(uint256 => IRoyalty.RoyaltyInfo) royaltyInfoForToken;
                  }
                  function royaltyStorage() internal pure returns (Data storage royaltyData) {
                      bytes32 position = ROYALTY_STORAGE_POSITION;
                      assembly {
                          royaltyData.slot := position
                      }
                  }
              }
              /**
               *  @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 {
                  /**
                   *  @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) {
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      RoyaltyInfo memory royaltyForToken = data.royaltyInfoForToken[_tokenId];
                      return
                          royaltyForToken.recipient == address(0)
                              ? (data.royaltyRecipient, uint16(data.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) {
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      return (data.royaltyRecipient, uint16(data.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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.royaltyRecipient = _royaltyRecipient;
                      data.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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.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;
              /// @author thirdweb
              import "../../extension/interface/ISignatureAction.sol";
              import "./EIP712Upgradeable.sol";
              library SignatureActionStorage {
                  bytes32 public constant SIGNATURE_ACTION_STORAGE_POSITION = keccak256("signature.action.storage");
                  struct Data {
                      /// @dev Mapping from a signed request UID => whether the request is processed.
                      mapping(bytes32 => bool) executed;
                  }
                  function signatureActionStorage() internal pure returns (Data storage signatureActionData) {
                      bytes32 position = SIGNATURE_ACTION_STORAGE_POSITION;
                      assembly {
                          signatureActionData.slot := position
                      }
                  }
              }
              abstract contract SignatureActionUpgradeable is EIP712Upgradeable, ISignatureAction {
                  using ECDSA for bytes32;
                  bytes32 private constant TYPEHASH =
                      keccak256("GenericRequest(uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid,bytes data)");
                  function __SignatureAction_init() internal onlyInitializing {
                      __EIP712_init("SignatureAction", "1");
                  }
                  function __SignatureAction_init_unchained() internal onlyInitializing {}
                  /// @dev Verifies that a request is signed by an authorized account.
                  function verify(GenericRequest calldata _req, bytes calldata _signature)
                      public
                      view
                      override
                      returns (bool success, address signer)
                  {
                      SignatureActionStorage.Data storage data = SignatureActionStorage.signatureActionStorage();
                      signer = _recoverAddress(_req, _signature);
                      success = !data.executed[_req.uid] && _isAuthorizedSigner(signer);
                  }
                  /// @dev Returns whether a given address is authorized to sign requests.
                  function _isAuthorizedSigner(address _signer) internal view virtual returns (bool);
                  /// @dev Verifies a request and marks the request as processed.
                  function _processRequest(GenericRequest calldata _req, bytes calldata _signature)
                      internal
                      returns (address signer)
                  {
                      bool success;
                      (success, signer) = verify(_req, _signature);
                      if (!success) {
                          revert("Invalid req");
                      }
                      if (_req.validityStartTimestamp > block.timestamp || block.timestamp > _req.validityEndTimestamp) {
                          revert("Req expired");
                      }
                      SignatureActionStorage.Data storage data = SignatureActionStorage.signatureActionStorage();
                      data.executed[_req.uid] = true;
                  }
                  /// @dev Returns the address of the signer of the request.
                  function _recoverAddress(GenericRequest calldata _req, bytes calldata _signature) internal view returns (address) {
                      return _hashTypedDataV4(keccak256(_encodeRequest(_req))).recover(_signature);
                  }
                  /// @dev Encodes a request for recovery of the signer in `recoverAddress`.
                  function _encodeRequest(GenericRequest calldata _req) internal pure returns (bytes memory) {
                      return
                          abi.encode(
                              TYPEHASH,
                              _req.validityStartTimestamp,
                              _req.validityEndTimestamp,
                              _req.uid,
                              keccak256(_req.data)
                          );
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { ContractMetadataStorage } from "../extension/ContractMetadata.sol";
              contract ContractMetadataInit {
                  event ContractURIUpdated(string prevURI, string newURI);
                  /// @dev Lets a contract admin set the URI for contract-level metadata.
                  function _setupContractURI(string memory _uri) internal {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      string memory prevURI = data.contractURI;
                      data.contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "../../extension/interface/IOperatorFilterRegistry.sol";
              import { OperatorFilterToggleStorage } from "../extension/OperatorFilterToggle.sol";
              contract DefaultOperatorFiltererInit {
                  event OperatorRestriction(bool restriction);
                  IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                  address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                  function _setupOperatorFilterer() internal {
                      __DefaultOperatorFilterer_init();
                  }
                  function __DefaultOperatorFilterer_init() private {
                      __OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      data.operatorRestriction = true;
                      emit OperatorRestriction(true);
                  }
                  function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) private {
                      // 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));
                                  }
                              }
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { ERC721AStorage } from "../eip/ERC721AUpgradeable.sol";
              import "../extension/Initializable.sol";
              contract ERC721AInit is Initializable {
                  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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._name = name_;
                      data._symbol = symbol_;
                      data._currentIndex = _startTokenId();
                  }
                  function _startTokenId() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { OwnableStorage } from "../extension/Ownable.sol";
              contract OwnableInit {
                  event OwnerUpdated(address indexed prevOwner, address indexed 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 {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      address _prevOwner = data._owner;
                      data._owner = _newOwner;
                      emit OwnerUpdated(_prevOwner, _newOwner);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PermissionsEnumerableStorage } from "../extension/PermissionsEnumerable.sol";
              import "./PermissionsInit.sol";
              contract PermissionsEnumerableInit is PermissionsInit {
                  /// @dev Sets up `role` for `account`
                  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 {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].index;
                      data.roleMembers[role].index += 1;
                      data.roleMembers[role].members[idx] = account;
                      data.roleMembers[role].indexOf[account] = idx;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PermissionsStorage } from "../extension/Permissions.sol";
              contract PermissionsInit {
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      data._hasRole[role][account] = true;
                      emit RoleGranted(role, account, msg.sender);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PrimarySaleStorage } from "../extension/PrimarySale.sol";
              contract PrimarySaleInit {
                  /// @dev Emitted when a new sale recipient is set.
                  event PrimarySaleRecipientUpdated(address indexed recipient);
                  /// @dev Lets a contract admin set the recipient for all primary sales.
                  function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      data.recipient = _saleRecipient;
                      emit PrimarySaleRecipientUpdated(_saleRecipient);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { RoyaltyStorage, IRoyalty } from "../extension/Royalty.sol";
              contract RoyaltyInit {
                  event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                  /// @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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.royaltyRecipient = _royaltyRecipient;
                      data.royaltyBps = uint16(_royaltyBps);
                      emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { SignatureActionStorage } from "../extension/SignatureActionUpgradeable.sol";
              import { EIP712Storage } from "../extension/EIP712Upgradeable.sol";
              import "../extension/Initializable.sol";
              contract EIP712Init is Initializable {
                  function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                      __EIP712_init_unchained(name, version);
                  }
                  function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                      bytes32 hashedName = keccak256(bytes(name));
                      bytes32 hashedVersion = keccak256(bytes(version));
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      data._HASHED_NAME = hashedName;
                      data._HASHED_VERSION = hashedVersion;
                  }
              }
              contract SignatureActionInit is EIP712Init {
                  function __SignatureAction_init() internal onlyInitializing {
                      __EIP712_init("SignatureAction", "1");
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./interface/IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts 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;
              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: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 {
                  /**
                   * @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);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address);
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes calldata data
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              import "./IERC721.sol";
              import "./IERC721Metadata.sol";
              /**
               * @dev Interface of an ERC721A compliant contract.
               */
              interface IERC721A is IERC721, IERC721Metadata {
                  /**
                   * 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);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
              /// @dev See https://eips.ethereum.org/EIPS/eip-721
              ///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
              /* is ERC721 */
              interface IERC721Metadata {
                  /// @notice A descriptive name for a collection of NFTs in this contract
                  function name() external view returns (string memory);
                  /// @notice An abbreviated name for NFTs in this contract
                  function symbol() external view returns (string memory);
                  /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
                  /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
                  ///  3986. The URI may point to a JSON file that conforms to the "ERC721
                  ///  Metadata JSON Schema".
                  function tokenURI(uint256 _tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
              pragma solidity ^0.8.0;
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              interface IERC721Receiver {
                  /**
                   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                   * by `operator` from `from`, this function is called.
                   *
                   * It must return its Solidity selector to confirm the token transfer.
                   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                   *
                   * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../lib/TWAddress.sol";
              import "./interface/IMulticall.sol";
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              contract Multicall is IMulticall {
                  /**
                   *  @notice Receives and executes a batch of function calls on this contract.
                   *  @dev Receives and executes a batch of function calls on this contract.
                   *
                   *  @param data The bytes data that makes up the batch of function calls to execute.
                   *  @return results The bytes data that makes up the result of the batch of function calls executed.
                   */
                  function multicall(bytes[] calldata data) external virtual override returns (bytes[] memory results) {
                      results = new bytes[](data.length);
                      for (uint256 i = 0; i < data.length; i++) {
                          results[i] = TWAddress.functionDelegateCall(address(this), data[i]);
                      }
                      return results;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               *  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;
              interface IERC2771Context {
                  function isTrustedForwarder(address forwarder) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              interface IMulticall {
                  /**
                   * @dev Receives and executes a batch of function calls on this contract.
                   */
                  function multicall(bytes[] calldata data) external returns (bytes[] memory results);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  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;
              /// @author thirdweb
              /**
               * @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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  thirdweb's `SignatureAction` extension smart contract can be used with any base smart contract. It provides a generic
               *  payload struct that can be signed by an authorized wallet and verified by the contract. The bytes `data` field provided
               *  in the payload can be abi encoded <-> decoded to use `SignatureContract` for any authorized signature action.
               */
              interface ISignatureAction {
                  /**
                   *  @notice The payload that must be signed by an authorized wallet.
                   *
                   *  @param validityStartTimestamp The UNIX timestamp at and after which a signature is valid.
                   *  @param validityEndTimestamp The UNIX timestamp at and after which a signature is invalid/expired.
                   *  @param uid A unique non-repeatable ID for the payload.
                   *  @param data Arbitrary bytes data to be used at the discretion of the contract.
                   */
                  struct GenericRequest {
                      uint128 validityStartTimestamp;
                      uint128 validityEndTimestamp;
                      bytes32 uid;
                      bytes data;
                  }
                  /// @notice Emitted when a payload is verified and executed.
                  event RequestExecuted(address indexed user, address indexed signer, GenericRequest _req);
                  /**
                   *  @notice Verfies that a payload is signed by an authorized wallet.
                   *
                   *  @param req The payload signed by the authorized wallet.
                   *  @param signature The signature produced by the authorized wallet signing the given payload.
                   *
                   *  @return success Whether the payload is signed by the authorized wallet.
                   *  @return signer The address of the signer.
                   */
                  function verify(GenericRequest calldata req, bytes calldata signature)
                      external
                      view
                      returns (bool success, address signer);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @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: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @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.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../../../lib/TWStrings.sol";
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      } else if (error == RecoverError.InvalidSignatureV) {
                          revert("ECDSA: invalid signature 'v' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      // Check the signature length
                      // - case 65: r,s,v signature (standard)
                      // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else if (signature.length == 64) {
                          bytes32 r;
                          bytes32 vs;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          assembly {
                              r := mload(add(signature, 0x20))
                              vs := mload(add(signature, 0x40))
                          }
                          return tryRecover(hash, r, vs);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address, RecoverError) {
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      if (v != 27 && v != 28) {
                          return (address(0), RecoverError.InvalidSignatureV);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", hash));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", TWStrings.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              //   $$\\     $$\\       $$\\                 $$\\                         $$\\
              //   $$ |    $$ |      \\__|                $$ |                        $$ |
              // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
              // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
              //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
              //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
              //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
              //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
              import "lib/dynamic-contracts/src/presets/BaseRouter.sol";
              import "../extension/Multicall.sol";
              import "../dynamic-contracts/extension/Initializable.sol";
              import "../dynamic-contracts/extension/Permissions.sol";
              import "../dynamic-contracts/extension/ERC2771ContextUpgradeable.sol";
              import "../dynamic-contracts/init/ContractMetadataInit.sol";
              import "../dynamic-contracts/init/ERC721AInit.sol";
              import "../dynamic-contracts/init/OwnableInit.sol";
              import "../dynamic-contracts/init/PermissionsEnumerableInit.sol";
              import "../dynamic-contracts/init/PrimarySaleInit.sol";
              import "../dynamic-contracts/init/RoyaltyInit.sol";
              import "../dynamic-contracts/init/SignatureActionInit.sol";
              import "../dynamic-contracts/init/DefaultOperatorFiltererInit.sol";
              /**
               *  Defualt extensions to add:
               *      - TieredDropLogic
               *      - PermissionsEnumerable
               */
              contract TieredDrop is
                  Initializable,
                  Multicall,
                  ERC2771ContextUpgradeable,
                  BaseRouter,
                  DefaultOperatorFiltererInit,
                  PrimarySaleInit,
                  ContractMetadataInit,
                  ERC721AInit,
                  OwnableInit,
                  PermissionsEnumerableInit,
                  RoyaltyInit,
                  SignatureActionInit
              {
                  /*///////////////////////////////////////////////////////////////
                                  Constructor and Initializer logic
                  //////////////////////////////////////////////////////////////*/
                  constructor(Extension[] memory _extensions) BaseRouter(_extensions) {}
                  /// @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,
                      uint16 _royaltyBps
                  ) external initializer {
                      // Initialize inherited contracts, most base-like -> most derived.
                      __ERC2771Context_init(_trustedForwarders);
                      __ERC721A_init(_name, _symbol);
                      __SignatureAction_init();
                      _setupContractURI(_contractURI);
                      _setupOwner(_defaultAdmin);
                      _setupRoles(_defaultAdmin);
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                      _setupPrimarySaleRecipient(_saleRecipient);
                      _setupOperatorFilterer();
                  }
                  function _setupRoles(address _defaultAdmin) internal onlyInitializing {
                      bytes32 _operatorRole = keccak256("OPERATOR_ROLE");
                      bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                      bytes32 _minterRole = keccak256("MINTER_ROLE");
                      bytes32 _defaultAdminRole = 0x00;
                      _setupRole(_defaultAdminRole, _defaultAdmin);
                      _setupRole(_minterRole, _defaultAdmin);
                      _setupRole(_transferRole, _defaultAdmin);
                      _setupRole(_transferRole, address(0));
                      _setupRole(_operatorRole, _defaultAdmin);
                      _setupRole(_operatorRole, address(0));
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns whether a plugin can be set in the given execution context.
                  function _canSetExtension() internal view virtual override returns (bool) {
                      bytes32 defaultAdminRole = 0x00;
                      return _hasRole(defaultAdminRole, _msgSender());
                  }
                  /// @dev Checks whether an account holds the given role.
                  function _hasRole(bytes32 role, address addr) internal view returns (bool) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._hasRole[role][addr];
                  }
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "../interface/IRouter.sol";
              abstract contract Router is IRouter {
                  fallback() external payable virtual {
                  /// @dev delegate calls the appropriate implementation smart contract for a given function.
                      address implementation = getImplementationForFunction(msg.sig);
                      _delegate(implementation);
                  }
                  receive() external payable virtual {}
                  /// @dev delegateCalls an `implementation` smart contract.
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /// @dev Unimplemented. Returns the implementation contract address for a given function signature.
                  function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address);
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "./IDefaultExtensionSet.sol";
              interface IBaseRouter is IDefaultExtensionSet {
                  /*///////////////////////////////////////////////////////////////
                                      External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Adds a new extension to the router.
                  function addExtension(Extension memory extension) external;
                  /// @dev Updates an existing extension in the router, or overrides a default extension.
                  function updateExtension(Extension memory extension) external;
                  /// @dev Removes an existing extension from the router.
                  function removeExtension(string memory extensionName) external;
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "./IExtension.sol";
              interface IDefaultExtensionSet is IExtension {
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns all extensions stored.
                  function getAllExtensions() external view returns (Extension[] memory);
                  /// @dev Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory extensionName) external view returns (ExtensionFunction[] memory);
                  /// @dev Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory);
                  /// @dev Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory extensionName) external view returns (address);
                  /// @dev Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory extensionName) external view returns (Extension memory);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              interface IExtension {
                  /*///////////////////////////////////////////////////////////////
                                              Structs
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @notice A extension's metadata.
                   *
                   *  @param name             The unique name of the extension.
                   *  @param metadataURI      The URI where the metadata for the extension lives.
                   *  @param implementation   The implementation smart contract address of the extension.
                   */
                  struct ExtensionMetadata {
                      string name;
                      string metadataURI;
                      address implementation;
                  }
                  /**
                   *  @notice An interface to describe a extension's function.
                   *
                   *  @param functionSelector    The 4 byte selector of the function.
                   *  @param functionSignature   Function signature as a string. E.g. "transfer(address,address,uint256)"
                   */
                  struct ExtensionFunction {
                      bytes4 functionSelector;
                      string functionSignature;
                  }
                  /**
                   *  @notice An interface to describe an extension.
                   *
                   *  @param metadata     The extension's metadata; it's name, metadata URI and implementation contract address.
                   *  @param functions    The functions that belong to the extension.
                   */
                  struct Extension {
                      ExtensionMetadata metadata;
                      ExtensionFunction[] functions;
                  }
                  /*///////////////////////////////////////////////////////////////
                                              Events
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Emitted when a extension is added; emitted for each function of the extension.
                  event ExtensionAdded(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
                  /// @dev Emitted when extension is updated; emitted for each function of the extension.
                  event ExtensionUpdated(
                      address indexed oldExtensionAddress,
                      address indexed newExtensionAddress,
                      bytes4 indexed functionSelector,
                      string functionSignature
                  );
                  /// @dev Emitted when a extension is removed; emitted for each function of the extension.
                  event ExtensionRemoved(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              interface IRouter {
                  fallback() external payable;
                  receive() external payable;
                  function getImplementationForFunction(bytes4 _functionSelector) external view returns (address);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../interface/IBaseRouter.sol";
              // Core
              import "../core/Router.sol";
              // Utils
              import "./utils/StringSet.sol";
              import "./utils/DefaultExtensionSet.sol";
              import "./utils/ExtensionState.sol";
              abstract contract BaseRouter is IBaseRouter, Router, ExtensionState {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @notice The DefaultExtensionSet that stores default extensions of the router.
                  address public immutable defaultExtensionSet;
                  /*///////////////////////////////////////////////////////////////
                                              Constructor
                  //////////////////////////////////////////////////////////////*/
                  constructor(Extension[] memory _extensions) {
                      DefaultExtensionSet map = new DefaultExtensionSet();
                      defaultExtensionSet = address(map);
                      uint256 len = _extensions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          map.setExtension(_extensions[i]);
                      }
                  }
                  /*///////////////////////////////////////////////////////////////
                                      External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Adds a new extension to the router.
                  function addExtension(Extension memory _extension) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _addExtension(_extension);
                  }
                  /// @dev Updates an existing extension in the router, or overrides a default extension.
                  function updateExtension(Extension memory _extension) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _updateExtension(_extension);
                  }
                  /// @dev Removes an existing extension from the router.
                  function removeExtension(string memory _extensionName) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _removeExtension(_extensionName);
                  }
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @notice Returns all extensions stored. Override default lugins stored in router are
                   *          given precedence over default extensions in DefaultExtensionSet.
                   */
                  function getAllExtensions() external view returns (Extension[] memory allExtensions) {
                      Extension[] memory defaultExtensions = IDefaultExtensionSet(defaultExtensionSet).getAllExtensions();
                      uint256 defaultExtensionsLen = defaultExtensions.length;
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string[] memory names = data.extensionNames.values();
                      uint256 namesLen = names.length;
                      uint256 overrides = 0;
                      for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
                          if (data.extensionNames.contains(defaultExtensions[i].metadata.name)) {
                              overrides += 1;
                          }
                      }
                      uint256 total = (namesLen + defaultExtensionsLen) - overrides;
                      allExtensions = new Extension[](total);
                      uint256 idx = 0;
                      for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
                          string memory name = defaultExtensions[i].metadata.name;
                          if (!data.extensionNames.contains(name)) {
                              allExtensions[idx] = defaultExtensions[i];
                              idx += 1;
                          }
                      }
                      for (uint256 i = 0; i < namesLen; i += 1) {
                          allExtensions[idx] = data.extensions[names[i]];
                          idx += 1;
                      }
                  }
                  /// @dev Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory _extensionName) public view returns (Extension memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      bool isLocalExtension = data.extensionNames.contains(_extensionName);
                      return isLocalExtension ? data.extensions[_extensionName] : IDefaultExtensionSet(defaultExtensionSet).getExtension(_extensionName);
                  }
                  /// @dev Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory _extensionName) external view returns (address) {
                      return getExtension(_extensionName).metadata.implementation;
                  }
                  /// @dev Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
                      return getExtension(_extensionName).functions;
                  }
                  /// @dev Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 _functionSelector) public view returns (ExtensionMetadata memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
                      bool isLocalExtension = metadata.implementation != address(0);
                      return isLocalExtension ? metadata : IDefaultExtensionSet(defaultExtensionSet).getExtensionForFunction(_functionSelector);
                  }
                  /// @dev Returns the extension implementation address stored in router, for the given function.
                  function getImplementationForFunction(bytes4 _functionSelector)
                      public
                      view
                      override
                      returns (address extensionAddress)
                  {
                      return getExtensionForFunction(_functionSelector).implementation;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns whether a extension can be set in the given execution context.
                  function _canSetExtension() internal view virtual returns (bool);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../../interface/IDefaultExtensionSet.sol";
              // Extensions
              import "./ExtensionState.sol";
              contract DefaultExtensionSet is IDefaultExtensionSet, ExtensionState {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @notice The deployer of DefaultExtensionSet.
                  address private deployer;
                  /*///////////////////////////////////////////////////////////////
                                          Constructor
                  //////////////////////////////////////////////////////////////*/
                  constructor() {
                      deployer = msg.sender;
                  }
                  /*///////////////////////////////////////////////////////////////
                                          External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @notice Stores a extension in the DefaultExtensionSet.
                  function setExtension(Extension memory _extension) external {
                      require(msg.sender == deployer, "DefaultExtensionSet: unauthorized caller.");
                      _addExtension(_extension);
                  }
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /// @notice Returns all extensions stored.
                  function getAllExtensions() external view returns (Extension[] memory allExtensions) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string[] memory names = data.extensionNames.values();
                      uint256 len = names.length;
                      allExtensions = new Extension[](len);
                      for (uint256 i = 0; i < len; i += 1) {
                          allExtensions[i] = data.extensions[names[i]];
                      }
                  }
                  /// @notice Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory _extensionName) public view returns (Extension memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      require(data.extensionNames.contains(_extensionName), "DefaultExtensionSet: extension does not exist.");
                      return data.extensions[_extensionName];
                  }
                  /// @notice Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory _extensionName) external view returns (address) {
                      return getExtension(_extensionName).metadata.implementation;
                  }
                  /// @notice Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
                      return getExtension(_extensionName).functions;
                  }
                  /// @notice Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 _functionSelector) external view returns (ExtensionMetadata memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
                      require(metadata.implementation != address(0), "DefaultExtensionSet: no extension for function.");
                      return metadata;
                  }
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../../interface/IExtension.sol";
              // Extensions
              import "./StringSet.sol";
              library ExtensionStateStorage {
                  bytes32 public constant EXTENSION_STATE_STORAGE_POSITION = keccak256("extension.state.storage");
                  struct Data {
                      /// @dev Set of names of all extensions stored.
                      StringSet.Set extensionNames;
                      /// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions.
                      mapping(string => IExtension.Extension) extensions;
                      /// @dev Mapping from function selector => extension metadata of the extension the function belongs to.
                      mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata;
                  }
                  function extensionStateStorage() internal pure returns (Data storage extensionStateData) {
                      bytes32 position = EXTENSION_STATE_STORAGE_POSITION;
                      assembly {
                          extensionStateData.slot := position
                      }
                  }
              }
              contract ExtensionState is IExtension {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Stores a new extension in the contract.
                  function _addExtension(Extension memory _extension) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string memory name = _extension.metadata.name;
                      require(data.extensionNames.add(name), "ExtensionState: extension already exists.");
                      data.extensions[name].metadata = _extension.metadata;
                      require(_extension.metadata.implementation != address(0), "ExtensionState: adding extension without implementation.");
                      uint256 len = _extension.functions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          require(
                              _extension.functions[i].functionSelector ==
                                  bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
                              "ExtensionState: fn selector and signature mismatch."
                          );
                          require(
                              data.extensionMetadata[_extension.functions[i].functionSelector].implementation == address(0),
                              "ExtensionState: extension already exists for function."
                          );
                          data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
                          data.extensions[name].functions.push(_extension.functions[i]);
                          emit ExtensionAdded(
                              _extension.metadata.implementation,
                              _extension.functions[i].functionSelector,
                              _extension.functions[i].functionSignature
                          );
                      }
                  }
                  /// @dev Updates / overrides an existing extension in the contract.
                  function _updateExtension(Extension memory _extension) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string memory name = _extension.metadata.name;
                      require(data.extensionNames.contains(name), "ExtensionState: extension does not exist.");
                      address oldImplementation = data.extensions[name].metadata.implementation;
                      require(_extension.metadata.implementation != oldImplementation, "ExtensionState: re-adding same extension.");
                      data.extensions[name].metadata = _extension.metadata;
                      ExtensionFunction[] memory oldFunctions = data.extensions[name].functions;
                      uint256 oldFunctionsLen = oldFunctions.length;
                      delete data.extensions[name].functions;
                      for (uint256 i = 0; i < oldFunctionsLen; i += 1) {
                          delete data.extensionMetadata[oldFunctions[i].functionSelector];
                      }
                      uint256 len = _extension.functions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          require(
                              _extension.functions[i].functionSelector ==
                                  bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
                              "ExtensionState: fn selector and signature mismatch."
                          );
                          data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
                          data.extensions[name].functions.push(_extension.functions[i]);
                          emit ExtensionUpdated(
                              oldImplementation,
                              _extension.metadata.implementation,
                              _extension.functions[i].functionSelector,
                              _extension.functions[i].functionSignature
                          );
                      }
                  }
                  /// @dev Removes an existing extension from the contract.
                  function _removeExtension(string memory _extensionName) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      require(data.extensionNames.remove(_extensionName), "ExtensionState: extension does not exist.");
                      address implementation = data.extensions[_extensionName].metadata.implementation;
                      ExtensionFunction[] memory extensionFunctions = data.extensions[_extensionName].functions;
                      delete data.extensions[_extensionName];
                      uint256 len = extensionFunctions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          emit ExtensionRemoved(
                              implementation,
                              extensionFunctions[i].functionSelector,
                              extensionFunctions[i].functionSignature
                          );
                          delete data.extensionMetadata[extensionFunctions[i].functionSelector];
                      }
                  }
              }// SPDX-License-Identifier: Apache 2.0
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              library StringSet {
                  struct Set {
                      // Storage of set values
                      string[] _values;
                      // Position of the value in the `values` array, plus 1 because index 0
                      // means a value is not in the set.
                      mapping(string => uint256) _indexes;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function _add(Set storage set, string memory value) private returns (bool) {
                      if (!_contains(set, value)) {
                          set._values.push(value);
                          // The value is stored at length-1, but we add 1 to all indexes
                          // and use 0 as a sentinel value
                          set._indexes[value] = set._values.length;
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function _remove(Set storage set, string memory value) private returns (bool) {
                      // We read and store the value's index to prevent multiple reads from the same storage slot
                      uint256 valueIndex = set._indexes[value];
                      if (valueIndex != 0) {
                          // Equivalent to contains(set, value)
                          // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                          // the array, and then remove the last element (sometimes called as 'swap and pop').
                          // This modifies the order of the array, as noted in {at}.
                          uint256 toDeleteIndex = valueIndex - 1;
                          uint256 lastIndex = set._values.length - 1;
                          if (lastIndex != toDeleteIndex) {
                              string memory lastValue = set._values[lastIndex];
                              // Move the last value to the index where the value to delete is
                              set._values[toDeleteIndex] = lastValue;
                              // Update the index for the moved value
                              set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                          }
                          // Delete the slot where the moved value was stored
                          set._values.pop();
                          // Delete the index for the deleted slot
                          delete set._indexes[value];
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function _contains(Set storage set, string memory value) private view returns (bool) {
                      return set._indexes[value] != 0;
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function _length(Set storage set) private view returns (uint256) {
                      return set._values.length;
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function _at(Set storage set, uint256 index) private view returns (string memory) {
                      return set._values[index];
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function _values(Set storage set) private view returns (string[] memory) {
                      return set._values;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(Set storage set, string memory value) internal returns (bool) {
                      return _add(set, value);
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(Set storage set, string memory value) internal returns (bool) {
                      return _remove(set, value);
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(Set storage set, string memory value) internal view returns (bool) {
                      return _contains(set, value);
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(Set storage set) internal view returns (uint256) {
                      return _length(set);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(Set storage set, uint256 index) internal view returns (string memory) {
                      return _at(set, index);
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(Set storage set) internal view returns (string[] memory) {
                      return _values(set);
                  }
              }

              File 3 of 6: BlurExchangeV2
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { Ownable2StepUpgradeable } from "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol";
              import { UUPSUpgradeable } from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
              import { Executor } from "./Executor.sol";
              import "./lib/Constants.sol";
              import {
                  TakeAsk,
                  TakeBid,
                  TakeAskSingle,
                  TakeBidSingle,
                  Order,
                  Exchange,
                  Fees,
                  FeeRate,
                  AssetType,
                  OrderType,
                  Transfer,
                  FungibleTransfers,
                  StateUpdate,
                  AtomicExecution,
                  Cancel,
                  Listing
              } from "./lib/Structs.sol";
              import { IBlurExchangeV2 } from "./interfaces/IBlurExchangeV2.sol";
              import { ReentrancyGuardUpgradeable } from "./lib/ReentrancyGuardUpgradeable.sol";
              contract BlurExchangeV2 is
                  IBlurExchangeV2,
                  Ownable2StepUpgradeable,
                  UUPSUpgradeable,
                  ReentrancyGuardUpgradeable,
                  Executor
              {
                  address public governor;
                  // required by the OZ UUPS module
                  function _authorizeUpgrade(address) internal override onlyOwner {}
                  constructor(address delegate, address pool, address proxy) Executor(delegate, pool, proxy) {
                      _disableInitializers();
                  }
                  function initialize() external initializer {
                      __UUPSUpgradeable_init();
                      __Ownable_init();
                      __Reentrancy_init();
                      verifyDomain();
                  }
                  modifier onlyGovernor() {
                      if (msg.sender != governor) {
                          revert Unauthorized();
                      }
                      _;
                  }
                  /**
                   * @notice Governor only function to set the protocol fee rate and recipient
                   * @param recipient Protocol fee recipient
                   * @param rate Protocol fee rate
                   */
                  function setProtocolFee(address recipient, uint16 rate) external onlyGovernor {
                      if (rate > _MAX_PROTOCOL_FEE_RATE) {
                          revert ProtocolFeeTooHigh();
                      }
                      protocolFee = FeeRate(recipient, rate);
                      emit NewProtocolFee(recipient, rate);
                  }
                  /**
                   * @notice Admin only function to set the governor of the exchange
                   * @param _governor Address of governor to set
                   */
                  function setGovernor(address _governor) external onlyOwner {
                      governor = _governor;
                      emit NewGovernor(_governor);
                  }
                  /**
                   * @notice Admin only function to grant or revoke the approval of an oracle
                   * @param oracle Address to set approval of
                   * @param approved If the oracle should be approved or not
                   */
                  function setOracle(address oracle, bool approved) external onlyOwner {
                      if (approved) {
                          oracles[oracle] = 1;
                      } else {
                          oracles[oracle] = 0;
                      }
                      emit SetOracle(oracle, approved);
                  }
                  /**
                   * @notice Admin only function to set the block range
                   * @param _blockRange Block range that oracle signatures are valid for
                   */
                  function setBlockRange(uint256 _blockRange) external onlyOwner {
                      blockRange = _blockRange;
                      emit NewBlockRange(_blockRange);
                  }
                  /**
                   * @notice Cancel listings by recording their fulfillment
                   * @param cancels List of cancels to execute
                   */
                  function cancelTrades(Cancel[] memory cancels) external {
                      uint256 cancelsLength = cancels.length;
                      for (uint256 i; i < cancelsLength; ) {
                          Cancel memory cancel = cancels[i];
                          amountTaken[msg.sender][cancel.hash][cancel.index] += cancel.amount;
                          emit CancelTrade(msg.sender, cancel.hash, cancel.index, cancel.amount);
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  /**
                   * @notice Cancels all orders by incrementing caller nonce
                   */
                  function incrementNonce() external {
                      emit NonceIncremented(msg.sender, ++nonces[msg.sender]);
                  }
                  /*//////////////////////////////////////////////////////////////
                                        EXECUTION WRAPPERS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Wrapper of _takeAsk that verifies an oracle signature of the calldata before executing
                   * @param inputs Inputs for _takeAsk
                   * @param oracleSignature Oracle signature of inputs
                   */
                  function takeAsk(
                      TakeAsk memory inputs,
                      bytes calldata oracleSignature
                  )
                      public
                      payable
                      nonReentrant
                      verifyOracleSignature(_hashCalldata(msg.sender), oracleSignature)
                  {
                      _takeAsk(
                          inputs.orders,
                          inputs.exchanges,
                          inputs.takerFee,
                          inputs.signatures,
                          inputs.tokenRecipient
                      );
                  }
                  /**
                   * @notice Wrapper of _takeBid that verifies an oracle signature of the calldata before executing
                   * @param inputs Inputs for _takeBid
                   * @param oracleSignature Oracle signature of inputs
                   */
                  function takeBid(
                      TakeBid memory inputs,
                      bytes calldata oracleSignature
                  ) public verifyOracleSignature(_hashCalldata(msg.sender), oracleSignature) {
                      _takeBid(inputs.orders, inputs.exchanges, inputs.takerFee, inputs.signatures);
                  }
                  /**
                   * @notice Wrapper of _takeAskSingle that verifies an oracle signature of the calldata before executing
                   * @param inputs Inputs for _takeAskSingle
                   * @param oracleSignature Oracle signature of inputs
                   */
                  function takeAskSingle(
                      TakeAskSingle memory inputs,
                      bytes calldata oracleSignature
                  )
                      public
                      payable
                      nonReentrant
                      verifyOracleSignature(_hashCalldata(msg.sender), oracleSignature)
                  {
                      _takeAskSingle(
                          inputs.order,
                          inputs.exchange,
                          inputs.takerFee,
                          inputs.signature,
                          inputs.tokenRecipient
                      );
                  }
                  /**
                   * @notice Wrapper of _takeBidSingle that verifies an oracle signature of the calldata before executing
                   * @param inputs Inputs for _takeBidSingle
                   * @param oracleSignature Oracle signature of inputs
                   */
                  function takeBidSingle(
                      TakeBidSingle memory inputs,
                      bytes calldata oracleSignature
                  ) external verifyOracleSignature(_hashCalldata(msg.sender), oracleSignature) {
                      _takeBidSingle(inputs.order, inputs.exchange, inputs.takerFee, inputs.signature);
                  }
                  /*//////////////////////////////////////////////////////////////
                                      EXECUTION POOL WRAPPERS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Wrapper of takeAskSingle that withdraws ETH from the caller's pool balance prior to executing
                   * @param inputs Inputs for takeAskSingle
                   * @param oracleSignature Oracle signature of inputs
                   * @param amountToWithdraw Amount of ETH to withdraw from the pool
                   */
                  function takeAskSinglePool(
                      TakeAskSingle memory inputs,
                      bytes calldata oracleSignature,
                      uint256 amountToWithdraw
                  ) external payable {
                      _withdrawFromPool(msg.sender, amountToWithdraw);
                      takeAskSingle(inputs, oracleSignature);
                  }
                  /**
                   * @notice Wrapper of takeAsk that withdraws ETH from the caller's pool balance prior to executing
                   * @param inputs Inputs for takeAsk
                   * @param oracleSignature Oracle signature of inputs
                   * @param amountToWithdraw Amount of ETH to withdraw from the pool
                   */
                  function takeAskPool(
                      TakeAsk memory inputs,
                      bytes calldata oracleSignature,
                      uint256 amountToWithdraw
                  ) external payable {
                      _withdrawFromPool(msg.sender, amountToWithdraw);
                      takeAsk(inputs, oracleSignature);
                  }
                  /*//////////////////////////////////////////////////////////////
                                        EXECUTION FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Take a single ask
                   * @param order Order of listing to fulfill
                   * @param exchange Exchange struct indicating the listing to take and the parameters to match it with
                   * @param takerFee Taker fee to be taken
                   * @param signature Order signature
                   * @param tokenRecipient Address to receive the token transfer
                   */
                  function _takeAskSingle(
                      Order memory order,
                      Exchange memory exchange,
                      FeeRate memory takerFee,
                      bytes memory signature,
                      address tokenRecipient
                  ) internal {
                      Fees memory fees = Fees(protocolFee, takerFee);
                      Listing memory listing = exchange.listing;
                      uint256 takerAmount = exchange.taker.amount;
                      /* Validate the order and listing, revert if not. */
                      if (!_validateOrderAndListing(order, OrderType.ASK, exchange, signature, fees)) {
                          revert InvalidOrder();
                      }
                      /* Create single execution batch and insert the transfer. */
                      bytes memory executionBatch = _initializeSingleExecution(
                          order,
                          OrderType.ASK,
                          listing.tokenId,
                          takerAmount,
                          tokenRecipient
                      );
                      /* Set the fulfillment of the order. */
                      unchecked {
                          amountTaken[order.trader][bytes32(order.salt)][listing.index] += takerAmount;
                      }
                      /* Execute the token transfers, revert if not successful. */
                      {
                          bool[] memory successfulTransfers = _executeNonfungibleTransfers(executionBatch, 1);
                          if (!successfulTransfers[0]) {
                              revert TokenTransferFailed();
                          }
                      }
                      (
                          uint256 totalPrice,
                          uint256 protocolFeeAmount,
                          uint256 makerFeeAmount,
                          uint256 takerFeeAmount
                      ) = _computeFees(listing.price, takerAmount, order.makerFee, fees);
                      /* If there are insufficient funds to cover the price with the fees, revert. */
                      unchecked {
                          if (address(this).balance < totalPrice + takerFeeAmount) {
                              revert InsufficientFunds();
                          }
                      }
                      /* Execute ETH transfers. */
                      _transferETH(fees.protocolFee.recipient, protocolFeeAmount);
                      _transferETH(fees.takerFee.recipient, takerFeeAmount);
                      _transferETH(order.makerFee.recipient, makerFeeAmount);
                      unchecked {
                          _transferETH(order.trader, totalPrice - makerFeeAmount - protocolFeeAmount);
                      }
                      _emitExecutionEvent(executionBatch, order, listing.index, totalPrice, fees, OrderType.ASK);
                      /* Return dust. */
                      _transferETH(msg.sender, address(this).balance);
                  }
                  /**
                   * @notice Take a single bid
                   * @param order Order of listing to fulfill
                   * @param exchange Exchange struct indicating the listing to take and the parameters to match it with
                   * @param takerFee Taker fee to be taken
                   * @param signature Order signature
                   */
                  function _takeBidSingle(
                      Order memory order,
                      Exchange memory exchange,
                      FeeRate memory takerFee,
                      bytes memory signature
                  ) internal {
                      Fees memory fees = Fees(protocolFee, takerFee);
                      Listing memory listing = exchange.listing;
                      uint256 takerAmount = exchange.taker.amount;
                      /* Validate the order and listing, revert if not. */
                      if (!_validateOrderAndListing(order, OrderType.BID, exchange, signature, fees)) {
                          revert InvalidOrder();
                      }
                      /* Create single execution batch and insert the transfer. */
                      bytes memory executionBatch = _initializeSingleExecution(
                          order,
                          OrderType.BID,
                          exchange.taker.tokenId,
                          takerAmount,
                          msg.sender
                      );
                      /* Execute the token transfers, revert if not successful. */
                      {
                          bool[] memory successfulTransfers = _executeNonfungibleTransfers(executionBatch, 1);
                          if (!successfulTransfers[0]) {
                              revert TokenTransferFailed();
                          }
                      }
                      (
                          uint256 totalPrice,
                          uint256 protocolFeeAmount,
                          uint256 makerFeeAmount,
                          uint256 takerFeeAmount
                      ) = _computeFees(listing.price, takerAmount, order.makerFee, fees);
                      /* Execute pool transfers and set the fulfillment of the order. */
                      address trader = order.trader;
                      _transferPool(trader, order.makerFee.recipient, makerFeeAmount);
                      _transferPool(trader, fees.takerFee.recipient, takerFeeAmount);
                      _transferPool(trader, fees.protocolFee.recipient, protocolFeeAmount);
                      unchecked {
                          _transferPool(trader, msg.sender, totalPrice - takerFeeAmount - protocolFeeAmount);
                          amountTaken[trader][bytes32(order.salt)][listing.index] += exchange.taker.amount;
                      }
                      _emitExecutionEvent(executionBatch, order, listing.index, totalPrice, fees, OrderType.BID);
                  }
                  /**
                   * @notice Take multiple asks; efficiently verifying and executing the transfers in bulk
                   * @param orders List of orders
                   * @param exchanges List of exchanges indicating the listing to take and the parameters to match it with
                   * @param takerFee Taker fee to be taken on each exchange
                   * @param signatures Bytes array of order signatures
                   * @param tokenRecipient Address to receive the tokens purchased
                   */
                  function _takeAsk(
                      Order[] memory orders,
                      Exchange[] memory exchanges,
                      FeeRate memory takerFee,
                      bytes memory signatures,
                      address tokenRecipient
                  ) internal {
                      Fees memory fees = Fees(protocolFee, takerFee);
                      /**
                       * Validate all the orders potentially used in the execution and
                       * initialize the arrays for pending fulfillments.
                       */
                      (bool[] memory validOrders, uint256[][] memory pendingAmountTaken) = _validateOrders(
                          orders,
                          OrderType.ASK,
                          signatures,
                          fees
                      );
                      uint256 exchangesLength = exchanges.length;
                      /* Initialize the execution batch structs. */
                      (
                          bytes memory executionBatch,
                          FungibleTransfers memory fungibleTransfers
                      ) = _initializeBatch(exchangesLength, OrderType.ASK, tokenRecipient);
                      Order memory order;
                      Exchange memory exchange;
                      uint256 remainingETH = address(this).balance;
                      for (uint256 i; i < exchangesLength; ) {
                          exchange = exchanges[i];
                          order = orders[exchange.index];
                          /* Check the listing and exchange is valid and its parent order has already been validated. */
                          if (
                              _validateListingFromBatch(
                                  order,
                                  OrderType.ASK,
                                  exchange,
                                  validOrders,
                                  pendingAmountTaken
                              )
                          ) {
                              /* Insert the transfers into the batch. */
                              bool inserted;
                              (remainingETH, inserted) = _insertExecutionAsk(
                                  executionBatch,
                                  fungibleTransfers,
                                  order,
                                  exchange,
                                  fees,
                                  remainingETH
                              );
                              if (inserted) {
                                  unchecked {
                                      pendingAmountTaken[exchange.index][exchange.listing.index] += exchange
                                          .taker
                                          .amount;
                                  }
                              }
                          }
                          unchecked {
                              ++i;
                          }
                      }
                      /* Execute all transfers. */
                      _executeBatchTransfer(executionBatch, fungibleTransfers, fees, OrderType.ASK);
                      /* Return dust. */
                      _transferETH(msg.sender, address(this).balance);
                  }
                  /**
                   * @notice Take multiple bids; efficiently verifying and executing the transfers in bulk
                   * @param orders List of orders
                   * @param exchanges List of exchanges indicating the listing to take and the parameters to match it with
                   * @param takerFee Taker fee to be taken on each exchange
                   * @param signatures Bytes array of order signatures
                   */
                  function _takeBid(
                      Order[] memory orders,
                      Exchange[] memory exchanges,
                      FeeRate memory takerFee,
                      bytes memory signatures
                  ) internal {
                      Fees memory fees = Fees(protocolFee, takerFee);
                      /**
                       * Validate all the orders potentially used in the execution and
                       * initialize the arrays for pending fulfillments.
                       */
                      (bool[] memory validOrders, uint256[][] memory pendingAmountTaken) = _validateOrders(
                          orders,
                          OrderType.BID,
                          signatures,
                          fees
                      );
                      uint256 exchangesLength = exchanges.length;
                      /* Initialize the execution batch structs. */
                      (
                          bytes memory executionBatch,
                          FungibleTransfers memory fungibleTransfers
                      ) = _initializeBatch(exchangesLength, OrderType.BID, msg.sender);
                      Order memory order;
                      Exchange memory exchange;
                      for (uint256 i; i < exchangesLength; ) {
                          exchange = exchanges[i];
                          order = orders[exchange.index];
                          /* Check the listing and exchange is valid and its parent order has already been validated. */
                          if (
                              _validateListingFromBatch(
                                  order,
                                  OrderType.BID,
                                  exchange,
                                  validOrders,
                                  pendingAmountTaken
                              )
                          ) {
                              /* Insert the transfers into the batch. */
                              _insertExecutionBid(executionBatch, fungibleTransfers, order, exchange, fees);
                              /* Record the pending fulfillment. */
                              unchecked {
                                  pendingAmountTaken[exchange.index][exchange.listing.index] += exchange
                                      .taker
                                      .amount;
                              }
                          }
                          unchecked {
                              ++i;
                          }
                      }
                      /* Execute all transfers. */
                      _executeBatchTransfer(executionBatch, fungibleTransfers, fees, OrderType.BID);
                  }
                  /*//////////////////////////////////////////////////////////////
                                        EXECUTION HELPERS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Initialize the ExecutionBatch and FungibleTransfers objects for bulk execution
                   * @param exchangesLength Number of exchanges
                   * @param orderType Order type
                   * @param taker Order taker address
                   */
                  function _initializeBatch(
                      uint256 exchangesLength,
                      OrderType orderType,
                      address taker
                  )
                      internal
                      pure
                      returns (bytes memory executionBatch, FungibleTransfers memory fungibleTransfers)
                  {
                      /* Initialize the batch. Constructing it manually in calldata packing allows for cheaper delegate execution. */
                      uint256 arrayLength = Transfer_size * exchangesLength + One_word;
                      uint256 executionBatchLength = ExecutionBatch_base_size + arrayLength;
                      executionBatch = new bytes(executionBatchLength);
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          mstore(add(calldataPointer, ExecutionBatch_taker_offset), taker)
                          mstore(add(calldataPointer, ExecutionBatch_orderType_offset), orderType)
                          mstore(add(calldataPointer, ExecutionBatch_transfers_pointer_offset), ExecutionBatch_transfers_offset) // set the transfers pointer
                          mstore(add(calldataPointer, ExecutionBatch_transfers_offset), exchangesLength) // set the length of the transfers array
                      }
                      /* Initialize the fungible transfers object. */
                      AtomicExecution[] memory executions = new AtomicExecution[](exchangesLength);
                      address[] memory feeRecipients = new address[](exchangesLength);
                      address[] memory makers = new address[](exchangesLength);
                      uint256[] memory makerTransfers = new uint256[](exchangesLength);
                      uint256[] memory feeTransfers = new uint256[](exchangesLength);
                      fungibleTransfers = FungibleTransfers({
                          totalProtocolFee: 0,
                          totalSellerTransfer: 0,
                          totalTakerFee: 0,
                          feeRecipientId: 0,
                          feeRecipients: feeRecipients,
                          makerId: 0,
                          makers: makers,
                          feeTransfers: feeTransfers,
                          makerTransfers: makerTransfers,
                          executions: executions
                      });
                  }
                  /**
                   * @notice Initialize the ExecutionBatch object for a single execution
                   * @param order Order to take a Listing from
                   * @param orderType Order type
                   * @param tokenId Token id
                   * @param amount ERC721/ERC1155 amount
                   * @param taker Order taker address
                   */
                  function _initializeSingleExecution(
                      Order memory order,
                      OrderType orderType,
                      uint256 tokenId,
                      uint256 amount,
                      address taker
                  ) internal pure returns (bytes memory executionBatch) {
                      /* Initialize the batch. Constructing it manually in calldata packing allows for cheaper delegate execution. */
                      uint256 arrayLength = Transfer_size + One_word;
                      uint256 executionBatchLength = ExecutionBatch_base_size + arrayLength;
                      executionBatch = new bytes(executionBatchLength);
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          mstore(add(calldataPointer, ExecutionBatch_taker_offset), taker)
                          mstore(add(calldataPointer, ExecutionBatch_orderType_offset), orderType)
                          mstore(add(calldataPointer, ExecutionBatch_transfers_pointer_offset), ExecutionBatch_transfers_offset) // set the transfers pointer
                          mstore(add(calldataPointer, ExecutionBatch_transfers_offset), 1) // set the length of the transfers array
                      }
                      /* Insert the transfer into the batch. */
                      _insertNonfungibleTransfer(executionBatch, order, tokenId, amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)
              pragma solidity ^0.8.0;
              import "./OwnableUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Contract module which provides access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership} and {acceptOwnership}.
               *
               * This module is used through inheritance. It will make available all functions
               * from parent (Ownable).
               */
              abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
                  function __Ownable2Step_init() internal onlyInitializing {
                      __Ownable_init_unchained();
                  }
                  function __Ownable2Step_init_unchained() internal onlyInitializing {
                  }
                  address private _pendingOwner;
                  event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Returns the address of the pending owner.
                   */
                  function pendingOwner() public view virtual returns (address) {
                      return _pendingOwner;
                  }
                  /**
                   * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual override onlyOwner {
                      _pendingOwner = newOwner;
                      emit OwnershipTransferStarted(owner(), newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual override {
                      delete _pendingOwner;
                      super._transferOwnership(newOwner);
                  }
                  /**
                   * @dev The new owner accepts the ownership transfer.
                   */
                  function acceptOwnership() external {
                      address sender = _msgSender();
                      require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
                      _transferOwnership(sender);
                  }
                  /**
                   * @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[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol)
              pragma solidity ^0.8.0;
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
              import "./Initializable.sol";
              /**
               * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
               * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
               *
               * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
               * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
               * `UUPSUpgradeable` with a custom implementation of upgrades.
               *
               * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
               *
               * _Available since v4.1._
               */
              abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
                  function __UUPSUpgradeable_init() internal onlyInitializing {
                  }
                  function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                  }
                  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
                  address private immutable __self = address(this);
                  /**
                   * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
                   * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
                   * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
                   * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
                   * fail.
                   */
                  modifier onlyProxy() {
                      require(address(this) != __self, "Function must be called through delegatecall");
                      require(_getImplementation() == __self, "Function must be called through active proxy");
                      _;
                  }
                  /**
                   * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
                   * callable on the implementing contract but not through proxies.
                   */
                  modifier notDelegated() {
                      require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                      _;
                  }
                  /**
                   * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
                   * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
                   */
                  function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
                      return _IMPLEMENTATION_SLOT;
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy to `newImplementation`.
                   *
                   * Calls {_authorizeUpgrade}.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function upgradeTo(address newImplementation) external virtual onlyProxy {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                   * encoded in `data`.
                   *
                   * Calls {_authorizeUpgrade}.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallUUPS(newImplementation, data, true);
                  }
                  /**
                   * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                   * {upgradeTo} and {upgradeToAndCall}.
                   *
                   * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                   *
                   * ```solidity
                   * function _authorizeUpgrade(address) internal override onlyOwner {}
                   * ```
                   */
                  function _authorizeUpgrade(address newImplementation) 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[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { Validation } from "./Validation.sol";
              import "./lib/Constants.sol";
              import {
                  Order,
                  Exchange,
                  FungibleTransfers,
                  StateUpdate,
                  AtomicExecution,
                  AssetType,
                  Fees,
                  FeeRate,
                  Listing,
                  Taker,
                  Transfer,
                  OrderType
              } from "./lib/Structs.sol";
              import { IDelegate } from "./interfaces/IDelegate.sol";
              import { IExecutor } from "./interfaces/IExecutor.sol";
              abstract contract Executor is IExecutor, Validation {
                  address private immutable _DELEGATE;
                  address private immutable _POOL;
                  constructor(address delegate, address pool, address proxy) Validation(proxy) {
                      _DELEGATE = delegate;
                      _POOL = pool;
                  }
                  receive() external payable {
                      if (msg.sender != _POOL) {
                          revert Unauthorized();
                      }
                  }
                  /**
                   * @notice Insert a validated ask listing into the batch if there's sufficient ETH to fulfill
                   * @param executionBatch Execution batch
                   * @param fungibleTransfers Fungible transfers
                   * @param order Order of the listing to insert
                   * @param exchange Exchange containing the listing to insert
                   * @param fees Protocol and taker fees
                   * @param remainingETH Available ETH remaining
                   * @return Available ETH remaining after insertion; if the listing was inserted in the batch
                   */
                  function _insertExecutionAsk(
                      bytes memory executionBatch,
                      FungibleTransfers memory fungibleTransfers,
                      Order memory order,
                      Exchange memory exchange,
                      Fees memory fees,
                      uint256 remainingETH
                  ) internal pure returns (uint256, bool) {
                      uint256 takerAmount = exchange.taker.amount;
                      (
                          uint256 totalPrice,
                          uint256 protocolFeeAmount,
                          uint256 makerFeeAmount,
                          uint256 takerFeeAmount
                      ) = _computeFees(exchange.listing.price, takerAmount, order.makerFee, fees);
                      /* Only insert the executions if there are sufficient funds to execute. */
                      if (remainingETH >= totalPrice + takerFeeAmount) {
                          unchecked {
                              remainingETH = remainingETH - totalPrice - takerFeeAmount;
                          }
                          _setAddresses(fungibleTransfers, order);
                          uint256 index = _insertNonfungibleTransfer(
                              executionBatch,
                              order,
                              exchange.listing.tokenId,
                              takerAmount
                          );
                          _insertFungibleTransfers(
                              fungibleTransfers,
                              takerAmount,
                              exchange.listing,
                              bytes32(order.salt),
                              index,
                              totalPrice,
                              protocolFeeAmount,
                              makerFeeAmount,
                              takerFeeAmount,
                              true
                          );
                          return (remainingETH, true);
                      } else {
                          return (remainingETH, false);
                      }
                  }
                  /**
                   * @notice Insert a validated bid listing into the batch
                   * @param executionBatch Execution batch
                   * @param fungibleTransfers Fungible transfers
                   * @param order Order of the listing to insert
                   * @param exchange Exchange containing listing to insert
                   * @param fees Protocol and taker fees
                   */
                  function _insertExecutionBid(
                      bytes memory executionBatch,
                      FungibleTransfers memory fungibleTransfers,
                      Order memory order,
                      Exchange memory exchange,
                      Fees memory fees
                  ) internal pure {
                      uint256 takerAmount = exchange.taker.amount;
                      (
                          uint256 totalPrice,
                          uint256 protocolFeeAmount,
                          uint256 makerFeeAmount,
                          uint256 takerFeeAmount
                      ) = _computeFees(exchange.listing.price, takerAmount, order.makerFee, fees);
                      _setAddresses(fungibleTransfers, order);
                      uint256 index = _insertNonfungibleTransfer(
                          executionBatch,
                          order,
                          exchange.taker.tokenId,
                          takerAmount
                      );
                      _insertFungibleTransfers(
                          fungibleTransfers,
                          takerAmount,
                          exchange.listing,
                          bytes32(order.salt),
                          index,
                          totalPrice,
                          protocolFeeAmount,
                          makerFeeAmount,
                          takerFeeAmount,
                          false
                      );
                  }
                  /**
                   * @notice Insert the nonfungible transfer into the batch
                   * @param executionBatch Execution batch
                   * @param order Order
                   * @param tokenId Token id
                   * @param amount Number of token units
                   * @return transferIndex Index of the transfer
                   */
                  function _insertNonfungibleTransfer(
                      bytes memory executionBatch,
                      Order memory order,
                      uint256 tokenId,
                      uint256 amount
                  ) internal pure returns (uint256 transferIndex) {
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          transferIndex := mload(add(calldataPointer, ExecutionBatch_length_offset))
                          let transfersOffset := mload(add(calldataPointer, ExecutionBatch_transfers_pointer_offset))
                          let transferPointer := add(
                              add(calldataPointer, add(transfersOffset, One_word)),
                              mul(transferIndex, Transfer_size)
                          )
                          mstore(
                              add(transferPointer, Transfer_trader_offset),
                              mload(add(order, Order_trader_offset))
                          ) // set the trader
                          mstore(add(transferPointer, Transfer_id_offset), tokenId) // set the token id
                          mstore(
                              add(transferPointer, Transfer_collection_offset),
                              mload(add(order, Order_collection_offset))
                          ) // set the collection
                          mstore(
                              add(transferPointer, Transfer_assetType_offset),
                              mload(add(order, Order_assetType_offset))
                          ) // set the asset type
                          mstore(add(calldataPointer, ExecutionBatch_length_offset), add(transferIndex, 1)) // increment the batch length
                          if eq(mload(add(order, Order_assetType_offset)), AssetType_ERC1155) {
                              mstore(add(transferPointer, Transfer_amount_offset), amount) // set the amount (don't need to set for ERC721's)
                          }
                      }
                  }
                  /**
                   * @notice Insert the fungible transfers that need to be executed atomically
                   * @param fungibleTransfers Fungible transfers struct
                   * @param takerAmount Amount of the listing being taken
                   * @param listing Listing to execute
                   * @param orderHash Order hash
                   * @param index Execution index
                   * @param totalPrice Total price of the purchased tokens
                   * @param protocolFeeAmount Computed protocol fee
                   * @param makerFeeAmount Computed maker fee
                   * @param takerFeeAmount Computed taker fee
                   * @param makerIsSeller Is the order maker the seller
                   */
                  function _insertFungibleTransfers(
                      FungibleTransfers memory fungibleTransfers,
                      uint256 takerAmount,
                      Listing memory listing,
                      bytes32 orderHash,
                      uint256 index,
                      uint256 totalPrice,
                      uint256 protocolFeeAmount,
                      uint256 makerFeeAmount,
                      uint256 takerFeeAmount,
                      bool makerIsSeller
                  ) internal pure {
                      uint256 makerId = fungibleTransfers.makerId;
                      fungibleTransfers.executions[index].makerId = makerId;
                      fungibleTransfers.executions[index].makerFeeRecipientId = fungibleTransfers.feeRecipientId;
                      fungibleTransfers.executions[index].stateUpdate = StateUpdate({
                          trader: fungibleTransfers.makers[makerId],
                          hash: orderHash,
                          index: listing.index,
                          value: takerAmount,
                          maxAmount: listing.amount
                      });
                      if (makerIsSeller) {
                          unchecked {
                              fungibleTransfers.executions[index].sellerAmount =
                                  totalPrice -
                                  protocolFeeAmount -
                                  makerFeeAmount;
                          }
                      } else {
                          unchecked {
                              fungibleTransfers.executions[index].sellerAmount =
                                  totalPrice -
                                  protocolFeeAmount -
                                  takerFeeAmount;
                          }
                      }
                      fungibleTransfers.executions[index].makerFeeAmount = makerFeeAmount;
                      fungibleTransfers.executions[index].takerFeeAmount = takerFeeAmount;
                      fungibleTransfers.executions[index].protocolFeeAmount = protocolFeeAmount;
                  }
                  /**
                   * @notice Set the addresses of the maker fee recipient and order maker if different than currently being batched
                   * @param fungibleTransfers Fungible transfers struct
                   * @param order Parent order of listing being added to the batch
                   */
                  function _setAddresses(
                      FungibleTransfers memory fungibleTransfers,
                      Order memory order
                  ) internal pure {
                      address feeRecipient = order.makerFee.recipient;
                      uint256 feeRecipientId = fungibleTransfers.feeRecipientId;
                      address currentFeeRecipient = fungibleTransfers.feeRecipients[feeRecipientId];
                      if (feeRecipient != currentFeeRecipient) {
                          if (currentFeeRecipient == address(0)) {
                              fungibleTransfers.feeRecipients[feeRecipientId] = feeRecipient;
                          } else {
                              unchecked {
                                  fungibleTransfers.feeRecipients[++feeRecipientId] = feeRecipient;
                              }
                              fungibleTransfers.feeRecipientId = feeRecipientId;
                          }
                      }
                      address trader = order.trader;
                      uint256 makerId = fungibleTransfers.makerId;
                      address currentTrader = fungibleTransfers.makers[makerId];
                      if (trader != currentTrader) {
                          if (currentTrader == address(0)) {
                              fungibleTransfers.makers[makerId] = trader;
                          } else {
                              unchecked {
                                  fungibleTransfers.makers[++makerId] = trader;
                              }
                              fungibleTransfers.makerId = makerId;
                          }
                      }
                  }
                  /**
                   * @notice Compute all necessary fees to be taken
                   * @param pricePerToken Price per token unit
                   * @param takerAmount Number of token units taken (should only be greater than 1 for ERC1155)
                   * @param fees Protocol and taker fee set by the transaction
                   */
                  function _computeFees(
                      uint256 pricePerToken,
                      uint256 takerAmount,
                      FeeRate memory makerFee,
                      Fees memory fees
                  )
                      internal
                      pure
                      returns (
                          uint256 totalPrice,
                          uint256 protocolFeeAmount,
                          uint256 makerFeeAmount,
                          uint256 takerFeeAmount
                      )
                  {
                      totalPrice = pricePerToken * takerAmount;
                      makerFeeAmount = (totalPrice * makerFee.rate) / _BASIS_POINTS;
                      takerFeeAmount = (totalPrice * fees.takerFee.rate) / _BASIS_POINTS;
                      protocolFeeAmount = (totalPrice * fees.protocolFee.rate) / _BASIS_POINTS;
                  }
                  /*//////////////////////////////////////////////////////////////
                                      EXECUTION FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Execute the transfers by first attempting the nonfungible transfers, for the successful transfers sum the fungible transfers by the recipients and execute
                   * @param executionBatch Execution batch struct
                   * @param fungibleTransfers Fungible transfers struct
                   * @param fees Protocol, maker, taker fees (note: makerFee will be inaccurate at this point in execution)
                   * @param orderType Order type
                   */
                  function _executeBatchTransfer(
                      bytes memory executionBatch,
                      FungibleTransfers memory fungibleTransfers,
                      Fees memory fees,
                      OrderType orderType
                  ) internal {
                      uint256 batchLength;
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          batchLength := mload(add(calldataPointer, ExecutionBatch_length_offset))
                      }
                      if (batchLength > 0) {
                          bool[] memory successfulTransfers = _executeNonfungibleTransfers(
                              executionBatch,
                              batchLength
                          );
                          uint256 transfersLength = successfulTransfers.length;
                          for (uint256 i; i < transfersLength; ) {
                              if (successfulTransfers[i]) {
                                  AtomicExecution memory execution = fungibleTransfers.executions[i];
                                  FeeRate memory makerFee;
                                  uint256 price;
                                  unchecked {
                                      if (orderType == OrderType.ASK) {
                                          fungibleTransfers.makerTransfers[execution.makerId] += execution
                                              .sellerAmount; // amount that needs to be sent *to* the order maker
                                          price =
                                              execution.sellerAmount +
                                              execution.protocolFeeAmount +
                                              execution.makerFeeAmount;
                                      } else {
                                          fungibleTransfers.makerTransfers[execution.makerId] +=
                                              execution.protocolFeeAmount +
                                              execution.makerFeeAmount +
                                              execution.takerFeeAmount +
                                              execution.sellerAmount; // amount that needs to be taken *from* the order maker
                                          price =
                                              execution.sellerAmount +
                                              execution.protocolFeeAmount +
                                              execution.takerFeeAmount;
                                      }
                                      fungibleTransfers.totalSellerTransfer += execution.sellerAmount; // only for bids
                                      fungibleTransfers.totalProtocolFee += execution.protocolFeeAmount;
                                      fungibleTransfers.totalTakerFee += execution.takerFeeAmount;
                                      fungibleTransfers.feeTransfers[execution.makerFeeRecipientId] += execution
                                          .makerFeeAmount;
                                      makerFee = FeeRate(
                                          fungibleTransfers.feeRecipients[execution.makerFeeRecipientId],
                                          uint16((execution.makerFeeAmount * _BASIS_POINTS) / price)
                                      );
                                  }
                                  /* Commit state updates. */
                                  StateUpdate memory stateUpdate = fungibleTransfers.executions[i].stateUpdate;
                                  {
                                      address trader = stateUpdate.trader;
                                      bytes32 hash = stateUpdate.hash;
                                      uint256 index = stateUpdate.index;
                                      uint256 _amountTaken = amountTaken[trader][hash][index];
                                      uint256 newAmountTaken = _amountTaken + stateUpdate.value;
                                      /* Overfulfilled Listings should be caught prior to inserting into the batch, but this check prevents any misuse. */
                                      if (newAmountTaken <= stateUpdate.maxAmount) {
                                          amountTaken[trader][hash][index] = newAmountTaken;
                                      } else {
                                          revert OrderFulfilled();
                                      }
                                  }
                                  _emitExecutionEventFromBatch(
                                      executionBatch,
                                      price,
                                      makerFee,
                                      fees,
                                      stateUpdate,
                                      orderType,
                                      i
                                  );
                              }
                              unchecked {
                                  ++i;
                              }
                          }
                          if (orderType == OrderType.ASK) {
                              /* Transfer the payments to the sellers. */
                              uint256 makersLength = fungibleTransfers.makerId + 1;
                              for (uint256 i; i < makersLength; ) {
                                  _transferETH(fungibleTransfers.makers[i], fungibleTransfers.makerTransfers[i]);
                                  unchecked {
                                      ++i;
                                  }
                              }
                              /* Transfer the fees to the fee recipients. */
                              uint256 feesLength = fungibleTransfers.feeRecipientId + 1;
                              for (uint256 i; i < feesLength; ) {
                                  _transferETH(
                                      fungibleTransfers.feeRecipients[i],
                                      fungibleTransfers.feeTransfers[i]
                                  );
                                  unchecked {
                                      ++i;
                                  }
                              }
                              /* Transfer the protocol fees. */
                              _transferETH(fees.protocolFee.recipient, fungibleTransfers.totalProtocolFee);
                              /* Transfer the taker fees. */
                              _transferETH(fees.takerFee.recipient, fungibleTransfers.totalTakerFee);
                          } else {
                              /* Take the pool funds from the buyers. */
                              uint256 makersLength = fungibleTransfers.makerId + 1;
                              for (uint256 i; i < makersLength; ) {
                                  _transferPool(
                                      fungibleTransfers.makers[i],
                                      address(this),
                                      fungibleTransfers.makerTransfers[i]
                                  );
                                  unchecked {
                                      ++i;
                                  }
                              }
                              /* Transfer the payment to the seller. */
                              _transferPool(address(this), msg.sender, fungibleTransfers.totalSellerTransfer);
                              /* Transfer the fees to the fee recipients. */
                              uint256 feesLength = fungibleTransfers.feeRecipientId + 1;
                              for (uint256 i; i < feesLength; ) {
                                  _transferPool(
                                      address(this),
                                      fungibleTransfers.feeRecipients[i],
                                      fungibleTransfers.feeTransfers[i]
                                  );
                                  unchecked {
                                      ++i;
                                  }
                              }
                              /* Transfer the protocol fees. */
                              _transferPool(
                                  address(this),
                                  fees.protocolFee.recipient,
                                  fungibleTransfers.totalProtocolFee
                              );
                              /* Transfer the taker fees. */
                              _transferPool(
                                  address(this),
                                  fees.takerFee.recipient,
                                  fungibleTransfers.totalTakerFee
                              );
                          }
                      }
                  }
                  /**
                   * @notice Attempt to execute a series of nonfungible transfers through the delegate; reverts will be skipped
                   * @param executionBatch Execution batch struct
                   * @param batchIndex Current available transfer slot in the batch
                   * @return Array indicating which transfers were successful
                   */
                  function _executeNonfungibleTransfers(
                      bytes memory executionBatch,
                      uint256 batchIndex
                  ) internal returns (bool[] memory) {
                      address delegate = _DELEGATE;
                      /* Initialize the memory space for the successful transfers array returned from the Delegate call. */
                      uint256 successfulTransfersPointer;
                      assembly {
                          successfulTransfersPointer := mload(Memory_pointer)
                          /* Need to shift the free memory pointer ahead one word to account for the array pointer returned from the call. */
                          mstore(Memory_pointer, add(successfulTransfersPointer, One_word))
                      }
                      bool[] memory successfulTransfers = new bool[](batchIndex);
                      assembly {
                          let size := mload(executionBatch)
                          let selectorPointer := add(executionBatch, ExecutionBatch_selector_offset)
                          mstore(selectorPointer, shr(Bytes4_shift, Delegate_transfer_selector))
                          let success := call(
                              gas(),
                              delegate,
                              0,
                              add(selectorPointer, Delegate_transfer_calldata_offset),
                              sub(size, Delegate_transfer_calldata_offset),
                              successfulTransfersPointer,
                              add(0x40, mul(batchIndex, One_word))
                          )
                      }
                      return successfulTransfers;
                  }
                  /*//////////////////////////////////////////////////////////////
                                      TRANSFER FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Transfer ETH
                   * @param to Recipient address
                   * @param amount Amount of ETH to send
                   */
                  function _transferETH(address to, uint256 amount) internal {
                      if (amount > 0) {
                          bool success;
                          assembly {
                              success := call(gas(), to, amount, 0, 0, 0, 0)
                          }
                          if (!success) {
                              revert ETHTransferFailed();
                          }
                      }
                  }
                  /**
                   * @notice Transfer pool funds on behalf of a user
                   * @param from Sender address
                   * @param to Recipient address
                   * @param amount Amount to send
                   */
                  function _transferPool(address from, address to, uint256 amount) internal {
                      if (amount > 0) {
                          bool success;
                          address pool = _POOL;
                          assembly {
                              let x := mload(Memory_pointer)
                              mstore(x, ERC20_transferFrom_selector)
                              mstore(add(x, ERC20_transferFrom_from_offset), from)
                              mstore(add(x, ERC20_transferFrom_to_offset), to)
                              mstore(add(x, ERC20_transferFrom_amount_offset), amount)
                              success := call(gas(), pool, 0, x, ERC20_transferFrom_size, 0, 0)
                          }
                          if (!success) {
                              revert PoolTransferFailed();
                          }
                      }
                  }
                  /**
                   * @notice Deposit ETH to user's pool funds
                   * @param to Recipient address
                   * @param amount Amount of ETH to deposit
                   */
                  function _depositPool(address to, uint256 amount) internal {
                      bool success;
                      address pool = _POOL;
                      assembly {
                          let x := mload(Memory_pointer)
                          mstore(x, Pool_deposit_selector)
                          mstore(add(x, Pool_deposit_user_offset), to)
                          success := call(gas(), pool, amount, x, Pool_deposit_size, 0, 0)
                      }
                      if (!success) {
                          revert PoolDepositFailed();
                      }
                  }
                  /**
                   * @notice Withdraw ETH from user's pool funds
                   * @param from Address to withdraw from
                   * @param amount Amount of ETH to withdraw
                   */
                  function _withdrawFromPool(address from, uint256 amount) internal {
                      bool success;
                      address pool = _POOL;
                      assembly {
                          let x := mload(Memory_pointer)
                          mstore(x, Pool_withdrawFrom_selector)
                          mstore(add(x, Pool_withdrawFrom_from_offset), from)
                          mstore(add(x, Pool_withdrawFrom_to_offset), address())
                          mstore(add(x, Pool_withdrawFrom_amount_offset), amount)
                          success := call(gas(), pool, 0, x, Pool_withdrawFrom_size, 0, 0)
                      }
                      if (!success) {
                          revert PoolWithdrawFromFailed();
                      }
                  }
                  /*//////////////////////////////////////////////////////////////
                                        EVENT EMITTERS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice Emit Execution event from a single execution
                   * @param executionBatch Execution batch struct
                   * @param price Price of the token purchased
                   * @param fees Protocol, maker, and taker fees taken
                   * @param stateUpdate Fulfillment to be recorded with a successful execution
                   * @param orderType Order type
                   * @param transferIndex Index of the transfer corresponding to the execution
                   */
                  function _emitExecutionEventFromBatch(
                      bytes memory executionBatch,
                      uint256 price,
                      FeeRate memory makerFee,
                      Fees memory fees,
                      StateUpdate memory stateUpdate,
                      OrderType orderType,
                      uint256 transferIndex
                  ) internal {
                      Transfer memory transfer;
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          let transfersOffset := mload(add(calldataPointer, ExecutionBatch_transfers_pointer_offset))
                          transfer := add(
                              add(calldataPointer, add(transfersOffset, One_word)),
                              mul(transferIndex, Transfer_size)
                          )
                      }
                      _emitOptimalExecutionEvent(
                          transfer,
                          stateUpdate.hash,
                          stateUpdate.index,
                          price,
                          makerFee,
                          fees,
                          orderType
                      );
                  }
                  /**
                   * @notice Emit the Execution event that minimizes the number of bytes in the log
                   * @param transfer The nft transfer
                   * @param orderHash Order hash
                   * @param listingIndex Index of the listing being fulfilled within the order
                   * @param price Price of the token purchased
                   * @param makerFee Maker fees taken
                   * @param fees Protocol, and taker fees taken
                   * @param orderType Order type
                   */
                  function _emitOptimalExecutionEvent(
                      Transfer memory transfer,
                      bytes32 orderHash,
                      uint256 listingIndex,
                      uint256 price,
                      FeeRate memory makerFee,
                      Fees memory fees,
                      OrderType orderType
                  ) internal {
                      if (
                          // see _insertNonfungibleTransfer; ERC721 transfers don't set the transfer amount,
                          // so we can assume the transfer amount and not check it
                          transfer.assetType == AssetType.ERC721 &&
                          fees.protocolFee.rate == 0 &&
                          transfer.id < 1 << (11 * 8) &&
                          listingIndex < 1 << (1 * 8) &&
                          price < 1 << (11 * 8)
                      ) {
                          if (makerFee.rate == 0 && fees.takerFee.rate == 0) {
                              emit Execution721Packed(
                                  orderHash,
                                  packTokenIdListingIndexTrader(transfer.id, listingIndex, transfer.trader),
                                  packTypePriceCollection(orderType, price, transfer.collection)
                              );
                              return;
                          } else if (makerFee.rate == 0) {
                              emit Execution721TakerFeePacked(
                                  orderHash,
                                  packTokenIdListingIndexTrader(transfer.id, listingIndex, transfer.trader),
                                  packTypePriceCollection(orderType, price, transfer.collection),
                                  packFee(fees.takerFee)
                              );
                              return;
                          } else if (fees.takerFee.rate == 0) {
                              emit Execution721MakerFeePacked(
                                  orderHash,
                                  packTokenIdListingIndexTrader(transfer.id, listingIndex, transfer.trader),
                                  packTypePriceCollection(orderType, price, transfer.collection),
                                  packFee(makerFee)
                              );
                              return;
                          }
                      }
                      emit Execution({
                          transfer: transfer,
                          orderHash: orderHash,
                          listingIndex: listingIndex,
                          price: price,
                          makerFee: makerFee,
                          fees: fees,
                          orderType: orderType
                      });
                  }
                  /**
                   * @notice Emit Execution event from a single execution
                   * @param executionBatch Execution batch struct
                   * @param order Order being fulfilled
                   * @param listingIndex Index of the listing being fulfilled within the order
                   * @param price Price of the token purchased
                   * @param fees Protocol, and taker fees taken
                   * @param orderType Order type
                   */
                  function _emitExecutionEvent(
                      bytes memory executionBatch,
                      Order memory order,
                      uint256 listingIndex,
                      uint256 price,
                      Fees memory fees,
                      OrderType orderType
                  ) internal {
                      Transfer memory transfer;
                      assembly {
                          let calldataPointer := add(executionBatch, ExecutionBatch_calldata_offset)
                          let transfersOffset := mload(add(calldataPointer, ExecutionBatch_transfers_pointer_offset))
                          transfer := add(calldataPointer, add(transfersOffset, One_word))
                      }
                      _emitOptimalExecutionEvent(
                          transfer,
                          bytes32(order.salt),
                          listingIndex,
                          price,
                          order.makerFee,
                          fees,
                          orderType
                      );
                  }
                  function packTokenIdListingIndexTrader(
                      uint256 tokenId,
                      uint256 listingIndex,
                      address trader
                  ) private pure returns (uint256) {
                      return (tokenId << (21 * 8)) | (listingIndex << (20 * 8)) | uint160(trader);
                  }
                  function packTypePriceCollection(
                      OrderType orderType,
                      uint256 price,
                      address collection
                  ) private pure returns (uint256) {
                      return (uint256(orderType) << (31 * 8)) | (price << (20 * 8)) | uint160(collection);
                  }
                  function packFee(FeeRate memory fee) private pure returns (uint256) {
                      return (uint256(fee.rate) << (20 * 8)) | uint160(fee.recipient);
                  }
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              uint256 constant Bytes1_shift = 0xf8;
              uint256 constant Bytes4_shift = 0xe0;
              uint256 constant Bytes20_shift = 0x60;
              uint256 constant One_word = 0x20;
              uint256 constant Memory_pointer = 0x40;
              uint256 constant AssetType_ERC721 = 0;
              uint256 constant AssetType_ERC1155 = 1;
              uint256 constant OrderType_ASK = 0;
              uint256 constant OrderType_BID = 1;
              uint256 constant Pool_withdrawFrom_selector = 0x9555a94200000000000000000000000000000000000000000000000000000000;
              uint256 constant Pool_withdrawFrom_from_offset = 0x04;
              uint256 constant Pool_withdrawFrom_to_offset = 0x24;
              uint256 constant Pool_withdrawFrom_amount_offset = 0x44;
              uint256 constant Pool_withdrawFrom_size = 0x64;
              uint256 constant Pool_deposit_selector = 0xf340fa0100000000000000000000000000000000000000000000000000000000;
              uint256 constant Pool_deposit_user_offset = 0x04;
              uint256 constant Pool_deposit_size = 0x24;
              uint256 constant ERC20_transferFrom_selector = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC721_safeTransferFrom_selector = 0x42842e0e00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC1155_safeTransferFrom_selector = 0xf242432a00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC20_transferFrom_size = 0x64;
              uint256 constant ERC721_safeTransferFrom_size = 0x64;
              uint256 constant ERC1155_safeTransferFrom_size = 0xc4;
              uint256 constant OracleSignatures_size = 0x59;
              uint256 constant OracleSignatures_s_offset = 0x20;
              uint256 constant OracleSignatures_v_offset = 0x40;
              uint256 constant OracleSignatures_blockNumber_offset = 0x41;
              uint256 constant OracleSignatures_oracle_offset = 0x45;
              uint256 constant Signatures_size = 0x41;
              uint256 constant Signatures_s_offset = 0x20;
              uint256 constant Signatures_v_offset = 0x40;
              uint256 constant ERC20_transferFrom_from_offset = 0x4;
              uint256 constant ERC20_transferFrom_to_offset = 0x24;
              uint256 constant ERC20_transferFrom_amount_offset = 0x44;
              uint256 constant ERC721_safeTransferFrom_from_offset = 0x4;
              uint256 constant ERC721_safeTransferFrom_to_offset = 0x24;
              uint256 constant ERC721_safeTransferFrom_id_offset = 0x44;
              uint256 constant ERC1155_safeTransferFrom_from_offset = 0x4;
              uint256 constant ERC1155_safeTransferFrom_to_offset = 0x24;
              uint256 constant ERC1155_safeTransferFrom_id_offset = 0x44;
              uint256 constant ERC1155_safeTransferFrom_amount_offset = 0x64;
              uint256 constant ERC1155_safeTransferFrom_data_pointer_offset = 0x84;
              uint256 constant ERC1155_safeTransferFrom_data_offset = 0xa4;
              uint256 constant Delegate_transfer_selector = 0xa1ccb98e00000000000000000000000000000000000000000000000000000000;
              uint256 constant Delegate_transfer_calldata_offset = 0x1c;
              uint256 constant Order_size = 0x100;
              uint256 constant Order_trader_offset = 0x00;
              uint256 constant Order_collection_offset = 0x20;
              uint256 constant Order_listingsRoot_offset = 0x40;
              uint256 constant Order_numberOfListings_offset = 0x60;
              uint256 constant Order_expirationTime_offset = 0x80;
              uint256 constant Order_assetType_offset = 0xa0;
              uint256 constant Order_makerFee_offset = 0xc0;
              uint256 constant Order_salt_offset = 0xe0;
              uint256 constant Exchange_size = 0x80;
              uint256 constant Exchange_askIndex_offset = 0x00;
              uint256 constant Exchange_proof_offset = 0x20;
              uint256 constant Exchange_maker_offset = 0x40;
              uint256 constant Exchange_taker_offset = 0x60;
              uint256 constant BidExchange_size = 0x80;
              uint256 constant BidExchange_askIndex_offset = 0x00;
              uint256 constant BidExchange_proof_offset = 0x20;
              uint256 constant BidExchange_maker_offset = 0x40;
              uint256 constant BidExchange_taker_offset = 0x60;
              uint256 constant Listing_size = 0x80;
              uint256 constant Listing_index_offset = 0x00;
              uint256 constant Listing_tokenId_offset = 0x20;
              uint256 constant Listing_amount_offset = 0x40;
              uint256 constant Listing_price_offset = 0x60;
              uint256 constant Taker_size = 0x40;
              uint256 constant Taker_tokenId_offset = 0x00;
              uint256 constant Taker_amount_offset = 0x20;
              uint256 constant StateUpdate_size = 0x80;
              uint256 constant StateUpdate_salt_offset = 0x20;
              uint256 constant StateUpdate_leaf_offset = 0x40;
              uint256 constant StateUpdate_value_offset = 0x60;
              uint256 constant Transfer_size = 0xa0;
              uint256 constant Transfer_trader_offset = 0x00;
              uint256 constant Transfer_id_offset = 0x20;
              uint256 constant Transfer_amount_offset = 0x40;
              uint256 constant Transfer_collection_offset = 0x60;
              uint256 constant Transfer_assetType_offset = 0x80;
              uint256 constant ExecutionBatch_selector_offset = 0x20;
              uint256 constant ExecutionBatch_calldata_offset = 0x40;
              uint256 constant ExecutionBatch_base_size = 0xa0; // size of the executionBatch without the flattened dynamic elements
              uint256 constant ExecutionBatch_taker_offset = 0x00;
              uint256 constant ExecutionBatch_orderType_offset = 0x20;
              uint256 constant ExecutionBatch_transfers_pointer_offset = 0x40;
              uint256 constant ExecutionBatch_length_offset = 0x60;
              uint256 constant ExecutionBatch_transfers_offset = 0x80;
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.17;
              struct TakeAsk {
                  Order[] orders;
                  Exchange[] exchanges;
                  FeeRate takerFee;
                  bytes signatures;
                  address tokenRecipient;
              }
              struct TakeAskSingle {
                  Order order;
                  Exchange exchange;
                  FeeRate takerFee;
                  bytes signature;
                  address tokenRecipient;
              }
              struct TakeBid {
                  Order[] orders;
                  Exchange[] exchanges;
                  FeeRate takerFee;
                  bytes signatures;
              }
              struct TakeBidSingle {
                  Order order;
                  Exchange exchange;
                  FeeRate takerFee;
                  bytes signature;
              }
              enum AssetType {
                  ERC721,
                  ERC1155
              }
              enum OrderType {
                  ASK,
                  BID
              }
              struct Exchange { // Size: 0x80
                  uint256 index; // 0x00
                  bytes32[] proof; // 0x20
                  Listing listing; // 0x40
                  Taker taker; // 0x60
              }
              struct Listing { // Size: 0x80
                  uint256 index; // 0x00
                  uint256 tokenId; // 0x20
                  uint256 amount; // 0x40
                  uint256 price; // 0x60
              }
              struct Taker { // Size: 0x40
                  uint256 tokenId; // 0x00
                  uint256 amount; // 0x20
              }
              struct Order { // Size: 0x100
                  address trader; // 0x00
                  address collection; // 0x20
                  bytes32 listingsRoot; // 0x40
                  uint256 numberOfListings; // 0x60
                  uint256 expirationTime; // 0x80
                  AssetType assetType; // 0xa0
                  FeeRate makerFee; // 0xc0
                  uint256 salt; // 0xe0
              }
              /*
              Reference only; struct is composed manually using calldata formatting in execution
              struct ExecutionBatch { // Size: 0x80
                  address taker; // 0x00
                  OrderType orderType; // 0x20
                  Transfer[] transfers; // 0x40
                  uint256 length; // 0x60
              }
              */
              struct Transfer { // Size: 0xa0
                  address trader; // 0x00
                  uint256 id; // 0x20
                  uint256 amount; // 0x40
                  address collection; // 0x60
                  AssetType assetType; // 0x80
              }
              struct FungibleTransfers {
                  uint256 totalProtocolFee;
                  uint256 totalSellerTransfer;
                  uint256 totalTakerFee;
                  uint256 feeRecipientId;
                  uint256 makerId;
                  address[] feeRecipients;
                  address[] makers;
                  uint256[] makerTransfers;
                  uint256[] feeTransfers;
                  AtomicExecution[] executions;
              }
              struct AtomicExecution { // Size: 0xe0
                  uint256 makerId; // 0x00
                  uint256 sellerAmount; // 0x20
                  uint256 makerFeeRecipientId; // 0x40
                  uint256 makerFeeAmount; // 0x60
                  uint256 takerFeeAmount; // 0x80
                  uint256 protocolFeeAmount; // 0xa0
                  StateUpdate stateUpdate; // 0xc0
              }
              struct StateUpdate { // Size: 0xa0
                  address trader; // 0x00
                  bytes32 hash; // 0x20
                  uint256 index; // 0x40
                  uint256 value; // 0x60
                  uint256 maxAmount; // 0x80
              }
              struct Fees { // Size: 0x40
                  FeeRate protocolFee; // 0x00
                  FeeRate takerFee; // 0x20
              }
              struct FeeRate { // Size: 0x40
                  address recipient; // 0x00
                  uint16 rate; // 0x20
              }
              struct Cancel {
                  bytes32 hash;
                  uint256 index;
                  uint256 amount;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import {
                  TakeAsk,
                  TakeBid,
                  TakeAskSingle,
                  TakeBidSingle,
                  Order,
                  Exchange,
                  Fees,
                  FeeRate,
                  AssetType,
                  OrderType,
                  Transfer,
                  FungibleTransfers,
                  StateUpdate,
                  Cancel,
                  Listing
              } from "../lib/Structs.sol";
              interface IBlurExchangeV2 {
                  error InsufficientFunds();
                  error TokenTransferFailed();
                  error InvalidOrder();
                  error ProtocolFeeTooHigh();
                  event NewProtocolFee(address indexed recipient, uint16 indexed rate);
                  event NewGovernor(address indexed governor);
                  event NewBlockRange(uint256 blockRange);
                  event CancelTrade(address indexed user, bytes32 hash, uint256 index, uint256 amount);
                  event NonceIncremented(address indexed user, uint256 newNonce);
                  event SetOracle(address indexed user, bool approved);
                  function initialize() external;
                  function setProtocolFee(address recipient, uint16 rate) external;
                  function setGovernor(address _governor) external;
                  function setOracle(address oracle, bool approved) external;
                  function setBlockRange(uint256 _blockRange) external;
                  function cancelTrades(Cancel[] memory cancels) external;
                  function incrementNonce() external;
                  /*//////////////////////////////////////////////////////////////
                                        EXECUTION WRAPPERS
                  //////////////////////////////////////////////////////////////*/
                  function takeAsk(TakeAsk memory inputs, bytes calldata oracleSignature) external payable;
                  function takeBid(TakeBid memory inputs, bytes calldata oracleSignature) external;
                  function takeAskSingle(TakeAskSingle memory inputs, bytes calldata oracleSignature) external payable;
                  function takeBidSingle(TakeBidSingle memory inputs, bytes calldata oracleSignature) external;
                  /*//////////////////////////////////////////////////////////////
                                      EXECUTION POOL WRAPPERS
                  //////////////////////////////////////////////////////////////*/
                  function takeAskSinglePool(
                      TakeAskSingle memory inputs,
                      bytes calldata oracleSignature,
                      uint256 amountToWithdraw
                  ) external payable;
                  function takeAskPool(
                      TakeAsk memory inputs,
                      bytes calldata oracleSignature,
                      uint256 amountToWithdraw
                  ) external payable;
              }
              // SPDX-License-Identifier: AGPL-3.0-only
              pragma solidity 0.8.17;
              /// @notice Upgradeable gas optimized reentrancy protection for smart contracts.
              /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
              abstract contract ReentrancyGuardUpgradeable {
                  uint256 private locked;
                  function __Reentrancy_init() internal {
                      locked = 1;
                  }
                  modifier nonReentrant() virtual {
                      require(locked == 1, "REENTRANCY");
                      locked = 2;
                      _;
                      locked = 1;
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/ContextUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  function __Ownable_init() internal onlyInitializing {
                      __Ownable_init_unchained();
                  }
                  function __Ownable_init_unchained() internal onlyInitializing {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
                  /**
                   * @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[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.1) (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.
                   *
                   * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                   * constructor.
                   *
                   * Emits an {Initialized} event.
                   */
                  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.
                   *
                   * 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.
                   *
                   * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                   * cannot be nested. If one is invoked in the context of another, execution will revert.
                   *
                   * 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.
                   *
                   * WARNING: setting the version to 255 will prevent any future reinitialization.
                   *
                   * Emits an {Initialized} event.
                   */
                  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.
                   *
                   * Emits an {Initialized} event the first time it is successfully executed.
                   */
                  function _disableInitializers() internal virtual {
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized < type(uint8).max) {
                          _initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
                  /**
                   * @dev Returns the highest version that has been initialized. See {reinitializer}.
                   */
                  function _getInitializedVersion() internal view returns (uint8) {
                      return _initialized;
                  }
                  /**
                   * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                   */
                  function _isInitializing() internal view returns (bool) {
                      return _initializing;
                  }
              }
              // 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.8.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 functionCallWithValue(target, data, 0, "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");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, 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) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or 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 {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // 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 (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822ProxiableUpgradeable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.3) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeaconUpgradeable.sol";
              import "../../interfaces/IERC1967Upgradeable.sol";
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../../utils/AddressUpgradeable.sol";
              import "../../utils/StorageSlotUpgradeable.sol";
              import "../utils/Initializable.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
                  function __ERC1967Upgrade_init() internal onlyInitializing {
                  }
                  function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
                  }
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          _functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(
                      address newBeacon,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @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 v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeaconUpgradeable {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.3) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.9._
               */
              interface IERC1967Upgradeable {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlotUpgradeable {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { MerkleProof } from "lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";
              import { Signatures } from "./Signatures.sol";
              import { AssetType, Order, Exchange, Listing, OrderType, FeeRate, Fees, Taker } from "./lib/Structs.sol";
              import { IValidation } from "./interfaces/IValidation.sol";
              abstract contract Validation is IValidation, Signatures {
                  uint256 internal constant _BASIS_POINTS = 10_000;
                  uint256 internal constant _MAX_PROTOCOL_FEE_RATE = 250;
                  FeeRate public protocolFee;
                  /* amountTaken[user][orderHash][listingIndex] */
                  mapping(address => mapping(bytes32 => mapping(uint256 => uint256))) public amountTaken;
                  constructor(address proxy) Signatures(proxy) {}
                  /**
                   * @notice Check if an order has expired
                   * @param order Order to check liveness
                   * @return Order is live
                   */
                  function _checkLiveness(Order memory order) private view returns (bool) {
                      return (order.expirationTime > block.timestamp);
                  }
                  /**
                   * @notice Check that the fees to be taken will not overflow the purchase price
                   * @param makerFee Maker fee amount
                   * @param fees Protocol and taker fee rates
                   * @return Fees are valid
                   */
                  function _checkFee(FeeRate memory makerFee, Fees memory fees) private pure returns (bool) {
                      return makerFee.rate + fees.takerFee.rate + fees.protocolFee.rate <= _BASIS_POINTS;
                  }
                  /**
                   * @notice Validate a list of orders and prepare arrays for recording pending fulfillments
                   * @param orders List of orders
                   * @param orderType Order type for all orders
                   * @param signatures Bytes array of the order signatures
                   * @param fees Protocol and taker fee rates
                   */
                  function _validateOrders(
                      Order[] memory orders,
                      OrderType orderType,
                      bytes memory signatures,
                      Fees memory fees
                  ) internal view returns (bool[] memory validOrders, uint256[][] memory pendingAmountTaken) {
                      uint256 ordersLength = orders.length;
                      validOrders = new bool[](ordersLength);
                      pendingAmountTaken = new uint256[][](ordersLength);
                      for (uint256 i; i < ordersLength; ) {
                          pendingAmountTaken[i] = new uint256[](orders[i].numberOfListings);
                          validOrders[i] = _validateOrder(orders[i], orderType, signatures, fees, i);
                          unchecked {
                              ++i;
                          }
                      }
                  }
                  /**
                   * @notice Validate an order
                   * @param order Order to validate
                   * @param orderType Order type
                   * @param signatures Bytes array of order signatures
                   * @param fees Protocol and taker fee rates
                   * @param signatureIndex Index of the order signature
                   * @return Validity of the order
                   */
                  function _validateOrder(
                      Order memory order,
                      OrderType orderType,
                      bytes memory signatures,
                      Fees memory fees,
                      uint256 signatureIndex
                  ) internal view returns (bool) {
                      bytes32 orderHash = hashOrder(order, orderType);
                      /* After hashing, the salt is no longer needed so we can store the order hash here. */
                      order.salt = uint256(orderHash);
                      return _verifyAuthorization(
                          order.trader,
                          orderHash,
                          signatures,
                          signatureIndex
                      ) &&
                          _checkLiveness(order) &&
                          _checkFee(order.makerFee, fees);
                  }
                  /**
                   * @notice Validate a listing (only valid if the order has be prevalidated)
                   * @dev Validation can be manipulated by inputting the same order twice in the orders array,
                   * which will effectively bypass the `pendingAmountTaken` check. There is a safety check at the
                   * execution phase that will revert the transaction if this manipulation overdraws an order.
                   * @param order Order of the listing
                   * @param orderType Order type
                   * @param exchange Exchange containing the listing
                   * @param validOrders List indicated which orders were validated
                   * @param pendingAmountTaken Pending fulfillments from the current batch
                   * @return validListing Validity of the listing
                   */
                  function _validateListingFromBatch(
                      Order memory order,
                      OrderType orderType,
                      Exchange memory exchange,
                      bool[] memory validOrders,
                      uint256[][] memory pendingAmountTaken
                  ) internal view returns (bool validListing) {
                      Listing memory listing = exchange.listing;
                      uint256 listingIndex = listing.index;
                      uint256 amountTaken = amountTaken[order.trader][bytes32(order.salt)][listingIndex];
                      uint256 pendingAmountTaken = pendingAmountTaken[exchange.index][listingIndex];
                      uint256 takerAmount = exchange.taker.amount;
                      unchecked {
                          validListing =
                              validOrders[exchange.index] &&
                              _validateListing(order, orderType, exchange) &&
                              pendingAmountTaken + takerAmount <= type(uint256).max - amountTaken &&
                              amountTaken + pendingAmountTaken + takerAmount <= listing.amount;
                      }
                  }
                  /**
                   * @notice Validate a listing and its proposed exchange
                   * @param order Order of the listing
                   * @param orderType Order type
                   * @param exchange Exchange containing the listing
                   * @return validListing Validity of the listing and its proposed exchange
                   */
                  function _validateListing(
                      Order memory order,
                      OrderType orderType,
                      Exchange memory exchange
                  ) private pure returns (bool validListing) {
                      Listing memory listing = exchange.listing;
                      validListing = MerkleProof.verify(exchange.proof, order.listingsRoot, hashListing(listing));
                      Taker memory taker = exchange.taker;
                      if (orderType == OrderType.ASK) {
                          if (order.assetType == AssetType.ERC721) {
                              validListing = validListing && taker.amount == 1 && listing.amount == 1;
                          }
                          validListing = validListing && listing.tokenId == taker.tokenId;
                      } else {
                          if (order.assetType == AssetType.ERC721) {
                              validListing = validListing && taker.amount == 1;
                          } else {
                              validListing = validListing && listing.tokenId == taker.tokenId;
                          }
                      }
                  }
                  /**
                   * @notice Validate both the listing and it's parent order (only for single executions)
                   * @param order Order of the listing
                   * @param orderType Order type
                   * @param exchange Exchange containing the listing
                   * @param signature Order signature
                   * @param fees Protocol and taker fee rates
                   * @return Validity of the order and listing
                   */
                  function _validateOrderAndListing(
                      Order memory order,
                      OrderType orderType,
                      Exchange memory exchange,
                      bytes memory signature,
                      Fees memory fees
                  ) internal view returns (bool) {
                      Listing memory listing = exchange.listing;
                      uint256 listingIndex = listing.index;
                      return
                          _validateOrder(order, orderType, signature, fees, 0) &&
                          _validateListing(order, orderType, exchange) &&
                          amountTaken[order.trader][bytes32(order.salt)][listingIndex] + exchange.taker.amount <=
                          listing.amount;
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { AssetType, OrderType, Transfer } from "../lib/Structs.sol";
              interface IDelegate {
                  function transfer(
                      address caller,
                      OrderType orderType,
                      Transfer[] calldata transfers,
                      uint256 length
                  ) external returns (bool[] memory successful);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import {
                  Fees,
                  FeeRate,
                  Transfer,
                  OrderType
              } from "../lib/Structs.sol";
              interface IExecutor {
                  error ETHTransferFailed();
                  error PoolTransferFailed();
                  error PoolWithdrawFromFailed();
                  error PoolDepositFailed();
                  error OrderFulfilled();
                  event Execution(
                      Transfer transfer,
                      bytes32 orderHash,
                      uint256 listingIndex,
                      uint256 price,
                      FeeRate makerFee,
                      Fees fees,
                      OrderType orderType
                  );
                  event Execution721Packed(
                      bytes32 orderHash,
                      uint256 tokenIdListingIndexTrader,
                      uint256 collectionPriceSide
                  );
                  event Execution721TakerFeePacked(
                      bytes32 orderHash,
                      uint256 tokenIdListingIndexTrader,
                      uint256 collectionPriceSide,
                      uint256 takerFeeRecipientRate
                  );
                  event Execution721MakerFeePacked(
                      bytes32 orderHash,
                      uint256 tokenIdListingIndexTrader,
                      uint256 collectionPriceSide,
                      uint256 makerFeeRecipientRate
                  );
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev These functions deal with verification of Merkle Tree proofs.
               *
               * The tree and the proofs can be generated using our
               * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
               * You will find a quickstart guide in the readme.
               *
               * WARNING: You should avoid using leaf values that are 64 bytes long prior to
               * hashing, or use a hash function other than keccak256 for hashing leaves.
               * This is because the concatenation of a sorted pair of internal nodes in
               * the merkle tree could be reinterpreted as a leaf value.
               * OpenZeppelin's JavaScript library generates merkle trees that are safe
               * against this attack out of the box.
               */
              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) {
                      return processProof(proof, leaf) == root;
                  }
                  /**
                   * @dev Calldata version of {verify}
                   *
                   * _Available since v4.7._
                   */
                  function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
                      return processProofCalldata(proof, leaf) == root;
                  }
                  /**
                   * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
                   * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
                   * hash matches the root of the tree. When processing the proof, the pairs
                   * of leafs & pre-images are assumed to be sorted.
                   *
                   * _Available since v4.4._
                   */
                  function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
                      bytes32 computedHash = leaf;
                      for (uint256 i = 0; i < proof.length; i++) {
                          computedHash = _hashPair(computedHash, proof[i]);
                      }
                      return computedHash;
                  }
                  /**
                   * @dev Calldata version of {processProof}
                   *
                   * _Available since v4.7._
                   */
                  function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
                      bytes32 computedHash = leaf;
                      for (uint256 i = 0; i < proof.length; i++) {
                          computedHash = _hashPair(computedHash, proof[i]);
                      }
                      return computedHash;
                  }
                  /**
                   * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
                   * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
                   *
                   * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
                   *
                   * _Available since v4.7._
                   */
                  function multiProofVerify(
                      bytes32[] memory proof,
                      bool[] memory proofFlags,
                      bytes32 root,
                      bytes32[] memory leaves
                  ) internal pure returns (bool) {
                      return processMultiProof(proof, proofFlags, leaves) == root;
                  }
                  /**
                   * @dev Calldata version of {multiProofVerify}
                   *
                   * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
                   *
                   * _Available since v4.7._
                   */
                  function multiProofVerifyCalldata(
                      bytes32[] calldata proof,
                      bool[] calldata proofFlags,
                      bytes32 root,
                      bytes32[] memory leaves
                  ) internal pure returns (bool) {
                      return processMultiProofCalldata(proof, proofFlags, leaves) == root;
                  }
                  /**
                   * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
                   * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
                   * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
                   * respectively.
                   *
                   * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
                   * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
                   * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
                   *
                   * _Available since v4.7._
                   */
                  function processMultiProof(
                      bytes32[] memory proof,
                      bool[] memory proofFlags,
                      bytes32[] memory leaves
                  ) internal pure returns (bytes32 merkleRoot) {
                      // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                      // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                      // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                      // the merkle tree.
                      uint256 leavesLen = leaves.length;
                      uint256 totalHashes = proofFlags.length;
                      // Check proof validity.
                      require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                      // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                      // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                      bytes32[] memory hashes = new bytes32[](totalHashes);
                      uint256 leafPos = 0;
                      uint256 hashPos = 0;
                      uint256 proofPos = 0;
                      // At each step, we compute the next hash using two values:
                      // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                      //   get the next hash.
                      // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                      //   `proof` array.
                      for (uint256 i = 0; i < totalHashes; i++) {
                          bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                          bytes32 b = proofFlags[i]
                              ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                              : proof[proofPos++];
                          hashes[i] = _hashPair(a, b);
                      }
                      if (totalHashes > 0) {
                          unchecked {
                              return hashes[totalHashes - 1];
                          }
                      } else if (leavesLen > 0) {
                          return leaves[0];
                      } else {
                          return proof[0];
                      }
                  }
                  /**
                   * @dev Calldata version of {processMultiProof}.
                   *
                   * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
                   *
                   * _Available since v4.7._
                   */
                  function processMultiProofCalldata(
                      bytes32[] calldata proof,
                      bool[] calldata proofFlags,
                      bytes32[] memory leaves
                  ) internal pure returns (bytes32 merkleRoot) {
                      // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
                      // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
                      // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
                      // the merkle tree.
                      uint256 leavesLen = leaves.length;
                      uint256 totalHashes = proofFlags.length;
                      // Check proof validity.
                      require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
                      // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
                      // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
                      bytes32[] memory hashes = new bytes32[](totalHashes);
                      uint256 leafPos = 0;
                      uint256 hashPos = 0;
                      uint256 proofPos = 0;
                      // At each step, we compute the next hash using two values:
                      // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
                      //   get the next hash.
                      // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
                      //   `proof` array.
                      for (uint256 i = 0; i < totalHashes; i++) {
                          bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
                          bytes32 b = proofFlags[i]
                              ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                              : proof[proofPos++];
                          hashes[i] = _hashPair(a, b);
                      }
                      if (totalHashes > 0) {
                          unchecked {
                              return hashes[totalHashes - 1];
                          }
                      } else if (leavesLen > 0) {
                          return leaves[0];
                      } else {
                          return proof[0];
                      }
                  }
                  function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
                      return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
                  }
                  function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(0x00, a)
                          mstore(0x20, b)
                          value := keccak256(0x00, 0x40)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import "./lib/Constants.sol";
              import {
                  TakeAsk,
                  TakeBid,
                  TakeAskSingle,
                  TakeBidSingle,
                  FeeRate,
                  Order,
                  OrderType,
                  AssetType,
                  Listing
              } from "./lib/Structs.sol";
              import { ISignatures } from "./interfaces/ISignatures.sol";
              abstract contract Signatures is ISignatures {
                  string private constant _NAME = "Blur Exchange";
                  string private constant _VERSION = "1.0";
                  bytes32 private immutable _FEE_RATE_TYPEHASH;
                  bytes32 private immutable _ORDER_TYPEHASH;
                  bytes32 private immutable _DOMAIN_SEPARATOR;
                  mapping(address => uint256) public oracles;
                  mapping(address => uint256) public nonces;
                  uint256 public blockRange;
                  constructor(address proxy) {
                      (_FEE_RATE_TYPEHASH, _ORDER_TYPEHASH, _DOMAIN_SEPARATOR) = _createTypehashes(proxy);
                  }
                  /**
                   * @notice Verify the domain separator produced during deployment of the implementation matches that of the proxy
                   */
                  function verifyDomain() public view {
                      bytes32 eip712DomainTypehash = keccak256(
                          bytes.concat(
                              "EIP712Domain(",
                              "string name,",
                              "string version,",
                              "uint256 chainId,",
                              "address verifyingContract",
                              ")"
                          )
                      );
                      bytes32 domainSeparator = _hashDomain(
                          eip712DomainTypehash,
                          keccak256(bytes(_NAME)),
                          keccak256(bytes(_VERSION)),
                          address(this)
                      );
                      if (_DOMAIN_SEPARATOR != domainSeparator) {
                          revert InvalidDomain();
                      }
                  }
                  /**
                   * @notice Return version and domain separator
                   */
                  function information() external view returns (string memory version, bytes32 domainSeparator) {
                      version = _VERSION;
                      domainSeparator = _DOMAIN_SEPARATOR;
                  }
                  /**
                   * @notice Create a hash of TakeAsk calldata with an approved caller
                   * @param inputs TakeAsk inputs
                   * @param _caller Address approved to execute the calldata
                   * @return Calldata hash
                   */
                  function hashTakeAsk(TakeAsk memory inputs, address _caller) external pure returns (bytes32) {
                      return _hashCalldata(_caller);
                  }
                  /**
                   * @notice Create a hash of TakeBid calldata with an approved caller
                   * @param inputs TakeBid inputs
                   * @param _caller Address approved to execute the calldata
                   * @return Calldata hash
                   */
                  function hashTakeBid(TakeBid memory inputs, address _caller) external pure returns (bytes32) {
                      return _hashCalldata(_caller);
                  }
                  /**
                   * @notice Create a hash of TakeAskSingle calldata with an approved caller
                   * @param inputs TakeAskSingle inputs
                   * @param _caller Address approved to execute the calldata
                   * @return Calldata hash
                   */
                  function hashTakeAskSingle(
                      TakeAskSingle memory inputs,
                      address _caller
                  ) external pure returns (bytes32) {
                      return _hashCalldata(_caller);
                  }
                  /**
                   * @notice Create a hash of TakeBidSingle calldata with an approved caller
                   * @param inputs TakeBidSingle inputs
                   * @param _caller Address approved to execute the calldata
                   * @return Calldata hash
                   */
                  function hashTakeBidSingle(
                      TakeBidSingle memory inputs,
                      address _caller
                  ) external pure returns (bytes32) {
                      return _hashCalldata(_caller);
                  }
                  /**
                   * @notice Create an EIP712 hash of an Order
                   * @dev Includes two additional parameters not in the struct (orderType, nonce)
                   * @param order Order to hash
                   * @param orderType OrderType of the Order
                   * @return Order EIP712 hash
                   */
                  function hashOrder(Order memory order, OrderType orderType) public view returns (bytes32) {
                      return
                          keccak256(
                              abi.encode(
                                  _ORDER_TYPEHASH,
                                  order.trader,
                                  order.collection,
                                  order.listingsRoot,
                                  order.numberOfListings,
                                  order.expirationTime,
                                  order.assetType,
                                  _hashFeeRate(order.makerFee),
                                  order.salt,
                                  orderType,
                                  nonces[order.trader]
                              )
                          );
                  }
                  /**
                   * @notice Create a hash of a Listing struct
                   * @param listing Listing to hash
                   * @return Listing hash
                   */
                  function hashListing(Listing memory listing) public pure returns (bytes32) {
                      return keccak256(abi.encode(listing.index, listing.tokenId, listing.amount, listing.price));
                  }
                  /**
                   * @notice Create a hash of calldata with an approved caller
                   * @param _caller Address approved to execute the calldata
                   * @return hash Calldata hash
                   */
                  function _hashCalldata(address _caller) internal pure returns (bytes32 hash) {
                      assembly {
                          let nextPointer := mload(0x40)
                          let size := add(sub(nextPointer, 0x80), 0x20)
                          mstore(nextPointer, _caller)
                          hash := keccak256(0x80, size)
                      }
                  }
                  /**
                   * @notice Create an EIP712 hash of a FeeRate struct
                   * @param feeRate FeeRate to hash
                   * @return FeeRate EIP712 hash
                   */
                  function _hashFeeRate(FeeRate memory feeRate) private view returns (bytes32) {
                      return keccak256(abi.encode(_FEE_RATE_TYPEHASH, feeRate.recipient, feeRate.rate));
                  }
                  /**
                   * @notice Create an EIP712 hash to sign
                   * @param hash Primary EIP712 object hash
                   * @return EIP712 hash
                   */
                  function _hashToSign(bytes32 hash) private view returns (bytes32) {
                      return keccak256(bytes.concat(bytes2(0x1901), _DOMAIN_SEPARATOR, hash));
                  }
                  /**
                   * @notice Generate all EIP712 Typehashes
                   */
                  function _createTypehashes(
                      address proxy
                  )
                      private
                      view
                      returns (bytes32 feeRateTypehash, bytes32 orderTypehash, bytes32 domainSeparator)
                  {
                      bytes32 eip712DomainTypehash = keccak256(
                          bytes.concat(
                              "EIP712Domain(",
                              "string name,",
                              "string version,",
                              "uint256 chainId,",
                              "address verifyingContract",
                              ")"
                          )
                      );
                      bytes memory feeRateTypestring = "FeeRate(address recipient,uint16 rate)";
                      orderTypehash = keccak256(
                          bytes.concat(
                              "Order(",
                              "address trader,",
                              "address collection,",
                              "bytes32 listingsRoot,",
                              "uint256 numberOfListings,",
                              "uint256 expirationTime,",
                              "uint8 assetType,",
                              "FeeRate makerFee,",
                              "uint256 salt,",
                              "uint8 orderType,",
                              "uint256 nonce",
                              ")",
                              feeRateTypestring
                          )
                      );
                      feeRateTypehash = keccak256(feeRateTypestring);
                      domainSeparator = _hashDomain(
                          eip712DomainTypehash,
                          keccak256(bytes(_NAME)),
                          keccak256(bytes(_VERSION)),
                          proxy
                      );
                  }
                  /**
                   * @notice Create an EIP712 domain separator
                   * @param eip712DomainTypehash Typehash of the EIP712Domain struct
                   * @param nameHash Hash of the contract name
                   * @param versionHash Hash of the version string
                   * @param proxy Address of the proxy this implementation will be behind
                   * @return EIP712Domain hash
                   */
                  function _hashDomain(
                      bytes32 eip712DomainTypehash,
                      bytes32 nameHash,
                      bytes32 versionHash,
                      address proxy
                  ) private view returns (bytes32) {
                      return
                          keccak256(
                              abi.encode(eip712DomainTypehash, nameHash, versionHash, block.chainid, proxy)
                          );
                  }
                  /**
                   * @notice Verify EIP712 signature
                   * @param signer Address of the alleged signer
                   * @param hash EIP712 hash
                   * @param signatures Packed bytes array of order signatures
                   * @param index Index of the signature to verify
                   * @return authorized Validity of the signature
                   */
                  function _verifyAuthorization(
                      address signer,
                      bytes32 hash,
                      bytes memory signatures,
                      uint256 index
                  ) internal view returns (bool authorized) {
                      bytes32 hashToSign = _hashToSign(hash);
                      bytes32 r;
                      bytes32 s;
                      uint8 v;
                      assembly {
                          let signatureOffset := add(add(signatures, One_word), mul(Signatures_size, index))
                          r := mload(signatureOffset)
                          s := mload(add(signatureOffset, Signatures_s_offset))
                          v := shr(Bytes1_shift, mload(add(signatureOffset, Signatures_v_offset)))
                      }
                      authorized = _verify(signer, hashToSign, v, r, s);
                  }
                  modifier verifyOracleSignature(bytes32 hash, bytes calldata oracleSignature) {
                      bytes32 r;
                      bytes32 s;
                      uint8 v;
                      uint32 blockNumber;
                      address oracle;
                      assembly {
                          let signatureOffset := oracleSignature.offset
                          r := calldataload(signatureOffset)
                          s := calldataload(add(signatureOffset, OracleSignatures_s_offset))
                          v := shr(Bytes1_shift, calldataload(add(signatureOffset, OracleSignatures_v_offset)))
                          blockNumber := shr(
                              Bytes4_shift,
                              calldataload(add(signatureOffset, OracleSignatures_blockNumber_offset))
                          )
                          oracle := shr(
                              Bytes20_shift,
                              calldataload(add(signatureOffset, OracleSignatures_oracle_offset))
                          )
                      }
                      if (blockNumber + blockRange < block.number) {
                          revert ExpiredOracleSignature();
                      }
                      if (oracles[oracle] == 0) {
                          revert UnauthorizedOracle();
                      }
                      if (!_verify(oracle, keccak256(abi.encodePacked(hash, blockNumber)), v, r, s)) {
                          revert InvalidOracleSignature();
                      }
                      _;
                  }
                  /**
                   * @notice Verify signature of digest
                   * @param signer Address of expected signer
                   * @param digest Signature digest
                   * @param v v parameter
                   * @param r r parameter
                   * @param s s parameter
                   */
                  function _verify(
                      address signer,
                      bytes32 digest,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) private pure returns (bool valid) {
                      address recoveredSigner = ecrecover(digest, v, r, s);
                      if (recoveredSigner != address(0) && recoveredSigner == signer) {
                          valid = true;
                      }
                  }
                  uint256[47] private __gap;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { FeeRate } from "../lib/Structs.sol";
              interface IValidation {
                  function protocolFee() external view returns (address, uint16);
                  function amountTaken(address user, bytes32 hash, uint256 listingIndex) external view returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import {
                  TakeAsk,
                  TakeBid,
                  TakeAskSingle,
                  TakeBidSingle,
                  Order,
                  OrderType,
                  Listing
              } from "../lib/Structs.sol";
              interface ISignatures {
                  error Unauthorized();
                  error ExpiredOracleSignature();
                  error UnauthorizedOracle();
                  error InvalidOracleSignature();
                  error InvalidDomain();
                  function oracles(address oracle) external view returns (uint256);
                  function nonces(address user) external view returns (uint256);
                  function blockRange() external view returns (uint256);
                  function verifyDomain() external view;
                  function information() external view returns (string memory version, bytes32 domainSeparator);
                  function hashListing(Listing memory listing) external pure returns (bytes32);
                  function hashOrder(Order memory order, OrderType orderType) external view returns (bytes32);
                  function hashTakeAsk(TakeAsk memory inputs, address _caller) external pure returns (bytes32);
                  function hashTakeBid(TakeBid memory inputs, address _caller) external pure returns (bytes32);
                  function hashTakeAskSingle(TakeAskSingle memory inputs, address _caller) external pure returns (bytes32);
                  function hashTakeBidSingle(TakeBidSingle memory inputs, address _caller) external pure returns (bytes32);
              }
              

              File 4 of 6: Delegate
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              import { ERC721 } from "lib/solmate/src/tokens/ERC721.sol";
              import { ERC1155 } from "lib/solmate/src/tokens/ERC1155.sol";
              import { ERC20 } from "lib/solmate/src/tokens/ERC20.sol";
              import "./lib/Constants.sol";
              import { AssetType, OrderType, Transfer } from "./lib/Structs.sol";
              contract Delegate {
                  error Unauthorized();
                  error InvalidLength();
                  address private immutable _EXCHANGE;
                  constructor(address exchange) {
                      _EXCHANGE = exchange;
                  }
                  modifier onlyApproved() {
                      if (msg.sender != _EXCHANGE) {
                          revert Unauthorized();
                      }
                      _;
                  }
                  function transfer(
                      address taker,
                      OrderType orderType,
                      Transfer[] calldata transfers,
                      uint256 length
                  ) external onlyApproved returns (bool[] memory successful) {
                      if (transfers.length < length) {
                          revert InvalidLength();
                      }
                      successful = new bool[](length);
                      for (uint256 i; i < length; ) {
                          assembly {
                              let calldataPointer := mload(0x40)
                              let transfersPointer := add(transfers.offset, mul(Transfer_size, i))
                              let assetType := calldataload(add(transfersPointer, Transfer_assetType_offset))
                              switch assetType
                              case 0 {
                                  // AssetType_ERC721
                                  mstore(calldataPointer, ERC721_safeTransferFrom_selector)
                                  switch orderType
                                  case 0 {
                                      // OrderType_ASK; taker is recipient
                                      mstore(add(calldataPointer, ERC721_safeTransferFrom_to_offset), taker)
                                      mstore(
                                          add(calldataPointer, ERC721_safeTransferFrom_from_offset),
                                          calldataload(add(transfersPointer, Transfer_trader_offset))
                                      )
                                  }
                                  case 1 {
                                      // OrderType_BID; taker is sender
                                      mstore(add(calldataPointer, ERC721_safeTransferFrom_from_offset), taker)
                                      mstore(
                                          add(calldataPointer, ERC721_safeTransferFrom_to_offset),
                                          calldataload(add(transfersPointer, Transfer_trader_offset))
                                      )
                                  }
                                  default {
                                      revert(0, 0)
                                  }
                                  mstore(
                                      add(calldataPointer, ERC721_safeTransferFrom_id_offset),
                                      calldataload(add(transfersPointer, Transfer_id_offset))
                                  )
                                  let collection := calldataload(
                                      add(transfersPointer, Transfer_collection_offset)
                                  )
                                  let success := call(
                                      gas(),
                                      collection,
                                      0,
                                      calldataPointer,
                                      ERC721_safeTransferFrom_size,
                                      0,
                                      0
                                  )
                                  mstore(add(add(successful, 0x20), mul(0x20, i)), success)
                              }
                              case 1 {
                                  // AssetType_ERC1155
                                  mstore(calldataPointer, ERC1155_safeTransferFrom_selector)
                                  switch orderType
                                  case 0 {
                                      // OrderType_ASK; taker is recipient
                                      mstore(
                                          add(calldataPointer, ERC1155_safeTransferFrom_from_offset),
                                          calldataload(
                                              add(
                                                  transfersPointer,
                                                  Transfer_trader_offset
                                              )
                                          )
                                      )
                                      mstore(add(calldataPointer, ERC1155_safeTransferFrom_to_offset), taker)
                                  }
                                  case 1 {
                                      // OrderType_BID; taker is sender
                                      mstore(
                                          add(calldataPointer, ERC1155_safeTransferFrom_to_offset),
                                          calldataload(
                                              add(
                                                  transfersPointer,
                                                  Transfer_trader_offset
                                              )
                                          )
                                      )
                                      mstore(add(calldataPointer, ERC1155_safeTransferFrom_from_offset), taker)
                                  }
                                  default {
                                      revert(0, 0)
                                  }
                                  mstore(add(calldataPointer, ERC1155_safeTransferFrom_data_pointer_offset), 0xa0)
                                  mstore(add(calldataPointer, ERC1155_safeTransferFrom_data_offset), 0)
                                  mstore(
                                      add(calldataPointer, ERC1155_safeTransferFrom_id_offset),
                                      calldataload(
                                          add(transfersPointer, Transfer_id_offset)
                                      )
                                  )
                                  mstore(
                                      add(calldataPointer, ERC1155_safeTransferFrom_amount_offset),
                                      calldataload(
                                          add(
                                              transfersPointer,
                                              Transfer_amount_offset
                                          )
                                      )
                                  )
                                  let collection := calldataload(
                                      add(
                                          transfersPointer,
                                          Transfer_collection_offset
                                      )
                                  )
                                  let success := call(
                                      gas(),
                                      collection,
                                      0,
                                      calldataPointer,
                                      ERC1155_safeTransferFrom_size,
                                      0,
                                      0
                                  )
                                  mstore(add(add(successful, 0x20), mul(0x20, i)), success)
                              }
                              default {
                                  revert(0, 0)
                              }
                          }
                          unchecked {
                              ++i;
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: AGPL-3.0-only
              pragma solidity >=0.8.0;
              /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
              /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
              abstract contract ERC721 {
                  /*//////////////////////////////////////////////////////////////
                                               EVENTS
                  //////////////////////////////////////////////////////////////*/
                  event Transfer(address indexed from, address indexed to, uint256 indexed id);
                  event Approval(address indexed owner, address indexed spender, uint256 indexed id);
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /*//////////////////////////////////////////////////////////////
                                       METADATA STORAGE/LOGIC
                  //////////////////////////////////////////////////////////////*/
                  string public name;
                  string public symbol;
                  function tokenURI(uint256 id) public view virtual returns (string memory);
                  /*//////////////////////////////////////////////////////////////
                                    ERC721 BALANCE/OWNER STORAGE
                  //////////////////////////////////////////////////////////////*/
                  mapping(uint256 => address) internal _ownerOf;
                  mapping(address => uint256) internal _balanceOf;
                  function ownerOf(uint256 id) public view virtual returns (address owner) {
                      require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
                  }
                  function balanceOf(address owner) public view virtual returns (uint256) {
                      require(owner != address(0), "ZERO_ADDRESS");
                      return _balanceOf[owner];
                  }
                  /*//////////////////////////////////////////////////////////////
                                       ERC721 APPROVAL STORAGE
                  //////////////////////////////////////////////////////////////*/
                  mapping(uint256 => address) public getApproved;
                  mapping(address => mapping(address => bool)) public isApprovedForAll;
                  /*//////////////////////////////////////////////////////////////
                                             CONSTRUCTOR
                  //////////////////////////////////////////////////////////////*/
                  constructor(string memory _name, string memory _symbol) {
                      name = _name;
                      symbol = _symbol;
                  }
                  /*//////////////////////////////////////////////////////////////
                                            ERC721 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function approve(address spender, uint256 id) public virtual {
                      address owner = _ownerOf[id];
                      require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
                      getApproved[id] = spender;
                      emit Approval(owner, spender, id);
                  }
                  function setApprovalForAll(address operator, bool approved) public virtual {
                      isApprovedForAll[msg.sender][operator] = approved;
                      emit ApprovalForAll(msg.sender, operator, approved);
                  }
                  function transferFrom(
                      address from,
                      address to,
                      uint256 id
                  ) public virtual {
                      require(from == _ownerOf[id], "WRONG_FROM");
                      require(to != address(0), "INVALID_RECIPIENT");
                      require(
                          msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                          "NOT_AUTHORIZED"
                      );
                      // Underflow of the sender's balance is impossible because we check for
                      // ownership above and the recipient's balance can't realistically overflow.
                      unchecked {
                          _balanceOf[from]--;
                          _balanceOf[to]++;
                      }
                      _ownerOf[id] = to;
                      delete getApproved[id];
                      emit Transfer(from, to, id);
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 id
                  ) public virtual {
                      transferFrom(from, to, id);
                      require(
                          to.code.length == 0 ||
                              ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                              ERC721TokenReceiver.onERC721Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 id,
                      bytes calldata data
                  ) public virtual {
                      transferFrom(from, to, id);
                      require(
                          to.code.length == 0 ||
                              ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                              ERC721TokenReceiver.onERC721Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  /*//////////////////////////////////////////////////////////////
                                            ERC165 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                      return
                          interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                          interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                          interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
                  }
                  /*//////////////////////////////////////////////////////////////
                                      INTERNAL MINT/BURN LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function _mint(address to, uint256 id) internal virtual {
                      require(to != address(0), "INVALID_RECIPIENT");
                      require(_ownerOf[id] == address(0), "ALREADY_MINTED");
                      // Counter overflow is incredibly unrealistic.
                      unchecked {
                          _balanceOf[to]++;
                      }
                      _ownerOf[id] = to;
                      emit Transfer(address(0), to, id);
                  }
                  function _burn(uint256 id) internal virtual {
                      address owner = _ownerOf[id];
                      require(owner != address(0), "NOT_MINTED");
                      // Ownership check above ensures no underflow.
                      unchecked {
                          _balanceOf[owner]--;
                      }
                      delete _ownerOf[id];
                      delete getApproved[id];
                      emit Transfer(owner, address(0), id);
                  }
                  /*//////////////////////////////////////////////////////////////
                                      INTERNAL SAFE MINT LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function _safeMint(address to, uint256 id) internal virtual {
                      _mint(to, id);
                      require(
                          to.code.length == 0 ||
                              ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                              ERC721TokenReceiver.onERC721Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function _safeMint(
                      address to,
                      uint256 id,
                      bytes memory data
                  ) internal virtual {
                      _mint(to, id);
                      require(
                          to.code.length == 0 ||
                              ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                              ERC721TokenReceiver.onERC721Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
              }
              /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
              /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
              abstract contract ERC721TokenReceiver {
                  function onERC721Received(
                      address,
                      address,
                      uint256,
                      bytes calldata
                  ) external virtual returns (bytes4) {
                      return ERC721TokenReceiver.onERC721Received.selector;
                  }
              }
              // SPDX-License-Identifier: AGPL-3.0-only
              pragma solidity >=0.8.0;
              /// @notice Minimalist and gas efficient standard ERC1155 implementation.
              /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
              abstract contract ERC1155 {
                  /*//////////////////////////////////////////////////////////////
                                               EVENTS
                  //////////////////////////////////////////////////////////////*/
                  event TransferSingle(
                      address indexed operator,
                      address indexed from,
                      address indexed to,
                      uint256 id,
                      uint256 amount
                  );
                  event TransferBatch(
                      address indexed operator,
                      address indexed from,
                      address indexed to,
                      uint256[] ids,
                      uint256[] amounts
                  );
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  event URI(string value, uint256 indexed id);
                  /*//////////////////////////////////////////////////////////////
                                           ERC1155 STORAGE
                  //////////////////////////////////////////////////////////////*/
                  mapping(address => mapping(uint256 => uint256)) public balanceOf;
                  mapping(address => mapping(address => bool)) public isApprovedForAll;
                  /*//////////////////////////////////////////////////////////////
                                           METADATA LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function uri(uint256 id) public view virtual returns (string memory);
                  /*//////////////////////////////////////////////////////////////
                                            ERC1155 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function setApprovalForAll(address operator, bool approved) public virtual {
                      isApprovedForAll[msg.sender][operator] = approved;
                      emit ApprovalForAll(msg.sender, operator, approved);
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 id,
                      uint256 amount,
                      bytes calldata data
                  ) public virtual {
                      require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
                      balanceOf[from][id] -= amount;
                      balanceOf[to][id] += amount;
                      emit TransferSingle(msg.sender, from, to, id, amount);
                      require(
                          to.code.length == 0
                              ? to != address(0)
                              : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
                                  ERC1155TokenReceiver.onERC1155Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function safeBatchTransferFrom(
                      address from,
                      address to,
                      uint256[] calldata ids,
                      uint256[] calldata amounts,
                      bytes calldata data
                  ) public virtual {
                      require(ids.length == amounts.length, "LENGTH_MISMATCH");
                      require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
                      // Storing these outside the loop saves ~15 gas per iteration.
                      uint256 id;
                      uint256 amount;
                      for (uint256 i = 0; i < ids.length; ) {
                          id = ids[i];
                          amount = amounts[i];
                          balanceOf[from][id] -= amount;
                          balanceOf[to][id] += amount;
                          // An array can't have a total length
                          // larger than the max uint256 value.
                          unchecked {
                              ++i;
                          }
                      }
                      emit TransferBatch(msg.sender, from, to, ids, amounts);
                      require(
                          to.code.length == 0
                              ? to != address(0)
                              : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
                                  ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
                      public
                      view
                      virtual
                      returns (uint256[] memory balances)
                  {
                      require(owners.length == ids.length, "LENGTH_MISMATCH");
                      balances = new uint256[](owners.length);
                      // Unchecked because the only math done is incrementing
                      // the array index counter which cannot possibly overflow.
                      unchecked {
                          for (uint256 i = 0; i < owners.length; ++i) {
                              balances[i] = balanceOf[owners[i]][ids[i]];
                          }
                      }
                  }
                  /*//////////////////////////////////////////////////////////////
                                            ERC165 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                      return
                          interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                          interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
                          interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
                  }
                  /*//////////////////////////////////////////////////////////////
                                      INTERNAL MINT/BURN LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function _mint(
                      address to,
                      uint256 id,
                      uint256 amount,
                      bytes memory data
                  ) internal virtual {
                      balanceOf[to][id] += amount;
                      emit TransferSingle(msg.sender, address(0), to, id, amount);
                      require(
                          to.code.length == 0
                              ? to != address(0)
                              : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
                                  ERC1155TokenReceiver.onERC1155Received.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function _batchMint(
                      address to,
                      uint256[] memory ids,
                      uint256[] memory amounts,
                      bytes memory data
                  ) internal virtual {
                      uint256 idsLength = ids.length; // Saves MLOADs.
                      require(idsLength == amounts.length, "LENGTH_MISMATCH");
                      for (uint256 i = 0; i < idsLength; ) {
                          balanceOf[to][ids[i]] += amounts[i];
                          // An array can't have a total length
                          // larger than the max uint256 value.
                          unchecked {
                              ++i;
                          }
                      }
                      emit TransferBatch(msg.sender, address(0), to, ids, amounts);
                      require(
                          to.code.length == 0
                              ? to != address(0)
                              : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
                                  ERC1155TokenReceiver.onERC1155BatchReceived.selector,
                          "UNSAFE_RECIPIENT"
                      );
                  }
                  function _batchBurn(
                      address from,
                      uint256[] memory ids,
                      uint256[] memory amounts
                  ) internal virtual {
                      uint256 idsLength = ids.length; // Saves MLOADs.
                      require(idsLength == amounts.length, "LENGTH_MISMATCH");
                      for (uint256 i = 0; i < idsLength; ) {
                          balanceOf[from][ids[i]] -= amounts[i];
                          // An array can't have a total length
                          // larger than the max uint256 value.
                          unchecked {
                              ++i;
                          }
                      }
                      emit TransferBatch(msg.sender, from, address(0), ids, amounts);
                  }
                  function _burn(
                      address from,
                      uint256 id,
                      uint256 amount
                  ) internal virtual {
                      balanceOf[from][id] -= amount;
                      emit TransferSingle(msg.sender, from, address(0), id, amount);
                  }
              }
              /// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
              /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
              abstract contract ERC1155TokenReceiver {
                  function onERC1155Received(
                      address,
                      address,
                      uint256,
                      uint256,
                      bytes calldata
                  ) external virtual returns (bytes4) {
                      return ERC1155TokenReceiver.onERC1155Received.selector;
                  }
                  function onERC1155BatchReceived(
                      address,
                      address,
                      uint256[] calldata,
                      uint256[] calldata,
                      bytes calldata
                  ) external virtual returns (bytes4) {
                      return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
                  }
              }
              // SPDX-License-Identifier: AGPL-3.0-only
              pragma solidity >=0.8.0;
              /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
              /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
              /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
              /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
              abstract contract ERC20 {
                  /*//////////////////////////////////////////////////////////////
                                               EVENTS
                  //////////////////////////////////////////////////////////////*/
                  event Transfer(address indexed from, address indexed to, uint256 amount);
                  event Approval(address indexed owner, address indexed spender, uint256 amount);
                  /*//////////////////////////////////////////////////////////////
                                          METADATA STORAGE
                  //////////////////////////////////////////////////////////////*/
                  string public name;
                  string public symbol;
                  uint8 public immutable decimals;
                  /*//////////////////////////////////////////////////////////////
                                            ERC20 STORAGE
                  //////////////////////////////////////////////////////////////*/
                  uint256 public totalSupply;
                  mapping(address => uint256) public balanceOf;
                  mapping(address => mapping(address => uint256)) public allowance;
                  /*//////////////////////////////////////////////////////////////
                                          EIP-2612 STORAGE
                  //////////////////////////////////////////////////////////////*/
                  uint256 internal immutable INITIAL_CHAIN_ID;
                  bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
                  mapping(address => uint256) public nonces;
                  /*//////////////////////////////////////////////////////////////
                                             CONSTRUCTOR
                  //////////////////////////////////////////////////////////////*/
                  constructor(
                      string memory _name,
                      string memory _symbol,
                      uint8 _decimals
                  ) {
                      name = _name;
                      symbol = _symbol;
                      decimals = _decimals;
                      INITIAL_CHAIN_ID = block.chainid;
                      INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
                  }
                  /*//////////////////////////////////////////////////////////////
                                             ERC20 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function approve(address spender, uint256 amount) public virtual returns (bool) {
                      allowance[msg.sender][spender] = amount;
                      emit Approval(msg.sender, spender, amount);
                      return true;
                  }
                  function transfer(address to, uint256 amount) public virtual returns (bool) {
                      balanceOf[msg.sender] -= amount;
                      // Cannot overflow because the sum of all user
                      // balances can't exceed the max uint256 value.
                      unchecked {
                          balanceOf[to] += amount;
                      }
                      emit Transfer(msg.sender, to, amount);
                      return true;
                  }
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) public virtual returns (bool) {
                      uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
                      if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
                      balanceOf[from] -= amount;
                      // Cannot overflow because the sum of all user
                      // balances can't exceed the max uint256 value.
                      unchecked {
                          balanceOf[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      return true;
                  }
                  /*//////////////////////////////////////////////////////////////
                                           EIP-2612 LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual {
                      require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
                      // Unchecked because the only math done is incrementing
                      // the owner's nonce which cannot realistically overflow.
                      unchecked {
                          address recoveredAddress = ecrecover(
                              keccak256(
                                  abi.encodePacked(
                                      "\\x19\\x01",
                                      DOMAIN_SEPARATOR(),
                                      keccak256(
                                          abi.encode(
                                              keccak256(
                                                  "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                              ),
                                              owner,
                                              spender,
                                              value,
                                              nonces[owner]++,
                                              deadline
                                          )
                                      )
                                  )
                              ),
                              v,
                              r,
                              s
                          );
                          require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
                          allowance[recoveredAddress][spender] = value;
                      }
                      emit Approval(owner, spender, value);
                  }
                  function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
                      return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
                  }
                  function computeDomainSeparator() internal view virtual returns (bytes32) {
                      return
                          keccak256(
                              abi.encode(
                                  keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                  keccak256(bytes(name)),
                                  keccak256("1"),
                                  block.chainid,
                                  address(this)
                              )
                          );
                  }
                  /*//////////////////////////////////////////////////////////////
                                      INTERNAL MINT/BURN LOGIC
                  //////////////////////////////////////////////////////////////*/
                  function _mint(address to, uint256 amount) internal virtual {
                      totalSupply += amount;
                      // Cannot overflow because the sum of all user
                      // balances can't exceed the max uint256 value.
                      unchecked {
                          balanceOf[to] += amount;
                      }
                      emit Transfer(address(0), to, amount);
                  }
                  function _burn(address from, uint256 amount) internal virtual {
                      balanceOf[from] -= amount;
                      // Cannot underflow because a user's balance
                      // will never be larger than the total supply.
                      unchecked {
                          totalSupply -= amount;
                      }
                      emit Transfer(from, address(0), amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.17;
              uint256 constant Bytes1_shift = 0xf8;
              uint256 constant Bytes4_shift = 0xe0;
              uint256 constant Bytes20_shift = 0x60;
              uint256 constant One_word = 0x20;
              uint256 constant Memory_pointer = 0x40;
              uint256 constant AssetType_ERC721 = 0;
              uint256 constant AssetType_ERC1155 = 1;
              uint256 constant OrderType_ASK = 0;
              uint256 constant OrderType_BID = 1;
              uint256 constant Pool_withdrawFrom_selector = 0x9555a94200000000000000000000000000000000000000000000000000000000;
              uint256 constant Pool_withdrawFrom_from_offset = 0x04;
              uint256 constant Pool_withdrawFrom_to_offset = 0x24;
              uint256 constant Pool_withdrawFrom_amount_offset = 0x44;
              uint256 constant Pool_withdrawFrom_size = 0x64;
              uint256 constant Pool_deposit_selector = 0xf340fa0100000000000000000000000000000000000000000000000000000000;
              uint256 constant Pool_deposit_user_offset = 0x04;
              uint256 constant Pool_deposit_size = 0x24;
              uint256 constant ERC20_transferFrom_selector = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC721_safeTransferFrom_selector = 0x42842e0e00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC1155_safeTransferFrom_selector = 0xf242432a00000000000000000000000000000000000000000000000000000000;
              uint256 constant ERC20_transferFrom_size = 0x64;
              uint256 constant ERC721_safeTransferFrom_size = 0x64;
              uint256 constant ERC1155_safeTransferFrom_size = 0xc4;
              uint256 constant OracleSignatures_size = 0x59;
              uint256 constant OracleSignatures_s_offset = 0x20;
              uint256 constant OracleSignatures_v_offset = 0x40;
              uint256 constant OracleSignatures_blockNumber_offset = 0x41;
              uint256 constant OracleSignatures_oracle_offset = 0x45;
              uint256 constant Signatures_size = 0x41;
              uint256 constant Signatures_s_offset = 0x20;
              uint256 constant Signatures_v_offset = 0x40;
              uint256 constant ERC20_transferFrom_from_offset = 0x4;
              uint256 constant ERC20_transferFrom_to_offset = 0x24;
              uint256 constant ERC20_transferFrom_amount_offset = 0x44;
              uint256 constant ERC721_safeTransferFrom_from_offset = 0x4;
              uint256 constant ERC721_safeTransferFrom_to_offset = 0x24;
              uint256 constant ERC721_safeTransferFrom_id_offset = 0x44;
              uint256 constant ERC1155_safeTransferFrom_from_offset = 0x4;
              uint256 constant ERC1155_safeTransferFrom_to_offset = 0x24;
              uint256 constant ERC1155_safeTransferFrom_id_offset = 0x44;
              uint256 constant ERC1155_safeTransferFrom_amount_offset = 0x64;
              uint256 constant ERC1155_safeTransferFrom_data_pointer_offset = 0x84;
              uint256 constant ERC1155_safeTransferFrom_data_offset = 0xa4;
              uint256 constant Delegate_transfer_selector = 0xa1ccb98e00000000000000000000000000000000000000000000000000000000;
              uint256 constant Delegate_transfer_calldata_offset = 0x1c;
              uint256 constant Order_size = 0x100;
              uint256 constant Order_trader_offset = 0x00;
              uint256 constant Order_collection_offset = 0x20;
              uint256 constant Order_listingsRoot_offset = 0x40;
              uint256 constant Order_numberOfListings_offset = 0x60;
              uint256 constant Order_expirationTime_offset = 0x80;
              uint256 constant Order_assetType_offset = 0xa0;
              uint256 constant Order_makerFee_offset = 0xc0;
              uint256 constant Order_salt_offset = 0xe0;
              uint256 constant Exchange_size = 0x80;
              uint256 constant Exchange_askIndex_offset = 0x00;
              uint256 constant Exchange_proof_offset = 0x20;
              uint256 constant Exchange_maker_offset = 0x40;
              uint256 constant Exchange_taker_offset = 0x60;
              uint256 constant BidExchange_size = 0x80;
              uint256 constant BidExchange_askIndex_offset = 0x00;
              uint256 constant BidExchange_proof_offset = 0x20;
              uint256 constant BidExchange_maker_offset = 0x40;
              uint256 constant BidExchange_taker_offset = 0x60;
              uint256 constant Listing_size = 0x80;
              uint256 constant Listing_index_offset = 0x00;
              uint256 constant Listing_tokenId_offset = 0x20;
              uint256 constant Listing_amount_offset = 0x40;
              uint256 constant Listing_price_offset = 0x60;
              uint256 constant Taker_size = 0x40;
              uint256 constant Taker_tokenId_offset = 0x00;
              uint256 constant Taker_amount_offset = 0x20;
              uint256 constant StateUpdate_size = 0x80;
              uint256 constant StateUpdate_salt_offset = 0x20;
              uint256 constant StateUpdate_leaf_offset = 0x40;
              uint256 constant StateUpdate_value_offset = 0x60;
              uint256 constant Transfer_size = 0xa0;
              uint256 constant Transfer_trader_offset = 0x00;
              uint256 constant Transfer_id_offset = 0x20;
              uint256 constant Transfer_amount_offset = 0x40;
              uint256 constant Transfer_collection_offset = 0x60;
              uint256 constant Transfer_assetType_offset = 0x80;
              uint256 constant ExecutionBatch_selector_offset = 0x20;
              uint256 constant ExecutionBatch_calldata_offset = 0x40;
              uint256 constant ExecutionBatch_base_size = 0xa0; // size of the executionBatch without the flattened dynamic elements
              uint256 constant ExecutionBatch_taker_offset = 0x00;
              uint256 constant ExecutionBatch_orderType_offset = 0x20;
              uint256 constant ExecutionBatch_transfers_pointer_offset = 0x40;
              uint256 constant ExecutionBatch_length_offset = 0x60;
              uint256 constant ExecutionBatch_transfers_offset = 0x80;
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.17;
              struct TakeAsk {
                  Order[] orders;
                  Exchange[] exchanges;
                  FeeRate takerFee;
                  bytes signatures;
                  address tokenRecipient;
              }
              struct TakeAskSingle {
                  Order order;
                  Exchange exchange;
                  FeeRate takerFee;
                  bytes signature;
                  address tokenRecipient;
              }
              struct TakeBid {
                  Order[] orders;
                  Exchange[] exchanges;
                  FeeRate takerFee;
                  bytes signatures;
              }
              struct TakeBidSingle {
                  Order order;
                  Exchange exchange;
                  FeeRate takerFee;
                  bytes signature;
              }
              enum AssetType {
                  ERC721,
                  ERC1155
              }
              enum OrderType {
                  ASK,
                  BID
              }
              struct Exchange { // Size: 0x80
                  uint256 index; // 0x00
                  bytes32[] proof; // 0x20
                  Listing listing; // 0x40
                  Taker taker; // 0x60
              }
              struct Listing { // Size: 0x80
                  uint256 index; // 0x00
                  uint256 tokenId; // 0x20
                  uint256 amount; // 0x40
                  uint256 price; // 0x60
              }
              struct Taker { // Size: 0x40
                  uint256 tokenId; // 0x00
                  uint256 amount; // 0x20
              }
              struct Order { // Size: 0x100
                  address trader; // 0x00
                  address collection; // 0x20
                  bytes32 listingsRoot; // 0x40
                  uint256 numberOfListings; // 0x60
                  uint256 expirationTime; // 0x80
                  AssetType assetType; // 0xa0
                  FeeRate makerFee; // 0xc0
                  uint256 salt; // 0xe0
              }
              /*
              Reference only; struct is composed manually using calldata formatting in execution
              struct ExecutionBatch { // Size: 0x80
                  address taker; // 0x00
                  OrderType orderType; // 0x20
                  Transfer[] transfers; // 0x40
                  uint256 length; // 0x60
              }
              */
              struct Transfer { // Size: 0xa0
                  address trader; // 0x00
                  uint256 id; // 0x20
                  uint256 amount; // 0x40
                  address collection; // 0x60
                  AssetType assetType; // 0x80
              }
              struct FungibleTransfers {
                  uint256 totalProtocolFee;
                  uint256 totalSellerTransfer;
                  uint256 totalTakerFee;
                  uint256 feeRecipientId;
                  uint256 makerId;
                  address[] feeRecipients;
                  address[] makers;
                  uint256[] makerTransfers;
                  uint256[] feeTransfers;
                  AtomicExecution[] executions;
              }
              struct AtomicExecution { // Size: 0xe0
                  uint256 makerId; // 0x00
                  uint256 sellerAmount; // 0x20
                  uint256 makerFeeRecipientId; // 0x40
                  uint256 makerFeeAmount; // 0x60
                  uint256 takerFeeAmount; // 0x80
                  uint256 protocolFeeAmount; // 0xa0
                  StateUpdate stateUpdate; // 0xc0
              }
              struct StateUpdate { // Size: 0xa0
                  address trader; // 0x00
                  bytes32 hash; // 0x20
                  uint256 index; // 0x40
                  uint256 value; // 0x60
                  uint256 maxAmount; // 0x80
              }
              struct Fees { // Size: 0x40
                  FeeRate protocolFee; // 0x00
                  FeeRate takerFee; // 0x20
              }
              struct FeeRate { // Size: 0x40
                  address recipient; // 0x00
                  uint16 rate; // 0x20
              }
              struct Cancel {
                  bytes32 hash;
                  uint256 index;
                  uint256 amount;
              }
              

              File 5 of 6: TieredDrop
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              ////////// CHANGELOG: turn `approve` to virtual //////////
              import "../../eip/interface/IERC721A.sol";
              import "../../eip/interface/IERC721Receiver.sol";
              import "../../lib/TWAddress.sol";
              import "../../openzeppelin-presets/utils/Context.sol";
              import "../../lib/TWStrings.sol";
              import "../../eip/ERC165.sol";
              import "../extension/Initializable.sol";
              library ERC721AStorage {
                  bytes32 public constant ERC721A_STORAGE_POSITION = keccak256("erc721.a.storage");
                  struct Data {
                      // The tokenId of the next token to be minted.
                      uint256 _currentIndex;
                      // The number of tokens burned.
                      uint256 _burnCounter;
                      // Token name
                      string _name;
                      // Token symbol
                      string _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 => IERC721A.TokenOwnership) _ownerships;
                      // Mapping owner address to address data
                      mapping(address => IERC721A.AddressData) _addressData;
                      // Mapping from token ID to approved address
                      mapping(uint256 => address) _tokenApprovals;
                      // Mapping from owner to operator approvals
                      mapping(address => mapping(address => bool)) _operatorApprovals;
                  }
                  function erc721AStorage() internal pure returns (Data storage erc721AData) {
                      bytes32 position = ERC721A_STORAGE_POSITION;
                      assembly {
                          erc721AData.slot := position
                      }
                  }
              }
              /**
               * @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, Context, ERC165, IERC721A {
                  using TWAddress for address;
                  using TWStrings for uint256;
                  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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._name = name_;
                      data._symbol = symbol_;
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      // Counter underflow is impossible as _burnCounter cannot be incremented
                      // more than _currentIndex - _startTokenId() times
                      unchecked {
                          return data._currentIndex - data._burnCounter - _startTokenId();
                      }
                  }
                  /**
                   * Returns the total amount of tokens minted in the contract.
                   */
                  function _totalMinted() internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      // Counter underflow is impossible as _currentIndex does not decrement,
                      // and it is initialized to _startTokenId()
                      unchecked {
                          return data._currentIndex - _startTokenId();
                      }
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
                      return
                          interfaceId == type(IERC721).interfaceId ||
                          interfaceId == type(IERC721Metadata).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view override returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (owner == address(0)) revert BalanceQueryForZeroAddress();
                      return uint256(data._addressData[owner].balance);
                  }
                  /**
                   * Returns the number of tokens minted by `owner`.
                   */
                  function _numberMinted(address owner) internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return uint256(data._addressData[owner].numberMinted);
                  }
                  /**
                   * Returns the number of tokens burned by or on behalf of `owner`.
                   */
                  function _numberBurned(address owner) internal view returns (uint256) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return uint256(data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < data._currentIndex) {
                                  TokenOwnership memory ownership = data._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 = data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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 virtual override returns (address) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return data._tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      if (operator == _msgSender()) revert ApproveToCaller();
                      data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return data._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) {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      return _startTokenId() <= tokenId && tokenId < data._currentIndex && !data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 startTokenId = data._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 {
                          data._addressData[to].balance += uint64(quantity);
                          data._addressData[to].numberMinted += uint64(quantity);
                          data._ownerships[startTokenId].addr = to;
                          data._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 (data._currentIndex != startTokenId) revert();
                          } else {
                              do {
                                  emit Transfer(address(0), to, updatedIndex++);
                              } while (updatedIndex < end);
                          }
                          data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      uint256 startTokenId = data._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 {
                          data._addressData[to].balance += uint64(quantity);
                          data._addressData[to].numberMinted += uint64(quantity);
                          data._ownerships[startTokenId].addr = to;
                          data._ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
                          uint256 updatedIndex = startTokenId;
                          uint256 end = updatedIndex + quantity;
                          do {
                              emit Transfer(address(0), to, updatedIndex++);
                          } while (updatedIndex < end);
                          data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      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 {
                          data._addressData[from].balance -= 1;
                          data._addressData[to].balance += 1;
                          TokenOwnership storage currSlot = data._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 = data._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 != data._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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      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 = data._addressData[from];
                          addressData.balance -= 1;
                          addressData.numberBurned += 1;
                          // Keep track of who burned the token, and the timestamp of burning.
                          TokenOwnership storage currSlot = data._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 = data._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 != data._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 {
                          data._burnCounter++;
                      }
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits a {Approval} event.
                   */
                  function _approve(
                      address to,
                      uint256 tokenId,
                      address owner
                  ) private {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._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 IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                          return retval == IERC721Receiver(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 {}
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IContractMetadata.sol";
              /**
               *  @author  thirdweb.com
               *
               *  @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.
               */
              library ContractMetadataStorage {
                  bytes32 public constant CONTRACT_METADATA_STORAGE_POSITION = keccak256("contract.metadata.storage");
                  struct Data {
                      /// @notice Returns the contract metadata URI.
                      string contractURI;
                  }
                  function contractMetadataStorage() internal pure returns (Data storage contractMetadataData) {
                      bytes32 position = CONTRACT_METADATA_STORAGE_POSITION;
                      assembly {
                          contractMetadataData.slot := position
                      }
                  }
              }
              abstract contract ContractMetadata is IContractMetadata {
                  /**
                   *  @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 {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      string memory prevURI = data.contractURI;
                      data.contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
                  /// @notice Returns the contract metadata URI.
                  function contractURI() public view virtual override returns (string memory) {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      return data.contractURI;
                  }
                  /// @dev Returns whether contract metadata can be set in the given execution context.
                  function _canSetContractURI() internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)
              pragma solidity ^0.8.0;
              import "../../openzeppelin-presets/utils/cryptography/ECDSA.sol";
              import "./Initializable.sol";
              library EIP712Storage {
                  bytes32 public constant EIP712_STORAGE_POSITION = keccak256("eip712.storage");
                  struct Data {
                      /* solhint-disable var-name-mixedcase */
                      bytes32 _HASHED_NAME;
                      bytes32 _HASHED_VERSION;
                      /* solhint-enable var-name-mixedcase */
                  }
                  function eip712Storage() internal pure returns (Data storage eip712Data) {
                      bytes32 position = EIP712_STORAGE_POSITION;
                      assembly {
                          eip712Data.slot := position
                      }
                  }
              }
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * _Available since v3.4._
               *
               * @custom:storage-size 52
               */
              abstract contract EIP712Upgradeable is Initializable {
                  /* solhint-disable var-name-mixedcase */
                  bytes32 private constant _TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  /* solhint-enable var-name-mixedcase */
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                      __EIP712_init_unchained(name, version);
                  }
                  function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                      bytes32 hashedName = keccak256(bytes(name));
                      bytes32 hashedVersion = keccak256(bytes(version));
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      data._HASHED_NAME = hashedName;
                      data._HASHED_VERSION = hashedVersion;
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                  }
                  function _buildDomainSeparator(
                      bytes32 typeHash,
                      bytes32 nameHash,
                      bytes32 versionHash
                  ) private view returns (bytes32) {
                      return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev The hash of the name parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712NameHash() internal view virtual returns (bytes32) {
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      return data._HASHED_NAME;
                  }
                  /**
                   * @dev The hash of the version parameter for the EIP712 domain.
                   *
                   * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                   * are a concern.
                   */
                  function _EIP712VersionHash() internal view virtual returns (bytes32) {
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      return data._HASHED_VERSION;
                  }
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "../../extension/interface/IERC2771Context.sol";
              import "./Initializable.sol";
              /**
               * @dev Context variant with ERC2771 support.
               */
              library ERC2771ContextStorage {
                  bytes32 public constant ERC2771_CONTEXT_STORAGE_POSITION = keccak256("erc2771.context.storage");
                  struct Data {
                      mapping(address => bool) trustedForwarder;
                  }
                  function erc2771ContextStorage() internal pure returns (Data storage erc2771ContextData) {
                      bytes32 position = ERC2771_CONTEXT_STORAGE_POSITION;
                      assembly {
                          erc2771ContextData.slot := position
                      }
                  }
              }
              /**
               * @dev Context variant with ERC2771 support.
               */
              abstract contract ERC2771ContextUpgradeable is Initializable {
                  function __ERC2771Context_init(address[] memory trustedForwarder) internal onlyInitializing {
                      __ERC2771Context_init_unchained(trustedForwarder);
                  }
                  function __ERC2771Context_init_unchained(address[] memory trustedForwarder) internal onlyInitializing {
                      ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage();
                      for (uint256 i = 0; i < trustedForwarder.length; i++) {
                          data.trustedForwarder[trustedForwarder[i]] = true;
                      }
                  }
                  function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
                      ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.erc2771ContextStorage();
                      return data.trustedForwarder[forwarder];
                  }
                  function _msgSender() internal view virtual 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 msg.sender;
                      }
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      if (isTrustedForwarder(msg.sender)) {
                          return msg.data[:msg.data.length - 20];
                      } else {
                          return msg.data;
                      }
                  }
                  uint256[49] private __gap;
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              import "../../lib/TWAddress.sol";
              library InitStorage {
                  /// @dev The location of the storage of the entrypoint contract's data.
                  bytes32 constant INIT_STORAGE_POSITION = keccak256("init.storage");
                  /// @dev Layout of the entrypoint contract's storage.
                  struct Data {
                      uint8 initialized;
                      bool initializing;
                  }
                  /// @dev Returns the entrypoint contract's data at the relevant storage location.
                  function initStorage() internal pure returns (Data storage initData) {
                      bytes32 position = INIT_STORAGE_POSITION;
                      assembly {
                          initData.slot := position
                      }
                  }
              }
              abstract contract Initializable {
                  /**
                   * @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() {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      bool isTopLevelCall = !_initializing;
                      require(
                          (isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1),
                          "Initializable: contract is already initialized"
                      );
                      data.initialized = 1;
                      if (isTopLevelCall) {
                          data.initializing = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          data.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) {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                      data.initialized = version;
                      data.initializing = true;
                      _;
                      data.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() {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      require(data.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 {
                      InitStorage.Data storage data = InitStorage.initStorage();
                      uint8 _initialized = data.initialized;
                      bool _initializing = data.initializing;
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized < type(uint8).max) {
                          data.initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IOperatorFilterToggle.sol";
              library OperatorFilterToggleStorage {
                  bytes32 public constant OPERATOR_FILTER_TOGGLE_STORAGE_POSITION = keccak256("operator.filter.toggle.storage");
                  struct Data {
                      bool operatorRestriction;
                  }
                  function operatorFilterToggleStorage() internal pure returns (Data storage operatorFilterToggleData) {
                      bytes32 position = OPERATOR_FILTER_TOGGLE_STORAGE_POSITION;
                      assembly {
                          operatorFilterToggleData.slot := position
                      }
                  }
              }
              abstract contract OperatorFilterToggle is IOperatorFilterToggle {
                  function operatorRestriction() external view override returns (bool) {
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      return data.operatorRestriction;
                  }
                  function setOperatorRestriction(bool _restriction) external {
                      require(_canSetOperatorRestriction(), "Not authorized to set operator restriction.");
                      _setOperatorRestriction(_restriction);
                  }
                  function _setOperatorRestriction(bool _restriction) internal {
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      data.operatorRestriction = _restriction;
                      emit OperatorRestriction(_restriction);
                  }
                  function _canSetOperatorRestriction() internal virtual returns (bool);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/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.
               */
              library OwnableStorage {
                  bytes32 public constant OWNABLE_STORAGE_POSITION = keccak256("ownable.storage");
                  struct Data {
                      /// @dev Owner of the contract (purpose: OpenSea compatibility)
                      address _owner;
                  }
                  function ownableStorage() internal pure returns (Data storage ownableData) {
                      bytes32 position = OWNABLE_STORAGE_POSITION;
                      assembly {
                          ownableData.slot := position
                      }
                  }
              }
              abstract contract Ownable is IOwnable {
                  /// @dev Reverts if caller is not the owner.
                  modifier onlyOwner() {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      if (msg.sender != data._owner) {
                          revert("Not authorized");
                      }
                      _;
                  }
                  /**
                   *  @notice Returns the owner of the contract.
                   */
                  function owner() public view override returns (address) {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      return data._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 {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      address _prevOwner = data._owner;
                      data._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;
              /// @author thirdweb
              import "../../extension/interface/IPermissions.sol";
              import "../../lib/TWStrings.sol";
              /**
               *  @title   Permissions
               *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
               */
              library PermissionsStorage {
                  bytes32 public constant PERMISSIONS_STORAGE_POSITION = keccak256("permissions.storage");
                  struct Data {
                      /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
                      mapping(bytes32 => mapping(address => bool)) _hasRole;
                      /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
                      mapping(bytes32 => bytes32) _getRoleAdmin;
                  }
                  function permissionsStorage() internal pure returns (Data storage permissionsData) {
                      bytes32 position = PERMISSIONS_STORAGE_POSITION;
                      assembly {
                          permissionsData.slot := position
                      }
                  }
              }
              contract Permissions is IPermissions {
                  /// @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, _msgSender());
                      _;
                  }
                  /**
                   *  @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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      if (!data._hasRole[role][address(0)]) {
                          return data._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) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(data._getRoleAdmin[role], _msgSender());
                      if (data._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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(data._getRoleAdmin[role], _msgSender());
                      _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 (_msgSender() != 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 {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      bytes32 previousAdminRole = data._getRoleAdmin[role];
                      data._getRoleAdmin[role] = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      data._hasRole[role][account] = true;
                      emit RoleGranted(role, account, _msgSender());
                  }
                  /// @dev Revokes `role` from `account`
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      _checkRole(role, account);
                      delete data._hasRole[role][account];
                      emit RoleRevoked(role, account, _msgSender());
                  }
                  /// @dev Checks `role` for `account`. Reverts with a message including the required role.
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      if (!data._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)
                                  )
                              )
                          );
                      }
                  }
                  function _msgSender() internal view virtual returns (address sender) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/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.
               */
              library PermissionsEnumerableStorage {
                  bytes32 public constant PERMISSIONS_ENUMERABLE_STORAGE_POSITION = keccak256("permissions.enumerable.storage");
                  /**
                   *  @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;
                  }
                  struct Data {
                      /// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
                      mapping(bytes32 => RoleMembers) roleMembers;
                  }
                  function permissionsEnumerableStorage() internal pure returns (Data storage permissionsEnumerableData) {
                      bytes32 position = PERMISSIONS_ENUMERABLE_STORAGE_POSITION;
                      assembly {
                          permissionsEnumerableData.slot := position
                      }
                  }
              }
              contract PermissionsEnumerable is IPermissionsEnumerable, Permissions {
                  /**
                   *  @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) {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 currentIndex = data.roleMembers[role].index;
                      uint256 check;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (data.roleMembers[role].members[i] != address(0)) {
                              if (check == index) {
                                  member = data.roleMembers[role].members[i];
                                  return member;
                              }
                              check += 1;
                          } else if (hasRole(role, address(0)) && i == data.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) {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 currentIndex = data.roleMembers[role].index;
                      for (uint256 i = 0; i < currentIndex; i += 1) {
                          if (data.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 {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].index;
                      data.roleMembers[role].index += 1;
                      data.roleMembers[role].members[idx] = account;
                      data.roleMembers[role].indexOf[account] = idx;
                  }
                  /// @dev removes `account` from {roleMembers}, for `role`
                  function _removeMember(bytes32 role, address account) internal {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].indexOf[account];
                      delete data.roleMembers[role].members[idx];
                      delete data.roleMembers[role].indexOf[account];
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../../extension/interface/IPrimarySale.sol";
              library PrimarySaleStorage {
                  bytes32 public constant PRIMARY_SALE_STORAGE_POSITION = keccak256("primary.sale.storage");
                  struct Data {
                      address recipient;
                  }
                  function primarySaleStorage() internal pure returns (Data storage primarySaleData) {
                      bytes32 position = PRIMARY_SALE_STORAGE_POSITION;
                      assembly {
                          primarySaleData.slot := position
                      }
                  }
              }
              /**
               *  @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 Returns primary sale recipient address.
                  function primarySaleRecipient() public view override returns (address) {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      return data.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 {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      data.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;
              /// @author thirdweb
              import "../../extension/interface/IRoyalty.sol";
              library RoyaltyStorage {
                  bytes32 public constant ROYALTY_STORAGE_POSITION = keccak256("royalty.storage");
                  struct Data {
                      /// @dev The (default) address that receives all royalty value.
                      address royaltyRecipient;
                      /// @dev The (default) % of a sale to take as royalty (in basis points).
                      uint16 royaltyBps;
                      /// @dev Token ID => royalty recipient and bps for token
                      mapping(uint256 => IRoyalty.RoyaltyInfo) royaltyInfoForToken;
                  }
                  function royaltyStorage() internal pure returns (Data storage royaltyData) {
                      bytes32 position = ROYALTY_STORAGE_POSITION;
                      assembly {
                          royaltyData.slot := position
                      }
                  }
              }
              /**
               *  @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 {
                  /**
                   *  @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) {
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      RoyaltyInfo memory royaltyForToken = data.royaltyInfoForToken[_tokenId];
                      return
                          royaltyForToken.recipient == address(0)
                              ? (data.royaltyRecipient, uint16(data.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) {
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      return (data.royaltyRecipient, uint16(data.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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.royaltyRecipient = _royaltyRecipient;
                      data.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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.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;
              /// @author thirdweb
              import "../../extension/interface/ISignatureAction.sol";
              import "./EIP712Upgradeable.sol";
              library SignatureActionStorage {
                  bytes32 public constant SIGNATURE_ACTION_STORAGE_POSITION = keccak256("signature.action.storage");
                  struct Data {
                      /// @dev Mapping from a signed request UID => whether the request is processed.
                      mapping(bytes32 => bool) executed;
                  }
                  function signatureActionStorage() internal pure returns (Data storage signatureActionData) {
                      bytes32 position = SIGNATURE_ACTION_STORAGE_POSITION;
                      assembly {
                          signatureActionData.slot := position
                      }
                  }
              }
              abstract contract SignatureActionUpgradeable is EIP712Upgradeable, ISignatureAction {
                  using ECDSA for bytes32;
                  bytes32 private constant TYPEHASH =
                      keccak256("GenericRequest(uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid,bytes data)");
                  function __SignatureAction_init() internal onlyInitializing {
                      __EIP712_init("SignatureAction", "1");
                  }
                  function __SignatureAction_init_unchained() internal onlyInitializing {}
                  /// @dev Verifies that a request is signed by an authorized account.
                  function verify(GenericRequest calldata _req, bytes calldata _signature)
                      public
                      view
                      override
                      returns (bool success, address signer)
                  {
                      SignatureActionStorage.Data storage data = SignatureActionStorage.signatureActionStorage();
                      signer = _recoverAddress(_req, _signature);
                      success = !data.executed[_req.uid] && _isAuthorizedSigner(signer);
                  }
                  /// @dev Returns whether a given address is authorized to sign requests.
                  function _isAuthorizedSigner(address _signer) internal view virtual returns (bool);
                  /// @dev Verifies a request and marks the request as processed.
                  function _processRequest(GenericRequest calldata _req, bytes calldata _signature)
                      internal
                      returns (address signer)
                  {
                      bool success;
                      (success, signer) = verify(_req, _signature);
                      if (!success) {
                          revert("Invalid req");
                      }
                      if (_req.validityStartTimestamp > block.timestamp || block.timestamp > _req.validityEndTimestamp) {
                          revert("Req expired");
                      }
                      SignatureActionStorage.Data storage data = SignatureActionStorage.signatureActionStorage();
                      data.executed[_req.uid] = true;
                  }
                  /// @dev Returns the address of the signer of the request.
                  function _recoverAddress(GenericRequest calldata _req, bytes calldata _signature) internal view returns (address) {
                      return _hashTypedDataV4(keccak256(_encodeRequest(_req))).recover(_signature);
                  }
                  /// @dev Encodes a request for recovery of the signer in `recoverAddress`.
                  function _encodeRequest(GenericRequest calldata _req) internal pure returns (bytes memory) {
                      return
                          abi.encode(
                              TYPEHASH,
                              _req.validityStartTimestamp,
                              _req.validityEndTimestamp,
                              _req.uid,
                              keccak256(_req.data)
                          );
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { ContractMetadataStorage } from "../extension/ContractMetadata.sol";
              contract ContractMetadataInit {
                  event ContractURIUpdated(string prevURI, string newURI);
                  /// @dev Lets a contract admin set the URI for contract-level metadata.
                  function _setupContractURI(string memory _uri) internal {
                      ContractMetadataStorage.Data storage data = ContractMetadataStorage.contractMetadataStorage();
                      string memory prevURI = data.contractURI;
                      data.contractURI = _uri;
                      emit ContractURIUpdated(prevURI, _uri);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import "../../extension/interface/IOperatorFilterRegistry.sol";
              import { OperatorFilterToggleStorage } from "../extension/OperatorFilterToggle.sol";
              contract DefaultOperatorFiltererInit {
                  event OperatorRestriction(bool restriction);
                  IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                  address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);
                  function _setupOperatorFilterer() internal {
                      __DefaultOperatorFilterer_init();
                  }
                  function __DefaultOperatorFilterer_init() private {
                      __OperatorFilterer_init(DEFAULT_SUBSCRIPTION, true);
                      OperatorFilterToggleStorage.Data storage data = OperatorFilterToggleStorage.operatorFilterToggleStorage();
                      data.operatorRestriction = true;
                      emit OperatorRestriction(true);
                  }
                  function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe) private {
                      // 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));
                                  }
                              }
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { ERC721AStorage } from "../eip/ERC721AUpgradeable.sol";
              import "../extension/Initializable.sol";
              contract ERC721AInit is Initializable {
                  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 {
                      ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage();
                      data._name = name_;
                      data._symbol = symbol_;
                      data._currentIndex = _startTokenId();
                  }
                  function _startTokenId() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { OwnableStorage } from "../extension/Ownable.sol";
              contract OwnableInit {
                  event OwnerUpdated(address indexed prevOwner, address indexed 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 {
                      OwnableStorage.Data storage data = OwnableStorage.ownableStorage();
                      address _prevOwner = data._owner;
                      data._owner = _newOwner;
                      emit OwnerUpdated(_prevOwner, _newOwner);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PermissionsEnumerableStorage } from "../extension/PermissionsEnumerable.sol";
              import "./PermissionsInit.sol";
              contract PermissionsEnumerableInit is PermissionsInit {
                  /// @dev Sets up `role` for `account`
                  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 {
                      PermissionsEnumerableStorage.Data storage data = PermissionsEnumerableStorage.permissionsEnumerableStorage();
                      uint256 idx = data.roleMembers[role].index;
                      data.roleMembers[role].index += 1;
                      data.roleMembers[role].members[idx] = account;
                      data.roleMembers[role].indexOf[account] = idx;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PermissionsStorage } from "../extension/Permissions.sol";
              contract PermissionsInit {
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /// @dev Sets up `role` for `account`
                  function _setupRole(bytes32 role, address account) internal virtual {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      data._hasRole[role][account] = true;
                      emit RoleGranted(role, account, msg.sender);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { PrimarySaleStorage } from "../extension/PrimarySale.sol";
              contract PrimarySaleInit {
                  /// @dev Emitted when a new sale recipient is set.
                  event PrimarySaleRecipientUpdated(address indexed recipient);
                  /// @dev Lets a contract admin set the recipient for all primary sales.
                  function _setupPrimarySaleRecipient(address _saleRecipient) internal {
                      PrimarySaleStorage.Data storage data = PrimarySaleStorage.primarySaleStorage();
                      data.recipient = _saleRecipient;
                      emit PrimarySaleRecipientUpdated(_saleRecipient);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { RoyaltyStorage, IRoyalty } from "../extension/Royalty.sol";
              contract RoyaltyInit {
                  event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
                  /// @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");
                      }
                      RoyaltyStorage.Data storage data = RoyaltyStorage.royaltyStorage();
                      data.royaltyRecipient = _royaltyRecipient;
                      data.royaltyBps = uint16(_royaltyBps);
                      emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              import { SignatureActionStorage } from "../extension/SignatureActionUpgradeable.sol";
              import { EIP712Storage } from "../extension/EIP712Upgradeable.sol";
              import "../extension/Initializable.sol";
              contract EIP712Init is Initializable {
                  function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                      __EIP712_init_unchained(name, version);
                  }
                  function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                      bytes32 hashedName = keccak256(bytes(name));
                      bytes32 hashedVersion = keccak256(bytes(version));
                      EIP712Storage.Data storage data = EIP712Storage.eip712Storage();
                      data._HASHED_NAME = hashedName;
                      data._HASHED_VERSION = hashedVersion;
                  }
              }
              contract SignatureActionInit is EIP712Init {
                  function __SignatureAction_init() internal onlyInitializing {
                      __EIP712_init("SignatureAction", "1");
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./interface/IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts 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;
              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: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 {
                  /**
                   * @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);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address);
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes calldata data
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v3.3.0
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              import "./IERC721.sol";
              import "./IERC721Metadata.sol";
              /**
               * @dev Interface of an ERC721A compliant contract.
               */
              interface IERC721A is IERC721, IERC721Metadata {
                  /**
                   * 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);
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
              /// @dev See https://eips.ethereum.org/EIPS/eip-721
              ///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
              /* is ERC721 */
              interface IERC721Metadata {
                  /// @notice A descriptive name for a collection of NFTs in this contract
                  function name() external view returns (string memory);
                  /// @notice An abbreviated name for NFTs in this contract
                  function symbol() external view returns (string memory);
                  /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
                  /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
                  ///  3986. The URI may point to a JSON file that conforms to the "ERC721
                  ///  Metadata JSON Schema".
                  function tokenURI(uint256 _tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
              pragma solidity ^0.8.0;
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              interface IERC721Receiver {
                  /**
                   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                   * by `operator` from `from`, this function is called.
                   *
                   * It must return its Solidity selector to confirm the token transfer.
                   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                   *
                   * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              import "../lib/TWAddress.sol";
              import "./interface/IMulticall.sol";
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              contract Multicall is IMulticall {
                  /**
                   *  @notice Receives and executes a batch of function calls on this contract.
                   *  @dev Receives and executes a batch of function calls on this contract.
                   *
                   *  @param data The bytes data that makes up the batch of function calls to execute.
                   *  @return results The bytes data that makes up the result of the batch of function calls executed.
                   */
                  function multicall(bytes[] calldata data) external virtual override returns (bytes[] memory results) {
                      results = new bytes[](data.length);
                      for (uint256 i = 0; i < data.length; i++) {
                          results[i] = TWAddress.functionDelegateCall(address(this), data[i]);
                      }
                      return results;
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               *  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;
              interface IERC2771Context {
                  function isTrustedForwarder(address forwarder) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @dev Provides a function to batch together multiple calls in a single external call.
               *
               * _Available since v4.1._
               */
              interface IMulticall {
                  /**
                   * @dev Receives and executes a batch of function calls on this contract.
                   */
                  function multicall(bytes[] calldata data) external returns (bytes[] memory results);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  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;
              /// @author thirdweb
              /**
               * @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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  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;
              /// @author thirdweb
              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;
              /// @author thirdweb
              /**
               *  thirdweb's `SignatureAction` extension smart contract can be used with any base smart contract. It provides a generic
               *  payload struct that can be signed by an authorized wallet and verified by the contract. The bytes `data` field provided
               *  in the payload can be abi encoded <-> decoded to use `SignatureContract` for any authorized signature action.
               */
              interface ISignatureAction {
                  /**
                   *  @notice The payload that must be signed by an authorized wallet.
                   *
                   *  @param validityStartTimestamp The UNIX timestamp at and after which a signature is valid.
                   *  @param validityEndTimestamp The UNIX timestamp at and after which a signature is invalid/expired.
                   *  @param uid A unique non-repeatable ID for the payload.
                   *  @param data Arbitrary bytes data to be used at the discretion of the contract.
                   */
                  struct GenericRequest {
                      uint128 validityStartTimestamp;
                      uint128 validityEndTimestamp;
                      bytes32 uid;
                      bytes data;
                  }
                  /// @notice Emitted when a payload is verified and executed.
                  event RequestExecuted(address indexed user, address indexed signer, GenericRequest _req);
                  /**
                   *  @notice Verfies that a payload is signed by an authorized wallet.
                   *
                   *  @param req The payload signed by the authorized wallet.
                   *  @param signature The signature produced by the authorized wallet signing the given payload.
                   *
                   *  @return success Whether the payload is signed by the authorized wallet.
                   *  @return signer The address of the signer.
                   */
                  function verify(GenericRequest calldata req, bytes calldata signature)
                      external
                      view
                      returns (bool success, address signer);
              }
              // SPDX-License-Identifier: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @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: Apache 2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              /**
               * @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.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../../../lib/TWStrings.sol";
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      } else if (error == RecoverError.InvalidSignatureV) {
                          revert("ECDSA: invalid signature 'v' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      // Check the signature length
                      // - case 65: r,s,v signature (standard)
                      // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else if (signature.length == 64) {
                          bytes32 r;
                          bytes32 vs;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          assembly {
                              r := mload(add(signature, 0x20))
                              vs := mload(add(signature, 0x40))
                          }
                          return tryRecover(hash, r, vs);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(
                      bytes32 hash,
                      bytes32 r,
                      bytes32 vs
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address, RecoverError) {
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      if (v != 27 && v != 28) {
                          return (address(0), RecoverError.InvalidSignatureV);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(
                      bytes32 hash,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", hash));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", TWStrings.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                  }
              }
              // SPDX-License-Identifier: Apache-2.0
              pragma solidity ^0.8.0;
              /// @author thirdweb
              //   $$\\     $$\\       $$\\                 $$\\                         $$\\
              //   $$ |    $$ |      \\__|                $$ |                        $$ |
              // $$$$$$\\   $$$$$$$\\  $$\\  $$$$$$\\   $$$$$$$ |$$\\  $$\\  $$\\  $$$$$$\\  $$$$$$$\\
              // \\_$$  _|  $$  __$$\\ $$ |$$  __$$\\ $$  __$$ |$$ | $$ | $$ |$$  __$$\\ $$  __$$\\
              //   $$ |    $$ |  $$ |$$ |$$ |  \\__|$$ /  $$ |$$ | $$ | $$ |$$$$$$$$ |$$ |  $$ |
              //   $$ |$$\\ $$ |  $$ |$$ |$$ |      $$ |  $$ |$$ | $$ | $$ |$$   ____|$$ |  $$ |
              //   \\$$$$  |$$ |  $$ |$$ |$$ |      \\$$$$$$$ |\\$$$$$\\$$$$  |\\$$$$$$$\\ $$$$$$$  |
              //    \\____/ \\__|  \\__|\\__|\\__|       \\_______| \\_____\\____/  \\_______|\\_______/
              import "lib/dynamic-contracts/src/presets/BaseRouter.sol";
              import "../extension/Multicall.sol";
              import "../dynamic-contracts/extension/Initializable.sol";
              import "../dynamic-contracts/extension/Permissions.sol";
              import "../dynamic-contracts/extension/ERC2771ContextUpgradeable.sol";
              import "../dynamic-contracts/init/ContractMetadataInit.sol";
              import "../dynamic-contracts/init/ERC721AInit.sol";
              import "../dynamic-contracts/init/OwnableInit.sol";
              import "../dynamic-contracts/init/PermissionsEnumerableInit.sol";
              import "../dynamic-contracts/init/PrimarySaleInit.sol";
              import "../dynamic-contracts/init/RoyaltyInit.sol";
              import "../dynamic-contracts/init/SignatureActionInit.sol";
              import "../dynamic-contracts/init/DefaultOperatorFiltererInit.sol";
              /**
               *  Defualt extensions to add:
               *      - TieredDropLogic
               *      - PermissionsEnumerable
               */
              contract TieredDrop is
                  Initializable,
                  Multicall,
                  ERC2771ContextUpgradeable,
                  BaseRouter,
                  DefaultOperatorFiltererInit,
                  PrimarySaleInit,
                  ContractMetadataInit,
                  ERC721AInit,
                  OwnableInit,
                  PermissionsEnumerableInit,
                  RoyaltyInit,
                  SignatureActionInit
              {
                  /*///////////////////////////////////////////////////////////////
                                  Constructor and Initializer logic
                  //////////////////////////////////////////////////////////////*/
                  constructor(Extension[] memory _extensions) BaseRouter(_extensions) {}
                  /// @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,
                      uint16 _royaltyBps
                  ) external initializer {
                      // Initialize inherited contracts, most base-like -> most derived.
                      __ERC2771Context_init(_trustedForwarders);
                      __ERC721A_init(_name, _symbol);
                      __SignatureAction_init();
                      _setupContractURI(_contractURI);
                      _setupOwner(_defaultAdmin);
                      _setupRoles(_defaultAdmin);
                      _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
                      _setupPrimarySaleRecipient(_saleRecipient);
                      _setupOperatorFilterer();
                  }
                  function _setupRoles(address _defaultAdmin) internal onlyInitializing {
                      bytes32 _operatorRole = keccak256("OPERATOR_ROLE");
                      bytes32 _transferRole = keccak256("TRANSFER_ROLE");
                      bytes32 _minterRole = keccak256("MINTER_ROLE");
                      bytes32 _defaultAdminRole = 0x00;
                      _setupRole(_defaultAdminRole, _defaultAdmin);
                      _setupRole(_minterRole, _defaultAdmin);
                      _setupRole(_transferRole, _defaultAdmin);
                      _setupRole(_transferRole, address(0));
                      _setupRole(_operatorRole, _defaultAdmin);
                      _setupRole(_operatorRole, address(0));
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns whether a plugin can be set in the given execution context.
                  function _canSetExtension() internal view virtual override returns (bool) {
                      bytes32 defaultAdminRole = 0x00;
                      return _hasRole(defaultAdminRole, _msgSender());
                  }
                  /// @dev Checks whether an account holds the given role.
                  function _hasRole(bytes32 role, address addr) internal view returns (bool) {
                      PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage();
                      return data._hasRole[role][addr];
                  }
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "../interface/IRouter.sol";
              abstract contract Router is IRouter {
                  fallback() external payable virtual {
                  /// @dev delegate calls the appropriate implementation smart contract for a given function.
                      address implementation = getImplementationForFunction(msg.sig);
                      _delegate(implementation);
                  }
                  receive() external payable virtual {}
                  /// @dev delegateCalls an `implementation` smart contract.
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /// @dev Unimplemented. Returns the implementation contract address for a given function signature.
                  function getImplementationForFunction(bytes4 _functionSelector) public view virtual returns (address);
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "./IDefaultExtensionSet.sol";
              interface IBaseRouter is IDefaultExtensionSet {
                  /*///////////////////////////////////////////////////////////////
                                      External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Adds a new extension to the router.
                  function addExtension(Extension memory extension) external;
                  /// @dev Updates an existing extension in the router, or overrides a default extension.
                  function updateExtension(Extension memory extension) external;
                  /// @dev Removes an existing extension from the router.
                  function removeExtension(string memory extensionName) external;
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              import "./IExtension.sol";
              interface IDefaultExtensionSet is IExtension {
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns all extensions stored.
                  function getAllExtensions() external view returns (Extension[] memory);
                  /// @dev Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory extensionName) external view returns (ExtensionFunction[] memory);
                  /// @dev Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 functionSelector) external view returns (ExtensionMetadata memory);
                  /// @dev Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory extensionName) external view returns (address);
                  /// @dev Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory extensionName) external view returns (Extension memory);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              interface IExtension {
                  /*///////////////////////////////////////////////////////////////
                                              Structs
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @notice A extension's metadata.
                   *
                   *  @param name             The unique name of the extension.
                   *  @param metadataURI      The URI where the metadata for the extension lives.
                   *  @param implementation   The implementation smart contract address of the extension.
                   */
                  struct ExtensionMetadata {
                      string name;
                      string metadataURI;
                      address implementation;
                  }
                  /**
                   *  @notice An interface to describe a extension's function.
                   *
                   *  @param functionSelector    The 4 byte selector of the function.
                   *  @param functionSignature   Function signature as a string. E.g. "transfer(address,address,uint256)"
                   */
                  struct ExtensionFunction {
                      bytes4 functionSelector;
                      string functionSignature;
                  }
                  /**
                   *  @notice An interface to describe an extension.
                   *
                   *  @param metadata     The extension's metadata; it's name, metadata URI and implementation contract address.
                   *  @param functions    The functions that belong to the extension.
                   */
                  struct Extension {
                      ExtensionMetadata metadata;
                      ExtensionFunction[] functions;
                  }
                  /*///////////////////////////////////////////////////////////////
                                              Events
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Emitted when a extension is added; emitted for each function of the extension.
                  event ExtensionAdded(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
                  /// @dev Emitted when extension is updated; emitted for each function of the extension.
                  event ExtensionUpdated(
                      address indexed oldExtensionAddress,
                      address indexed newExtensionAddress,
                      bytes4 indexed functionSelector,
                      string functionSignature
                  );
                  /// @dev Emitted when a extension is removed; emitted for each function of the extension.
                  event ExtensionRemoved(address indexed extensionAddress, bytes4 indexed functionSelector, string functionSignature);
              }
              // SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              interface IRouter {
                  fallback() external payable;
                  receive() external payable;
                  function getImplementationForFunction(bytes4 _functionSelector) external view returns (address);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../interface/IBaseRouter.sol";
              // Core
              import "../core/Router.sol";
              // Utils
              import "./utils/StringSet.sol";
              import "./utils/DefaultExtensionSet.sol";
              import "./utils/ExtensionState.sol";
              abstract contract BaseRouter is IBaseRouter, Router, ExtensionState {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @notice The DefaultExtensionSet that stores default extensions of the router.
                  address public immutable defaultExtensionSet;
                  /*///////////////////////////////////////////////////////////////
                                              Constructor
                  //////////////////////////////////////////////////////////////*/
                  constructor(Extension[] memory _extensions) {
                      DefaultExtensionSet map = new DefaultExtensionSet();
                      defaultExtensionSet = address(map);
                      uint256 len = _extensions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          map.setExtension(_extensions[i]);
                      }
                  }
                  /*///////////////////////////////////////////////////////////////
                                      External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Adds a new extension to the router.
                  function addExtension(Extension memory _extension) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _addExtension(_extension);
                  }
                  /// @dev Updates an existing extension in the router, or overrides a default extension.
                  function updateExtension(Extension memory _extension) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _updateExtension(_extension);
                  }
                  /// @dev Removes an existing extension from the router.
                  function removeExtension(string memory _extensionName) external {
                      require(_canSetExtension(), "BaseRouter: caller not authorized.");
                      _removeExtension(_extensionName);
                  }
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /**
                   *  @notice Returns all extensions stored. Override default lugins stored in router are
                   *          given precedence over default extensions in DefaultExtensionSet.
                   */
                  function getAllExtensions() external view returns (Extension[] memory allExtensions) {
                      Extension[] memory defaultExtensions = IDefaultExtensionSet(defaultExtensionSet).getAllExtensions();
                      uint256 defaultExtensionsLen = defaultExtensions.length;
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string[] memory names = data.extensionNames.values();
                      uint256 namesLen = names.length;
                      uint256 overrides = 0;
                      for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
                          if (data.extensionNames.contains(defaultExtensions[i].metadata.name)) {
                              overrides += 1;
                          }
                      }
                      uint256 total = (namesLen + defaultExtensionsLen) - overrides;
                      allExtensions = new Extension[](total);
                      uint256 idx = 0;
                      for (uint256 i = 0; i < defaultExtensionsLen; i += 1) {
                          string memory name = defaultExtensions[i].metadata.name;
                          if (!data.extensionNames.contains(name)) {
                              allExtensions[idx] = defaultExtensions[i];
                              idx += 1;
                          }
                      }
                      for (uint256 i = 0; i < namesLen; i += 1) {
                          allExtensions[idx] = data.extensions[names[i]];
                          idx += 1;
                      }
                  }
                  /// @dev Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory _extensionName) public view returns (Extension memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      bool isLocalExtension = data.extensionNames.contains(_extensionName);
                      return isLocalExtension ? data.extensions[_extensionName] : IDefaultExtensionSet(defaultExtensionSet).getExtension(_extensionName);
                  }
                  /// @dev Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory _extensionName) external view returns (address) {
                      return getExtension(_extensionName).metadata.implementation;
                  }
                  /// @dev Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
                      return getExtension(_extensionName).functions;
                  }
                  /// @dev Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 _functionSelector) public view returns (ExtensionMetadata memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
                      bool isLocalExtension = metadata.implementation != address(0);
                      return isLocalExtension ? metadata : IDefaultExtensionSet(defaultExtensionSet).getExtensionForFunction(_functionSelector);
                  }
                  /// @dev Returns the extension implementation address stored in router, for the given function.
                  function getImplementationForFunction(bytes4 _functionSelector)
                      public
                      view
                      override
                      returns (address extensionAddress)
                  {
                      return getExtensionForFunction(_functionSelector).implementation;
                  }
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Returns whether a extension can be set in the given execution context.
                  function _canSetExtension() internal view virtual returns (bool);
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../../interface/IDefaultExtensionSet.sol";
              // Extensions
              import "./ExtensionState.sol";
              contract DefaultExtensionSet is IDefaultExtensionSet, ExtensionState {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                          State variables
                  //////////////////////////////////////////////////////////////*/
                  /// @notice The deployer of DefaultExtensionSet.
                  address private deployer;
                  /*///////////////////////////////////////////////////////////////
                                          Constructor
                  //////////////////////////////////////////////////////////////*/
                  constructor() {
                      deployer = msg.sender;
                  }
                  /*///////////////////////////////////////////////////////////////
                                          External functions
                  //////////////////////////////////////////////////////////////*/
                  /// @notice Stores a extension in the DefaultExtensionSet.
                  function setExtension(Extension memory _extension) external {
                      require(msg.sender == deployer, "DefaultExtensionSet: unauthorized caller.");
                      _addExtension(_extension);
                  }
                  /*///////////////////////////////////////////////////////////////
                                          View functions
                  //////////////////////////////////////////////////////////////*/
                  /// @notice Returns all extensions stored.
                  function getAllExtensions() external view returns (Extension[] memory allExtensions) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string[] memory names = data.extensionNames.values();
                      uint256 len = names.length;
                      allExtensions = new Extension[](len);
                      for (uint256 i = 0; i < len; i += 1) {
                          allExtensions[i] = data.extensions[names[i]];
                      }
                  }
                  /// @notice Returns the extension metadata and functions for a given extension.
                  function getExtension(string memory _extensionName) public view returns (Extension memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      require(data.extensionNames.contains(_extensionName), "DefaultExtensionSet: extension does not exist.");
                      return data.extensions[_extensionName];
                  }
                  /// @notice Returns the extension's implementation smart contract address.
                  function getExtensionImplementation(string memory _extensionName) external view returns (address) {
                      return getExtension(_extensionName).metadata.implementation;
                  }
                  /// @notice Returns all functions that belong to the given extension contract.
                  function getAllFunctionsOfExtension(string memory _extensionName) external view returns (ExtensionFunction[] memory) {
                      return getExtension(_extensionName).functions;
                  }
                  /// @notice Returns the extension metadata for a given function.
                  function getExtensionForFunction(bytes4 _functionSelector) external view returns (ExtensionMetadata memory) {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      ExtensionMetadata memory metadata = data.extensionMetadata[_functionSelector];
                      require(metadata.implementation != address(0), "DefaultExtensionSet: no extension for function.");
                      return metadata;
                  }
              }// SPDX-License-Identifier: MIT
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              // Interface
              import "../../interface/IExtension.sol";
              // Extensions
              import "./StringSet.sol";
              library ExtensionStateStorage {
                  bytes32 public constant EXTENSION_STATE_STORAGE_POSITION = keccak256("extension.state.storage");
                  struct Data {
                      /// @dev Set of names of all extensions stored.
                      StringSet.Set extensionNames;
                      /// @dev Mapping from extension name => `Extension` i.e. extension metadata and functions.
                      mapping(string => IExtension.Extension) extensions;
                      /// @dev Mapping from function selector => extension metadata of the extension the function belongs to.
                      mapping(bytes4 => IExtension.ExtensionMetadata) extensionMetadata;
                  }
                  function extensionStateStorage() internal pure returns (Data storage extensionStateData) {
                      bytes32 position = EXTENSION_STATE_STORAGE_POSITION;
                      assembly {
                          extensionStateData.slot := position
                      }
                  }
              }
              contract ExtensionState is IExtension {
                  using StringSet for StringSet.Set;
                  /*///////////////////////////////////////////////////////////////
                                      Internal functions
                  //////////////////////////////////////////////////////////////*/
                  /// @dev Stores a new extension in the contract.
                  function _addExtension(Extension memory _extension) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string memory name = _extension.metadata.name;
                      require(data.extensionNames.add(name), "ExtensionState: extension already exists.");
                      data.extensions[name].metadata = _extension.metadata;
                      require(_extension.metadata.implementation != address(0), "ExtensionState: adding extension without implementation.");
                      uint256 len = _extension.functions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          require(
                              _extension.functions[i].functionSelector ==
                                  bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
                              "ExtensionState: fn selector and signature mismatch."
                          );
                          require(
                              data.extensionMetadata[_extension.functions[i].functionSelector].implementation == address(0),
                              "ExtensionState: extension already exists for function."
                          );
                          data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
                          data.extensions[name].functions.push(_extension.functions[i]);
                          emit ExtensionAdded(
                              _extension.metadata.implementation,
                              _extension.functions[i].functionSelector,
                              _extension.functions[i].functionSignature
                          );
                      }
                  }
                  /// @dev Updates / overrides an existing extension in the contract.
                  function _updateExtension(Extension memory _extension) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      string memory name = _extension.metadata.name;
                      require(data.extensionNames.contains(name), "ExtensionState: extension does not exist.");
                      address oldImplementation = data.extensions[name].metadata.implementation;
                      require(_extension.metadata.implementation != oldImplementation, "ExtensionState: re-adding same extension.");
                      data.extensions[name].metadata = _extension.metadata;
                      ExtensionFunction[] memory oldFunctions = data.extensions[name].functions;
                      uint256 oldFunctionsLen = oldFunctions.length;
                      delete data.extensions[name].functions;
                      for (uint256 i = 0; i < oldFunctionsLen; i += 1) {
                          delete data.extensionMetadata[oldFunctions[i].functionSelector];
                      }
                      uint256 len = _extension.functions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          require(
                              _extension.functions[i].functionSelector ==
                                  bytes4(keccak256(abi.encodePacked(_extension.functions[i].functionSignature))),
                              "ExtensionState: fn selector and signature mismatch."
                          );
                          data.extensionMetadata[_extension.functions[i].functionSelector] = _extension.metadata;
                          data.extensions[name].functions.push(_extension.functions[i]);
                          emit ExtensionUpdated(
                              oldImplementation,
                              _extension.metadata.implementation,
                              _extension.functions[i].functionSelector,
                              _extension.functions[i].functionSignature
                          );
                      }
                  }
                  /// @dev Removes an existing extension from the contract.
                  function _removeExtension(string memory _extensionName) internal {
                      ExtensionStateStorage.Data storage data = ExtensionStateStorage.extensionStateStorage();
                      require(data.extensionNames.remove(_extensionName), "ExtensionState: extension does not exist.");
                      address implementation = data.extensions[_extensionName].metadata.implementation;
                      ExtensionFunction[] memory extensionFunctions = data.extensions[_extensionName].functions;
                      delete data.extensions[_extensionName];
                      uint256 len = extensionFunctions.length;
                      for (uint256 i = 0; i < len; i += 1) {
                          emit ExtensionRemoved(
                              implementation,
                              extensionFunctions[i].functionSelector,
                              extensionFunctions[i].functionSignature
                          );
                          delete data.extensionMetadata[extensionFunctions[i].functionSelector];
                      }
                  }
              }// SPDX-License-Identifier: Apache 2.0
              // @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts)
              pragma solidity ^0.8.0;
              library StringSet {
                  struct Set {
                      // Storage of set values
                      string[] _values;
                      // Position of the value in the `values` array, plus 1 because index 0
                      // means a value is not in the set.
                      mapping(string => uint256) _indexes;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function _add(Set storage set, string memory value) private returns (bool) {
                      if (!_contains(set, value)) {
                          set._values.push(value);
                          // The value is stored at length-1, but we add 1 to all indexes
                          // and use 0 as a sentinel value
                          set._indexes[value] = set._values.length;
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function _remove(Set storage set, string memory value) private returns (bool) {
                      // We read and store the value's index to prevent multiple reads from the same storage slot
                      uint256 valueIndex = set._indexes[value];
                      if (valueIndex != 0) {
                          // Equivalent to contains(set, value)
                          // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                          // the array, and then remove the last element (sometimes called as 'swap and pop').
                          // This modifies the order of the array, as noted in {at}.
                          uint256 toDeleteIndex = valueIndex - 1;
                          uint256 lastIndex = set._values.length - 1;
                          if (lastIndex != toDeleteIndex) {
                              string memory lastValue = set._values[lastIndex];
                              // Move the last value to the index where the value to delete is
                              set._values[toDeleteIndex] = lastValue;
                              // Update the index for the moved value
                              set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                          }
                          // Delete the slot where the moved value was stored
                          set._values.pop();
                          // Delete the index for the deleted slot
                          delete set._indexes[value];
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function _contains(Set storage set, string memory value) private view returns (bool) {
                      return set._indexes[value] != 0;
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function _length(Set storage set) private view returns (uint256) {
                      return set._values.length;
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function _at(Set storage set, uint256 index) private view returns (string memory) {
                      return set._values[index];
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function _values(Set storage set) private view returns (string[] memory) {
                      return set._values;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(Set storage set, string memory value) internal returns (bool) {
                      return _add(set, value);
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(Set storage set, string memory value) internal returns (bool) {
                      return _remove(set, value);
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(Set storage set, string memory value) internal view returns (bool) {
                      return _contains(set, value);
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(Set storage set) internal view returns (uint256) {
                      return _length(set);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(Set storage set, uint256 index) internal view returns (string memory) {
                      return _at(set, index);
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(Set storage set) internal view returns (string[] memory) {
                      return _values(set);
                  }
              }

              File 6 of 6: OperatorFilterRegistry
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)
              // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Library for managing
               * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
               * types.
               *
               * Sets have the following properties:
               *
               * - Elements are added, removed, and checked for existence in constant time
               * (O(1)).
               * - Elements are enumerated in O(n). No guarantees are made on the ordering.
               *
               * ```
               * contract Example {
               *     // Add the library methods
               *     using EnumerableSet for EnumerableSet.AddressSet;
               *
               *     // Declare a set state variable
               *     EnumerableSet.AddressSet private mySet;
               * }
               * ```
               *
               * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
               * and `uint256` (`UintSet`) are supported.
               *
               * [WARNING]
               * ====
               * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
               * unusable.
               * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
               *
               * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
               * array of EnumerableSet.
               * ====
               */
              library EnumerableSet {
                  // To implement this library for multiple types with as little code
                  // repetition as possible, we write it in terms of a generic Set type with
                  // bytes32 values.
                  // The Set implementation uses private functions, and user-facing
                  // implementations (such as AddressSet) are just wrappers around the
                  // underlying Set.
                  // This means that we can only create new EnumerableSets for types that fit
                  // in bytes32.
                  struct Set {
                      // Storage of set values
                      bytes32[] _values;
                      // Position of the value in the `values` array, plus 1 because index 0
                      // means a value is not in the set.
                      mapping(bytes32 => uint256) _indexes;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function _add(Set storage set, bytes32 value) private returns (bool) {
                      if (!_contains(set, value)) {
                          set._values.push(value);
                          // The value is stored at length-1, but we add 1 to all indexes
                          // and use 0 as a sentinel value
                          set._indexes[value] = set._values.length;
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function _remove(Set storage set, bytes32 value) private returns (bool) {
                      // We read and store the value's index to prevent multiple reads from the same storage slot
                      uint256 valueIndex = set._indexes[value];
                      if (valueIndex != 0) {
                          // Equivalent to contains(set, value)
                          // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                          // the array, and then remove the last element (sometimes called as 'swap and pop').
                          // This modifies the order of the array, as noted in {at}.
                          uint256 toDeleteIndex = valueIndex - 1;
                          uint256 lastIndex = set._values.length - 1;
                          if (lastIndex != toDeleteIndex) {
                              bytes32 lastValue = set._values[lastIndex];
                              // Move the last value to the index where the value to delete is
                              set._values[toDeleteIndex] = lastValue;
                              // Update the index for the moved value
                              set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                          }
                          // Delete the slot where the moved value was stored
                          set._values.pop();
                          // Delete the index for the deleted slot
                          delete set._indexes[value];
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function _contains(Set storage set, bytes32 value) private view returns (bool) {
                      return set._indexes[value] != 0;
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function _length(Set storage set) private view returns (uint256) {
                      return set._values.length;
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function _at(Set storage set, uint256 index) private view returns (bytes32) {
                      return set._values[index];
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function _values(Set storage set) private view returns (bytes32[] memory) {
                      return set._values;
                  }
                  // Bytes32Set
                  struct Bytes32Set {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _add(set._inner, value);
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _remove(set._inner, value);
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                      return _contains(set._inner, value);
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(Bytes32Set storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                      return _at(set._inner, index);
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      bytes32[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
                  // AddressSet
                  struct AddressSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(AddressSet storage set, address value) internal returns (bool) {
                      return _add(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(AddressSet storage set, address value) internal returns (bool) {
                      return _remove(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(AddressSet storage set, address value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(AddressSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(AddressSet storage set, uint256 index) internal view returns (address) {
                      return address(uint160(uint256(_at(set._inner, index))));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(AddressSet storage set) internal view returns (address[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      address[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
                  // UintSet
                  struct UintSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(UintSet storage set, uint256 value) internal returns (bool) {
                      return _add(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(UintSet storage set, uint256 value) internal returns (bool) {
                      return _remove(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(UintSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                      return uint256(_at(set._inner, index));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(UintSet storage set) internal view returns (uint256[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      uint256[] memory result;
                      /// @solidity memory-safe-assembly
                      assembly {
                          result := store
                      }
                      return result;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
              interface IOperatorFilterRegistry {
                  function isOperatorAllowed(address registrant, address operator) external returns (bool);
                  function register(address registrant) external;
                  function registerAndSubscribe(address registrant, address subscription) external;
                  function registerAndCopyEntries(address registrant, address registrantToCopy) 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: MIT
              pragma solidity ^0.8.13;
              import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
              import {Ownable} from "openzeppelin-contracts/access/Ownable.sol";
              import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol";
              import {OperatorFilterRegistryErrorsAndEvents} from "./OperatorFilterRegistryErrorsAndEvents.sol";
              /**
               * @title  OperatorFilterRegistry
               * @notice Borrows heavily from the QQL BlacklistOperatorFilter contract:
               *         https://github.com/qql-art/contracts/blob/main/contracts/BlacklistOperatorFilter.sol
               * @notice This contracts allows tokens or token owners to register specific addresses or codeHashes that may be
               * *       restricted according to the isOperatorAllowed function.
               */
              contract OperatorFilterRegistry is IOperatorFilterRegistry, OperatorFilterRegistryErrorsAndEvents {
                  using EnumerableSet for EnumerableSet.AddressSet;
                  using EnumerableSet for EnumerableSet.Bytes32Set;
                  /// @dev initialized accounts have a nonzero codehash (see https://eips.ethereum.org/EIPS/eip-1052)
                  /// Note that this will also be a smart contract's codehash when making calls from its constructor.
                  bytes32 constant EOA_CODEHASH = keccak256("");
                  mapping(address => EnumerableSet.AddressSet) private _filteredOperators;
                  mapping(address => EnumerableSet.Bytes32Set) private _filteredCodeHashes;
                  mapping(address => address) private _registrations;
                  mapping(address => EnumerableSet.AddressSet) private _subscribers;
                  /**
                   * @notice restricts method caller to the address or EIP-173 "owner()"
                   */
                  modifier onlyAddressOrOwner(address addr) {
                      if (msg.sender != addr) {
                          try Ownable(addr).owner() returns (address owner) {
                              if (msg.sender != owner) {
                                  revert OnlyAddressOrOwner();
                              }
                          } catch (bytes memory reason) {
                              if (reason.length == 0) {
                                  revert NotOwnable();
                              } else {
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      revert(add(32, reason), mload(reason))
                                  }
                              }
                          }
                      }
                      _;
                  }
                  /**
                   * @notice Returns true if operator is not filtered for a given token, either by address or codeHash. Also returns
                   *         true if supplied registrant address is not registered.
                   */
                  function isOperatorAllowed(address registrant, address operator) external view returns (bool) {
                      address registration = _registrations[registrant];
                      if (registration != address(0)) {
                          EnumerableSet.AddressSet storage filteredOperatorsRef;
                          EnumerableSet.Bytes32Set storage filteredCodeHashesRef;
                          filteredOperatorsRef = _filteredOperators[registration];
                          filteredCodeHashesRef = _filteredCodeHashes[registration];
                          if (filteredOperatorsRef.contains(operator)) {
                              revert AddressFiltered(operator);
                          }
                          if (operator.code.length > 0) {
                              bytes32 codeHash = operator.codehash;
                              if (filteredCodeHashesRef.contains(codeHash)) {
                                  revert CodeHashFiltered(operator, codeHash);
                              }
                          }
                      }
                      return true;
                  }
                  //////////////////
                  // AUTH METHODS //
                  //////////////////
                  /**
                   * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
                   */
                  function register(address registrant) external onlyAddressOrOwner(registrant) {
                      if (_registrations[registrant] != address(0)) {
                          revert AlreadyRegistered();
                      }
                      _registrations[registrant] = registrant;
                      emit RegistrationUpdated(registrant, true);
                  }
                  /**
                   * @notice Unregisters an address with the registry and removes its subscription. May be called by address itself or by EIP-173 owner.
                   *         Note that this does not remove any filtered addresses or codeHashes.
                   *         Also note that any subscriptions to this registrant will still be active and follow the existing filtered addresses and codehashes.
                   */
                  function unregister(address registrant) external onlyAddressOrOwner(registrant) {
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          _subscribers[registration].remove(registrant);
                          emit SubscriptionUpdated(registrant, registration, false);
                      }
                      _registrations[registrant] = address(0);
                      emit RegistrationUpdated(registrant, false);
                  }
                  /**
                   * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
                   */
                  function registerAndSubscribe(address registrant, address subscription) external onlyAddressOrOwner(registrant) {
                      address registration = _registrations[registrant];
                      if (registration != address(0)) {
                          revert AlreadyRegistered();
                      }
                      if (registrant == subscription) {
                          revert CannotSubscribeToSelf();
                      }
                      address subscriptionRegistration = _registrations[subscription];
                      if (subscriptionRegistration == address(0)) {
                          revert NotRegistered(subscription);
                      }
                      if (subscriptionRegistration != subscription) {
                          revert CannotSubscribeToRegistrantWithSubscription(subscription);
                      }
                      _registrations[registrant] = subscription;
                      _subscribers[subscription].add(registrant);
                      emit RegistrationUpdated(registrant, true);
                      emit SubscriptionUpdated(registrant, subscription, true);
                  }
                  /**
                   * @notice Registers an address with the registry and copies the filtered operators and codeHashes from another
                   *         address without subscribing.
                   */
                  function registerAndCopyEntries(address registrant, address registrantToCopy)
                      external
                      onlyAddressOrOwner(registrant)
                  {
                      if (registrantToCopy == registrant) {
                          revert CannotCopyFromSelf();
                      }
                      address registration = _registrations[registrant];
                      if (registration != address(0)) {
                          revert AlreadyRegistered();
                      }
                      address registrantRegistration = _registrations[registrantToCopy];
                      if (registrantRegistration == address(0)) {
                          revert NotRegistered(registrantToCopy);
                      }
                      _registrations[registrant] = registrant;
                      emit RegistrationUpdated(registrant, true);
                      _copyEntries(registrant, registrantToCopy);
                  }
                  /**
                   * @notice Update an operator address for a registered address - when filtered is true, the operator is filtered.
                   */
                  function updateOperator(address registrant, address operator, bool filtered)
                      external
                      onlyAddressOrOwner(registrant)
                  {
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          revert CannotUpdateWhileSubscribed(registration);
                      }
                      EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant];
                      if (!filtered) {
                          bool removed = filteredOperatorsRef.remove(operator);
                          if (!removed) {
                              revert AddressNotFiltered(operator);
                          }
                      } else {
                          bool added = filteredOperatorsRef.add(operator);
                          if (!added) {
                              revert AddressAlreadyFiltered(operator);
                          }
                      }
                      emit OperatorUpdated(registrant, operator, filtered);
                  }
                  /**
                   * @notice Update a codeHash for a registered address - when filtered is true, the codeHash is filtered.
                   */
                  function updateCodeHash(address registrant, bytes32 codeHash, bool filtered)
                      external
                      onlyAddressOrOwner(registrant)
                  {
                      if (codeHash == EOA_CODEHASH) {
                          revert CannotFilterEOAs();
                      }
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          revert CannotUpdateWhileSubscribed(registration);
                      }
                      EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant];
                      if (!filtered) {
                          bool removed = filteredCodeHashesRef.remove(codeHash);
                          if (!removed) {
                              revert CodeHashNotFiltered(codeHash);
                          }
                      } else {
                          bool added = filteredCodeHashesRef.add(codeHash);
                          if (!added) {
                              revert CodeHashAlreadyFiltered(codeHash);
                          }
                      }
                      emit CodeHashUpdated(registrant, codeHash, filtered);
                  }
                  /**
                   * @notice Update multiple operators for a registered address - when filtered is true, the operators will be filtered. Reverts on duplicates.
                   */
                  function updateOperators(address registrant, address[] calldata operators, bool filtered)
                      external
                      onlyAddressOrOwner(registrant)
                  {
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          revert CannotUpdateWhileSubscribed(registration);
                      }
                      EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrant];
                      uint256 operatorsLength = operators.length;
                      unchecked {
                          if (!filtered) {
                              for (uint256 i = 0; i < operatorsLength; ++i) {
                                  address operator = operators[i];
                                  bool removed = filteredOperatorsRef.remove(operator);
                                  if (!removed) {
                                      revert AddressNotFiltered(operator);
                                  }
                              }
                          } else {
                              for (uint256 i = 0; i < operatorsLength; ++i) {
                                  address operator = operators[i];
                                  bool added = filteredOperatorsRef.add(operator);
                                  if (!added) {
                                      revert AddressAlreadyFiltered(operator);
                                  }
                              }
                          }
                      }
                      emit OperatorsUpdated(registrant, operators, filtered);
                  }
                  /**
                   * @notice Update multiple codeHashes for a registered address - when filtered is true, the codeHashes will be filtered. Reverts on duplicates.
                   */
                  function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered)
                      external
                      onlyAddressOrOwner(registrant)
                  {
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          revert CannotUpdateWhileSubscribed(registration);
                      }
                      EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrant];
                      uint256 codeHashesLength = codeHashes.length;
                      unchecked {
                          if (!filtered) {
                              for (uint256 i = 0; i < codeHashesLength; ++i) {
                                  bytes32 codeHash = codeHashes[i];
                                  bool removed = filteredCodeHashesRef.remove(codeHash);
                                  if (!removed) {
                                      revert CodeHashNotFiltered(codeHash);
                                  }
                              }
                          } else {
                              for (uint256 i = 0; i < codeHashesLength; ++i) {
                                  bytes32 codeHash = codeHashes[i];
                                  if (codeHash == EOA_CODEHASH) {
                                      revert CannotFilterEOAs();
                                  }
                                  bool added = filteredCodeHashesRef.add(codeHash);
                                  if (!added) {
                                      revert CodeHashAlreadyFiltered(codeHash);
                                  }
                              }
                          }
                      }
                      emit CodeHashesUpdated(registrant, codeHashes, filtered);
                  }
                  /**
                   * @notice Subscribe an address to another registrant's filtered operators and codeHashes. Will remove previous
                   *         subscription if present.
                   *         Note that accounts with subscriptions may go on to subscribe to other accounts - in this case,
                   *         subscriptions will not be forwarded. Instead the former subscription's existing entries will still be
                   *         used.
                   */
                  function subscribe(address registrant, address newSubscription) external onlyAddressOrOwner(registrant) {
                      if (registrant == newSubscription) {
                          revert CannotSubscribeToSelf();
                      }
                      if (newSubscription == address(0)) {
                          revert CannotSubscribeToZeroAddress();
                      }
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration == newSubscription) {
                          revert AlreadySubscribed(newSubscription);
                      }
                      address newSubscriptionRegistration = _registrations[newSubscription];
                      if (newSubscriptionRegistration == address(0)) {
                          revert NotRegistered(newSubscription);
                      }
                      if (newSubscriptionRegistration != newSubscription) {
                          revert CannotSubscribeToRegistrantWithSubscription(newSubscription);
                      }
                      if (registration != registrant) {
                          _subscribers[registration].remove(registrant);
                          emit SubscriptionUpdated(registrant, registration, false);
                      }
                      _registrations[registrant] = newSubscription;
                      _subscribers[newSubscription].add(registrant);
                      emit SubscriptionUpdated(registrant, newSubscription, true);
                  }
                  /**
                   * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
                   */
                  function unsubscribe(address registrant, bool copyExistingEntries) external onlyAddressOrOwner(registrant) {
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration == registrant) {
                          revert NotSubscribed();
                      }
                      _subscribers[registration].remove(registrant);
                      _registrations[registrant] = registrant;
                      emit SubscriptionUpdated(registrant, registration, false);
                      if (copyExistingEntries) {
                          _copyEntries(registrant, registration);
                      }
                  }
                  /**
                   * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
                   */
                  function copyEntriesOf(address registrant, address registrantToCopy) external onlyAddressOrOwner(registrant) {
                      if (registrant == registrantToCopy) {
                          revert CannotCopyFromSelf();
                      }
                      address registration = _registrations[registrant];
                      if (registration == address(0)) {
                          revert NotRegistered(registrant);
                      }
                      if (registration != registrant) {
                          revert CannotUpdateWhileSubscribed(registration);
                      }
                      address registrantRegistration = _registrations[registrantToCopy];
                      if (registrantRegistration == address(0)) {
                          revert NotRegistered(registrantToCopy);
                      }
                      _copyEntries(registrant, registrantToCopy);
                  }
                  /// @dev helper to copy entries from registrantToCopy to registrant and emit events
                  function _copyEntries(address registrant, address registrantToCopy) private {
                      EnumerableSet.AddressSet storage filteredOperatorsRef = _filteredOperators[registrantToCopy];
                      EnumerableSet.Bytes32Set storage filteredCodeHashesRef = _filteredCodeHashes[registrantToCopy];
                      uint256 filteredOperatorsLength = filteredOperatorsRef.length();
                      uint256 filteredCodeHashesLength = filteredCodeHashesRef.length();
                      unchecked {
                          for (uint256 i = 0; i < filteredOperatorsLength; ++i) {
                              address operator = filteredOperatorsRef.at(i);
                              bool added = _filteredOperators[registrant].add(operator);
                              if (added) {
                                  emit OperatorUpdated(registrant, operator, true);
                              }
                          }
                          for (uint256 i = 0; i < filteredCodeHashesLength; ++i) {
                              bytes32 codehash = filteredCodeHashesRef.at(i);
                              bool added = _filteredCodeHashes[registrant].add(codehash);
                              if (added) {
                                  emit CodeHashUpdated(registrant, codehash, true);
                              }
                          }
                      }
                  }
                  //////////////////
                  // VIEW METHODS //
                  //////////////////
                  /**
                   * @notice Get the subscription address of a given registrant, if any.
                   */
                  function subscriptionOf(address registrant) external view returns (address subscription) {
                      subscription = _registrations[registrant];
                      if (subscription == address(0)) {
                          revert NotRegistered(registrant);
                      } else if (subscription == registrant) {
                          subscription = address(0);
                      }
                  }
                  /**
                   * @notice Get the set of addresses subscribed to a given registrant.
                   *         Note that order is not guaranteed as updates are made.
                   */
                  function subscribers(address registrant) external view returns (address[] memory) {
                      return _subscribers[registrant].values();
                  }
                  /**
                   * @notice Get the subscriber at a given index in the set of addresses subscribed to a given registrant.
                   *         Note that order is not guaranteed as updates are made.
                   */
                  function subscriberAt(address registrant, uint256 index) external view returns (address) {
                      return _subscribers[registrant].at(index);
                  }
                  /**
                   * @notice Returns true if operator is filtered by a given address or its subscription.
                   */
                  function isOperatorFiltered(address registrant, address operator) external view returns (bool) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredOperators[registration].contains(operator);
                      }
                      return _filteredOperators[registrant].contains(operator);
                  }
                  /**
                   * @notice Returns true if a codeHash is filtered by a given address or its subscription.
                   */
                  function isCodeHashFiltered(address registrant, bytes32 codeHash) external view returns (bool) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredCodeHashes[registration].contains(codeHash);
                      }
                      return _filteredCodeHashes[registrant].contains(codeHash);
                  }
                  /**
                   * @notice Returns true if the hash of an address's code is filtered by a given address or its subscription.
                   */
                  function isCodeHashOfFiltered(address registrant, address operatorWithCode) external view returns (bool) {
                      bytes32 codeHash = operatorWithCode.codehash;
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredCodeHashes[registration].contains(codeHash);
                      }
                      return _filteredCodeHashes[registrant].contains(codeHash);
                  }
                  /**
                   * @notice Returns true if an address has registered
                   */
                  function isRegistered(address registrant) external view returns (bool) {
                      return _registrations[registrant] != address(0);
                  }
                  /**
                   * @notice Returns a list of filtered operators for a given address or its subscription.
                   */
                  function filteredOperators(address registrant) external view returns (address[] memory) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredOperators[registration].values();
                      }
                      return _filteredOperators[registrant].values();
                  }
                  /**
                   * @notice Returns the set of filtered codeHashes for a given address or its subscription.
                   *         Note that order is not guaranteed as updates are made.
                   */
                  function filteredCodeHashes(address registrant) external view returns (bytes32[] memory) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredCodeHashes[registration].values();
                      }
                      return _filteredCodeHashes[registrant].values();
                  }
                  /**
                   * @notice Returns the filtered operator at the given index of the set of filtered operators for a given address or
                   *         its subscription.
                   *         Note that order is not guaranteed as updates are made.
                   */
                  function filteredOperatorAt(address registrant, uint256 index) external view returns (address) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredOperators[registration].at(index);
                      }
                      return _filteredOperators[registrant].at(index);
                  }
                  /**
                   * @notice Returns the filtered codeHash at the given index of the list of filtered codeHashes for a given address or
                   *         its subscription.
                   *         Note that order is not guaranteed as updates are made.
                   */
                  function filteredCodeHashAt(address registrant, uint256 index) external view returns (bytes32) {
                      address registration = _registrations[registrant];
                      if (registration != registrant) {
                          return _filteredCodeHashes[registration].at(index);
                      }
                      return _filteredCodeHashes[registrant].at(index);
                  }
                  /// @dev Convenience method to compute the code hash of an arbitrary contract
                  function codeHashOf(address a) external view returns (bytes32) {
                      return a.codehash;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              contract OperatorFilterRegistryErrorsAndEvents {
                  error CannotFilterEOAs();
                  error AddressAlreadyFiltered(address operator);
                  error AddressNotFiltered(address operator);
                  error CodeHashAlreadyFiltered(bytes32 codeHash);
                  error CodeHashNotFiltered(bytes32 codeHash);
                  error OnlyAddressOrOwner();
                  error NotRegistered(address registrant);
                  error AlreadyRegistered();
                  error AlreadySubscribed(address subscription);
                  error NotSubscribed();
                  error CannotUpdateWhileSubscribed(address subscription);
                  error CannotSubscribeToSelf();
                  error CannotSubscribeToZeroAddress();
                  error NotOwnable();
                  error AddressFiltered(address filtered);
                  error CodeHashFiltered(address account, bytes32 codeHash);
                  error CannotSubscribeToRegistrantWithSubscription(address registrant);
                  error CannotCopyFromSelf();
                  event RegistrationUpdated(address indexed registrant, bool indexed registered);
                  event OperatorUpdated(address indexed registrant, address indexed operator, bool indexed filtered);
                  event OperatorsUpdated(address indexed registrant, address[] operators, bool indexed filtered);
                  event CodeHashUpdated(address indexed registrant, bytes32 indexed codeHash, bool indexed filtered);
                  event CodeHashesUpdated(address indexed registrant, bytes32[] codeHashes, bool indexed filtered);
                  event SubscriptionUpdated(address indexed registrant, address indexed subscription, bool indexed subscribed);
              }