ETH Price: $2,777.51 (+0.15%)

Transaction Decoder

Block:
19768984 at Apr-30-2024 03:21:11 PM +UTC
Transaction Fee:
0.003740311815829611 ETH $10.39
Gas Used:
187,979 Gas / 19.897498209 Gwei

Emitted Events:

131 MythicsProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000c04fec15c5a3e2efcf7cc321ebd1d18da6db73f0, 0x00000000000000000000000000a73466fb4bb2bb665a83f48c5535e78fe109e6, 0x000000000000000000000000000000000000000000000000000000000000207f )
132 GnosisSafeProxy.0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d( 0x3d0ce9bfc3ed7d6862dbb28b2dea94561fe714a1b4d019aa8af39730d1ad7c3d, 0x000000000000000000000000b2ecfe4e4d61f8790bbb9de2d1259b9e2410cea5, 0000000000000000000000000000000000000000000000000001c2332b29f000 )
133 ERC1967Proxy.0x7dc5c0699ac8dd5250cbe368a2fc3b4a2daadb120ad07f6cccea29f83482686e( 0x7dc5c0699ac8dd5250cbe368a2fc3b4a2daadb120ad07f6cccea29f83482686e, 937bef1462caadfe5b8351536e441b5d975500661a8a7b263a8218bbd55f0d0b, 000000000000000000207f02c04fec15c5a3e2efcf7cc321ebd1d18da6db73f0, 00000000015fb7f9b8c38000c0ffee8ff7e5497c2d6f7684859709225fcc5be8, 000000000000000000000032c5ee3c8d11a702c8dc00247eadd9cff2abf40298 )

Account State Difference:

  Address   Before After State Difference Code
0x00a73466...78fe109E6
0.112725459617210468 Eth
Nonce: 731
0.009985147801380857 Eth
Nonce: 732
0.102740311815829611
(Boba Builder)
4.700583150224296409 Eth4.700771129224296409 Eth0.000187979
0xb2ecfE4E...e2410CEA5
(Blur.io: Marketplace 3)
0xC04fec15...Da6DB73f0 0.029251002440772803 Eth0.127756002440772803 Eth0.098505
0xC0FFee8F...25Fcc5Be8
0xc5EE3c8D...2ABf40298 8.784404348348759498 Eth8.784899348348759498 Eth0.000495

Execution Trace

ETH 0.099 ERC1967Proxy.70bce2d6( )
  • ETH 0.099 BlurExchangeV2.takeAskSingle( )
    • Null: 0x000...001.65ba4fd8( )
    • Null: 0x000...001.3c678260( )
    • Delegate.transfer( taker=0x00a73466fB4bB2BB665a83F48c5535E78fe109E6, orderType=0, transfers=, length=1 ) => ( successful=[true] )
      • MythicsProxy.42842e0e( )
        • 0x228dc33ea527608406562f9adacbfbce5e754d7c.42842e0e( )
          • OperatorFilterRegistry.isOperatorAllowed( registrant=0xC0FFee8FF7e5497C2d6F7684859709225Fcc5Be8, operator=0x2f18F339620a63e43f0839Eeb18D7de1e1Be4DfB ) => ( True )
          • OperatorFilterRegistry.isOperatorAllowed( registrant=0xC0FFee8FF7e5497C2d6F7684859709225Fcc5Be8, operator=0x2f18F339620a63e43f0839Eeb18D7de1e1Be4DfB ) => ( True )
          • Deadities.onTransfer( from=0xC04fec15c5a3E2EfCf7cc321ebD1d18Da6DB73f0, to=0x00a73466fB4bB2BB665a83F48c5535E78fe109E6, firstTokenId=8319, batchSize=1 )
          • ETH 0.000495 GnosisSafeProxy.CALL( )
            • ETH 0.000495 GnosisSafe.DELEGATECALL( )
            • ETH 0.098505 0xc04fec15c5a3e2efcf7cc321ebd1d18da6db73f0.CALL( )
              File 1 of 8: 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 8: MythicsProxy
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";
              /**
               * @title Mythics: Egg
               * @notice A redeemable token claimable by all diamond nested Moonbirds.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsProxy is ERC1967Proxy {
                  constructor(address impl, bytes memory data) ERC1967Proxy(impl, data) {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./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 initializing the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      _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.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // 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 Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @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 Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @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
              // 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
                   * ====
                   *
                   * [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 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)
              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 StorageSlot {
                  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
                      }
                  }
              }
              

              File 3 of 8: GnosisSafeProxy
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              
              /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
              /// @author Richard Meissner - <[email protected]>
              interface IProxy {
                  function masterCopy() external view returns (address);
              }
              
              /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract GnosisSafeProxy {
                  // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                  // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                  address internal singleton;
              
                  /// @dev Constructor function sets address of singleton contract.
                  /// @param _singleton Singleton address.
                  constructor(address _singleton) {
                      require(_singleton != address(0), "Invalid singleton address provided");
                      singleton = _singleton;
                  }
              
                  /// @dev Fallback function forwards all transactions and returns all received return data.
                  fallback() external payable {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                          // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                          if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                              mstore(0, _singleton)
                              return(0, 0x20)
                          }
                          calldatacopy(0, 0, calldatasize())
                          let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if eq(success, 0) {
                              revert(0, returndatasize())
                          }
                          return(0, returndatasize())
                      }
                  }
              }
              
              /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
              /// @author Stefan George - <[email protected]>
              contract GnosisSafeProxyFactory {
                  event ProxyCreation(GnosisSafeProxy proxy, address singleton);
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param singleton Address of singleton contract.
                  /// @param data Payload for message call sent to new proxy contract.
                  function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                      proxy = new GnosisSafeProxy(singleton);
                      if (data.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, singleton);
                  }
              
                  /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                  function proxyRuntimeCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).runtimeCode;
                  }
              
                  /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                  function proxyCreationCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).creationCode;
                  }
              
                  /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                  ///      This method is only meant as an utility to be called from other methods
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function deployProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) internal returns (GnosisSafeProxy proxy) {
                      // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                      bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                      bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                      }
                      require(address(proxy) != address(0), "Create2 call failed");
                  }
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function createProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) public returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      if (initializer.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, _singleton);
                  }
              
                  /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                  function createProxyWithCallback(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce,
                      IProxyCreationCallback callback
                  ) public returns (GnosisSafeProxy proxy) {
                      uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                      proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                      if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                  }
              
                  /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                  ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                  ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function calculateCreateProxyWithNonceAddress(
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      revert(string(abi.encodePacked(proxy)));
                  }
              }
              
              interface IProxyCreationCallback {
                  function proxyCreated(
                      GnosisSafeProxy proxy,
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external;
              }

              File 4 of 8: 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 5 of 8: 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 6 of 8: 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);
              }
              

              File 7 of 8: Deadities
              // SPDX-License-Identifier: MIT
              // Copyright 2023 Divergence Tech Ltd
              pragma solidity ^0.8.19;
              import {IERC721TransferListener} from "../Mythics/MythicsV3.sol";
              import {SacrificedOddityMythics} from "./SacrificedOddityMythics.sol";
              import {AccessControlEnumerable, BaseTokenURI} from "ethier/erc721/BaseTokenURI.sol";
              import {IERC721, IERC721Metadata, IERC165} from "openzeppelin-contracts/interfaces/IERC721Metadata.sol";
              import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
              /**
               * @title Deadities
               * @notice Shadow NFTs that always follow Moonbirds Mythics as they are transferred. Only those Mythics that were minted
               * as a result of the "sacrifice" of an Oddity have an associated Deadity.
               * @dev As these tokens shadow the ownership of others, all transfer-related functionality is disabled.
               * @author Arran Schlosberg (@divergencearran)
               * @custom:reviewer David Huber (@cxkoda)
               */
              contract Deadities is IERC721Metadata, IERC721TransferListener, BaseTokenURI {
                  using Strings for uint256;
                  /**
                   * @notice Throw by functions that have been disabled. See contract-level @notice + @dev for rationale.
                   */
                  error FunctionDisabled();
                  /**
                   * @notice Thrown if a token doesn't exist.
                   */
                  error NonExistentToken(uint256 tokenId);
                  /**
                   * @notice Thrown by onTransfer() if called by any address other than the Mythics contract.
                   */
                  error OnlyMythicsContract(address caller);
                  /**
                   * @dev Guess.
                   */
                  IERC721 private immutable mythics;
                  /**
                   * @dev Number of bitmaps (encoding Mythic IDs) that have been processed by mint().
                   */
                  uint256 private _bitmapsMinted;
                  constructor(IERC721 mythics_, address admin) BaseTokenURI("") {
                      mythics = mythics_;
                      _grantRole(DEFAULT_ADMIN_ROLE, admin);
                      _grantRole(DEFAULT_STEERING_ROLE, msg.sender);
                  }
                  /**
                   * @notice Mints the next batch of tokens.
                   * @dev We rely entirely on the respective Mythics token for ownership so there is no storage bookkeeping, there is
                   * only a Transfer(0, …) event emitted.
                   * @param numBitmaps Mythic IDs for which a Deadity exists are stored in bitmaps of up to 256 IDs; specifies how
                   * many bitmaps of IDs to process. Note that not all encode the same number of IDs.
                   */
                  function mint(uint256 numBitmaps) external {
                      uint256 mapId = _bitmapsMinted;
                      uint256 end = mapId + numBitmaps;
                      IERC721 _mythics = mythics;
                      unchecked {
                          // This takes approximately 13M gas when the mythics.ownerOf() is stubbed out with a pure
                          // function. It will be more in production because of the storage reads.
                          for (; mapId < end; ++mapId) {
                              uint256 offset = 0;
                              for (uint256 map = SacrificedOddityMythics._bitmap(mapId); map > 0; map >>= 1) {
                                  if ((map & uint256(1)) == 1) {
                                      uint256 tokenId = mapId * 256 + offset;
                                      emit IERC721.Transfer(address(0), _mythics.ownerOf(tokenId), tokenId);
                                  }
                                  ++offset;
                              }
                          }
                      }
                      _bitmapsMinted = end;
                  }
                  modifier tokenExists(uint256 tokenId) {
                      if (!SacrificedOddityMythics._fromOddity(tokenId)) {
                          revert NonExistentToken(tokenId);
                      }
                      _;
                  }
                  /**
                   * @dev See mint() for rationale re naked emit.
                   */
                  function _transfer(address from, address to, uint256 tokenId) private {
                      emit IERC721.Transfer(from, to, tokenId);
                  }
                  /**
                   * @dev Hook called by the Mythics contract when token(s) are transferred, resulting in respective Transfer events
                   * for those Mythics that were created due to the sacrifice of an Oddity.
                   */
                  function onTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) external {
                      if (msg.sender != address(mythics)) {
                          revert OnlyMythicsContract(msg.sender);
                      }
                      uint256 end = firstTokenId + batchSize;
                      for (uint256 tokenId = firstTokenId; tokenId < end; ++tokenId) {
                          if (SacrificedOddityMythics._fromOddity(tokenId)) {
                              _transfer(from, to, tokenId);
                          }
                      }
                  }
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external pure returns (string memory) {
                      return "Deadities";
                  }
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external pure returns (string memory) {
                      return "DEAD";
                  }
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   */
                  function ownerOf(uint256 tokenId) external view tokenExists(tokenId) returns (address) {
                      return mythics.ownerOf(tokenId);
                  }
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view tokenExists(tokenId) returns (string memory) {
                      return string(abi.encodePacked(baseTokenURI(), tokenId.toString()));
                  }
                  /**
                   * @notice Returns the total number of tokens.
                   */
                  function totalSupply() external pure returns (uint256) {
                      return SacrificedOddityMythics.NUM_BITS_SET;
                  }
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   * @dev Implementation is O(max(tokenId)) so is only suitable for off-chain indexing.
                   */
                  function balanceOf(address owner) external view returns (uint256) {
                      IERC721 _mythics = mythics;
                      uint256 balance;
                      unchecked {
                          for (uint256 mapId = 0; mapId < SacrificedOddityMythics.NUM_BITMAPS; ++mapId) {
                              uint256 offset = 0;
                              for (uint256 map = SacrificedOddityMythics._bitmap(mapId); map > 0; map >>= 1) {
                                  if ((map & uint256(1)) == 1 && _mythics.ownerOf(mapId * 256 + offset) == owner) {
                                      ++balance;
                                  }
                                  ++offset;
                              }
                          }
                      }
                      return balance;
                  }
                  /**
                   * @notice Disabled but present to compile as IERC721.
                   */
                  function safeTransferFrom(address, address, uint256, bytes calldata) external pure {
                      revert FunctionDisabled();
                  }
                  /**
                   * @notice Disabled but present to compile as IERC721.
                   */
                  function safeTransferFrom(address, address, uint256) external pure {
                      revert FunctionDisabled();
                  }
                  /**
                   * @notice Disabled but present to compile as IERC721.
                   */
                  function transferFrom(address, address, uint256) external pure {
                      revert FunctionDisabled();
                  }
                  /**
                   * @notice Disabled but present to compile as IERC721.
                   */
                  function approve(address, uint256) external pure {
                      revert FunctionDisabled();
                  }
                  /**
                   * @notice Disabled but present to compile as IERC721.
                   */
                  function setApprovalForAll(address, bool) external pure {
                      revert FunctionDisabled();
                  }
                  /**
                   * @notice Always returns 0;
                   */
                  function getApproved(uint256) external pure returns (address) {
                      return address(0);
                  }
                  /**
                   * @notice Always returns false;
                   */
                  function isApprovedForAll(address, address) external pure returns (bool) {
                      return false;
                  }
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      override(AccessControlEnumerable, IERC165)
                      returns (bool)
                  {
                      return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC165).interfaceId
                          || interfaceId == type(IERC721Metadata).interfaceId || AccessControlEnumerable.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {Address} from "openzeppelin-contracts/utils/Address.sol";
              import {MythicsV2} from "./MythicsV2.sol";
              /**
               * @notice ERC721 Transfer Listener
               */
              interface IERC721TransferListener {
                  /**
                   * @notice Hook called upon token transfers.
                   */
                  function onTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) external;
              }
              library MythicsV3Storage {
                  bytes32 internal constant STORAGE_SLOT = keccak256("Mythics.V3.storage.location");
                  /**
                   * @notice This is the storage layout for the Mythics V3 contract.
                   * @dev The fields in this struct MUST NOT be removed, renamed, or reordered. Only additionas are allowed to keep
                   * the storage layout compatible between upgrades.
                   */
                  struct Layout {
                      IERC721TransferListener transferListener;
                  }
                  function layout() internal pure returns (Layout storage l) {
                      bytes32 slot = STORAGE_SLOT;
                      assembly {
                          l.slot := slot
                      }
                  }
              }
              /**
               * @title Mythics V3
               * @notice Adding notifications on transfer
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsV3 is MythicsV2 {
                  using Address for address;
                  struct InitArgsV3 {
                      IERC721TransferListener listener;
                  }
                  function initializeV3(InitArgsV3 memory init) public virtual reinitializer(3) {
                      MythicsV3Storage.layout().transferListener = init.listener;
                  }
                  function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
                      internal
                      virtual
                      override
                  {
                      super._afterTokenTransfer(from, to, firstTokenId, batchSize);
                      IERC721TransferListener listener = MythicsV3Storage.layout().transferListener;
                      // Trying to notify EOAs would result in reverts that would block transfers. We therefore return early in this
                      // case.
                      if (!address(listener).isContract()) {
                          return;
                      }
                      try listener.onTransfer{gas: 30_000}(from, to, firstTokenId, batchSize) {} catch {}
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 Divergence Tech Ltd
              pragma solidity ^0.8.19;
              // Generated Code - DO NOT EDIT
              library SacrificedOddityMythics {
                  uint256 internal constant NUM_BITMAPS = 25;
                  
                  uint256 internal constant NUM_BITS_SET = 3617;
                  function _bitmap(uint256 id) internal pure returns (uint256 bitmap) {
                      if (id >= NUM_BITMAPS) {
                          return 0;
                      }
                      assembly {
                          switch id
                          case 0 {
                              bitmap := 115792089237315784048216292424100703509555863858832249713185853195729623842815
                          }
                          case 1 {
                              bitmap := 113982865449968517032308318179027504998096168217306150269379887640075470635011
                          }
                          case 2 {
                              bitmap := 115792089237105571041101126167665677834686322290959825413008695683431652130815
                          }
                          case 3 {
                              bitmap := 112173614055635451478815021543382012168546980377358680286683472713984818284543
                          }
                          case 4 {
                              bitmap := 115792089237105571241968381700039283871472648852314729737099496265188730994687
                          }
                          case 5 {
                              bitmap := 115792089237312904414459407587685329689899327723318629418425842846152041433087
                          }
                          case 6 {
                              bitmap := 115679011240849952163682580764399248937639501696991653643124636415141229887519
                          }
                          case 7 {
                              bitmap := 115792089233946202491911666099101743040959878075793414362551275626348540854271
                          }
                          case 8 {
                              bitmap := 441711766194595690076966096205886555582734539942871422419006199917740031
                          }
                          case 9 {
                              bitmap := 115792089021636724930400699226638077990523808862022708858695834449781467381728
                          }
                          case 10 {
                              bitmap := 115763819792119527942900115646418449313208210896779290360694816403175346536447
                          }
                          case 11 {
                              bitmap := 115792089237309613408480251052212625570864500807933680928934320435481179848703
                          }
                          case 12 {
                              bitmap := 115735550239083073793626782886452234543121561830058017218305547259287586209855
                          }
                          case 13 {
                              bitmap := 115792089237312904414848661455065265114195483845727525271529183431796149190655
                          }
                          case 14 {
                              bitmap := 108555085385420519907237212809158388702171340794820489674619758837999444426783
                          }
                          case 15 {
                              bitmap := 115792089236894946357330384028083038760929798041186650002004608523928896274431
                          }
                          case 16 {
                              bitmap := 86844508639753341163368316096573868228552523284398553072539662106409444577279
                          }
                          case 17 {
                              bitmap := 2588154892901967303862341165498953769500217641890097615919225963020287
                          }
                          case 18 {
                              bitmap := 380058894137238120340422404666724666522574284817682128896
                          }
                          case 19 {
                              bitmap := 431359147377445631077980486941922672330952424291990423561480796372992
                          }
                          case 20 {
                              bitmap := 27606985387162230629810415266812534666927630529678916506709465801687040
                          }
                          case 21 {
                              bitmap := 115792089237263540081205728544468711375301041754031835095422641935697880023024
                          }
                          case 22 {
                              bitmap := 115792089183396302089269752561550691424835253869979900275526434382664777597183
                          }
                          case 23 {
                              bitmap := 904625683686970817741383290710736681721806396387499427736909327769553862655
                          }
                          case 24 {
                              bitmap := 2199014866944
                          }
                          
                      }
                      return bitmap;
                  }
                  function _fromOddity(uint256 mythicId) internal pure returns (bool) {
                      uint256 map = _bitmap(mythicId/256);
                      return (
                          (map >> (mythicId % 256))
                          & uint256(1)
                      ) == 1;
                  }
              }
              library SacrificedOddityMythicsIDs {
                  function mythicIds() internal pure returns (uint16[3617] memory ids) {
                      return [uint16(0), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1172, 1173, 1174, 1175, 1176, 1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1320, 1321, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1420, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, 1456, 1457, 1458, 1459, 1460, 1491, 1492, 1493, 1494, 1495, 1496, 1497, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514, 1515, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1529, 1530, 1531, 1532, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, 1608, 1609, 1610, 1611, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659, 1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669, 1670, 1671, 1672, 1673, 1674, 1675, 1676, 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, 1713, 1714, 1715, 1716, 1717, 1718, 1719, 1720, 1721, 1722, 1723, 1724, 1725, 1726, 1727, 1728, 1729, 1730, 1731, 1732, 1733, 1734, 1735, 1736, 1737, 1738, 1739, 1740, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, 1824, 1825, 1826, 1827, 1828, 1829, 1830, 1831, 1851, 1852, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, 1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1895, 1896, 1897, 1898, 1899, 1900, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199, 2200, 2201, 2202, 2203, 2204, 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626, 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, 2646, 2647, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, 2680, 2681, 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, 2736, 2737, 2738, 2739, 2740, 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 2749, 2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2760, 2761, 2762, 2763, 2764, 2765, 2766, 2767, 2768, 2769, 2770, 2771, 2772, 2773, 2774, 2775, 2776, 2777, 2778, 2779, 2780, 2781, 2782, 2783, 2784, 2785, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, 2831, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, 2841, 2842, 2843, 2844, 2845, 2846, 2847, 2848, 2849, 2850, 2851, 2852, 2853, 2875, 2876, 2877, 2878, 2879, 2880, 2881, 2882, 2883, 2884, 2885, 2886, 2887, 2888, 2889, 2890, 2891, 2892, 2893, 2894, 2895, 2896, 2897, 2898, 2899, 2900, 2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909, 2910, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, 2923, 2924, 2957, 2958, 2959, 2960, 2961, 2962, 2963, 2964, 2965, 2966, 2967, 2968, 2969, 2970, 2971, 2972, 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2983, 2984, 2985, 2986, 2987, 2988, 2989, 2990, 2991, 2992, 2993, 2994, 2995, 2996, 2997, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3028, 3029, 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076, 3077, 3100, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109, 3110, 3111, 3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3122, 3123, 3124, 3125, 3126, 3127, 3128, 3129, 3130, 3131, 3132, 3133, 3134, 3135, 3136, 3137, 3138, 3139, 3140, 3141, 3142, 3143, 3144, 3145, 3146, 3147, 3148, 3149, 3178, 3179, 3180, 3181, 3182, 3183, 3184, 3185, 3186, 3187, 3188, 3189, 3190, 3191, 3192, 3193, 3194, 3195, 3196, 3197, 3198, 3199, 3200, 3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3210, 3211, 3212, 3213, 3214, 3215, 3216, 3217, 3218, 3219, 3220, 3221, 3222, 3223, 3224, 3225, 3226, 3227, 3248, 3249, 3250, 3251, 3252, 3253, 3254, 3255, 3256, 3257, 3258, 3259, 3260, 3261, 3262, 3263, 3264, 3265, 3266, 3267, 3268, 3269, 3270, 3271, 3272, 3273, 3274, 3275, 3276, 3277, 3278, 3279, 3280, 3281, 3282, 3283, 3284, 3285, 3286, 3287, 3288, 3289, 3290, 3291, 3292, 3293, 3294, 3295, 3296, 3297, 3317, 3318, 3319, 3320, 3321, 3322, 3323, 3324, 3325, 3326, 3327, 3328, 3329, 3330, 3331, 3332, 3333, 3334, 3335, 3336, 3337, 3338, 3339, 3340, 3341, 3342, 3343, 3344, 3345, 3346, 3347, 3348, 3349, 3350, 3351, 3352, 3353, 3354, 3355, 3356, 3357, 3358, 3359, 3360, 3361, 3362, 3363, 3364, 3365, 3366, 3392, 3393, 3394, 3395, 3396, 3397, 3398, 3399, 3400, 3401, 3402, 3403, 3404, 3405, 3406, 3407, 3408, 3409, 3410, 3411, 3412, 3413, 3414, 3415, 3416, 3417, 3418, 3419, 3420, 3421, 3422, 3423, 3424, 3425, 3426, 3427, 3428, 3429, 3430, 3431, 3432, 3433, 3434, 3435, 3436, 3437, 3438, 3439, 3440, 3441, 3466, 3467, 3468, 3469, 3470, 3471, 3472, 3473, 3474, 3475, 3476, 3477, 3478, 3479, 3480, 3481, 3482, 3483, 3484, 3485, 3486, 3487, 3488, 3489, 3490, 3491, 3492, 3493, 3494, 3495, 3496, 3497, 3498, 3499, 3500, 3501, 3502, 3503, 3504, 3505, 3506, 3507, 3508, 3509, 3510, 3511, 3512, 3513, 3514, 3515, 3539, 3540, 3541, 3542, 3543, 3544, 3545, 3546, 3547, 3548, 3549, 3550, 3551, 3552, 3553, 3554, 3555, 3556, 3557, 3558, 3559, 3560, 3561, 3562, 3563, 3564, 3565, 3566, 3567, 3568, 3569, 3570, 3571, 3572, 3573, 3574, 3575, 3576, 3577, 3578, 3579, 3580, 3581, 3582, 3583, 3584, 3585, 3586, 3587, 3588, 3613, 3614, 3615, 3616, 3617, 3618, 3619, 3620, 3621, 3622, 3623, 3624, 3625, 3626, 3627, 3628, 3629, 3630, 3631, 3632, 3633, 3634, 3635, 3636, 3637, 3638, 3639, 3640, 3641, 3642, 3643, 3644, 3645, 3646, 3647, 3648, 3649, 3650, 3651, 3652, 3653, 3654, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662, 3692, 3693, 3694, 3695, 3696, 3697, 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, 3708, 3709, 3710, 3711, 3712, 3713, 3714, 3715, 3716, 3717, 3718, 3719, 3720, 3721, 3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733, 3734, 3735, 3736, 3737, 3738, 3739, 3740, 3741, 3764, 3765, 3766, 3767, 3768, 3769, 3770, 3771, 3772, 3773, 3774, 3775, 3776, 3777, 3778, 3779, 3780, 3781, 3782, 3783, 3784, 3785, 3786, 3787, 3788, 3789, 3790, 3791, 3792, 3793, 3794, 3795, 3796, 3797, 3798, 3799, 3800, 3801, 3802, 3803, 3804, 3805, 3806, 3807, 3808, 3809, 3810, 3811, 3812, 3813, 3836, 3837, 3838, 3839, 3840, 3841, 3842, 3843, 3844, 3845, 3846, 3847, 3848, 3849, 3850, 3851, 3852, 3853, 3854, 3855, 3856, 3857, 3858, 3859, 3860, 3861, 3862, 3863, 3864, 3865, 3866, 3867, 3868, 3869, 3870, 3871, 3872, 3873, 3874, 3875, 3876, 3877, 3878, 3879, 3880, 3881, 3882, 3883, 3884, 3885, 3911, 3912, 3913, 3914, 3915, 3916, 3917, 3918, 3919, 3920, 3921, 3922, 3923, 3924, 3925, 3926, 3927, 3928, 3929, 3930, 3931, 3932, 3933, 3934, 3935, 3936, 3937, 3938, 3939, 3940, 3941, 3942, 3943, 3944, 3945, 3946, 3947, 3948, 3949, 3950, 3951, 3952, 3953, 3954, 3955, 3956, 3957, 3958, 3959, 3960, 3986, 3987, 3988, 3989, 3990, 3991, 3992, 3993, 3994, 3995, 3996, 3997, 3998, 3999, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 4022, 4023, 4024, 4025, 4026, 4027, 4028, 4029, 4030, 4031, 4032, 4033, 4034, 4035, 4058, 4059, 4060, 4061, 4062, 4063, 4064, 4065, 4066, 4067, 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, 4076, 4077, 4078, 4079, 4080, 4081, 4082, 4083, 4084, 4085, 4086, 4087, 4088, 4089, 4090, 4091, 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4130, 4131, 4132, 4133, 4134, 4135, 4136, 4137, 4138, 4139, 4140, 4141, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4160, 4161, 4162, 4163, 4164, 4165, 4166, 4167, 4168, 4169, 4170, 4171, 4172, 4173, 4174, 4175, 4176, 4177, 4178, 4179, 4214, 4215, 4216, 4217, 4218, 4219, 4220, 4221, 4222, 4223, 4224, 4225, 4226, 4227, 4228, 4229, 4230, 4231, 4232, 4233, 4234, 4235, 4236, 4237, 4238, 4239, 4240, 4241, 4242, 4243, 4244, 4245, 4246, 4247, 4248, 4249, 4250, 4251, 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259, 4260, 4261, 4262, 4263, 4284, 4285, 4286, 4287, 4288, 4289, 4290, 4291, 4292, 4293, 4294, 4295, 4296, 4297, 4298, 4299, 4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4312, 4313, 4314, 4315, 4316, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, 4326, 4327, 4328, 4329, 4330, 4331, 4332, 4333, 4350, 4351, 4352, 4353, 4354, 4355, 4356, 4357, 4358, 4359, 4360, 4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, 4371, 4372, 4373, 4374, 4375, 4376, 4377, 4378, 4379, 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 4421, 4422, 4423, 4424, 4425, 4426, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435, 4436, 4437, 4438, 4439, 4440, 4441, 4442, 4443, 4444, 4445, 4446, 4447, 4448, 4449, 4450, 4451, 4452, 4453, 4454, 4455, 4476, 4477, 4478, 4479, 4480, 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4523, 4524, 4525, 4526, 4527, 4528, 4529, 4530, 4531, 4555, 4581, 4582, 4621, 4622, 4623, 4624, 4625, 4626, 4627, 4628, 4629, 4630, 4631, 4632, 4633, 4634, 4635, 4636, 4656, 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, 4705, 4730, 4756, 4757, 4758, 4791, 4792, 4793, 4794, 4795, 4895, 4896, 4926, 4943, 4944, 4981, 5060, 5061, 5062, 5092, 5180, 5181, 5182, 5183, 5184, 5185, 5186, 5187, 5188, 5225, 5226, 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, 5235, 5236, 5237, 5238, 5239, 5240, 5241, 5242, 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5260, 5261, 5262, 5263, 5264, 5265, 5266, 5267, 5268, 5269, 5270, 5271, 5272, 5273, 5274, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 5349, 5350, 5351, 5352, 5353, 5380, 5381, 5382, 5383, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5421, 5422, 5423, 5424, 5425, 5426, 5427, 5428, 5429, 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, 5471, 5472, 5473, 5474, 5475, 5476, 5477, 5478, 5479, 5480, 5481, 5482, 5483, 5484, 5485, 5486, 5487, 5488, 5489, 5490, 5491, 5492, 5493, 5494, 5495, 5496, 5497, 5498, 5499, 5500, 5501, 5502, 5503, 5504, 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5534, 5535, 5536, 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, 5551, 5552, 5553, 5554, 5555, 5556, 5557, 5558, 5559, 5560, 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5570, 5571, 5572, 5573, 5574, 5591, 5592, 5593, 5594, 5595, 5596, 5597, 5598, 5599, 5600, 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5612, 5613, 5614, 5615, 5616, 5617, 5618, 5619, 5620, 5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629, 5630, 5631, 5632, 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5665, 5666, 5667, 5668, 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, 5681, 5682, 5683, 5684, 5685, 5686, 5687, 5688, 5689, 5690, 5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, 5701, 5702, 5703, 5704, 5740, 5741, 5801, 5802, 5803, 5804, 5805, 5806, 5857, 5858, 5859, 5860, 5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, 5873, 5874, 5875, 5876, 5877, 5878, 5879, 5880, 5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, 5889, 5890, 5891, 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5949, 5950, 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, 5969, 5970, 5971, 5972, 5973, 5974, 5975, 5976, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, 6090, 6091, 6092, 6093, 6094, 6095, 6111, 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6119, 6120, 6121, 6122, 6123, 6124, 6125, 6126, 6127, 6128, 6129, 6130, 6131, 6132, 6133, 6134, 6135, 6136, 6167, 6168, 6169, 6170, 6171, 6172, 6173, 6174, 6175, 6176, 6177, 6178, 6179, 6180, 6181, 6182, 6183, 6184];
                  }
              }// SPDX-License-Identifier: MIT
              // Copyright (c) 2023 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {AccessControlEnumerable} from "../utils/AccessControlEnumerable.sol";
              import {ERC721A, ERC721ACommon} from "./ERC721ACommon.sol";
              /**
               * @notice ERC721 extension that implements a commonly used _baseURI() function
               * to return an URL prefix that can be set by the contract steerer.
               */
              contract BaseTokenURI is AccessControlEnumerable {
                  /**
                   * @notice Base token URI used as a prefix by tokenURI().
                   */
                  string private _baseTokenURI;
                  constructor(string memory baseTokenURI_) {
                      _setBaseTokenURI(baseTokenURI_);
                  }
                  /**
                   * @notice Sets the base token URI prefix.
                   * @dev Only callable by the contract steerer.
                   */
                  function setBaseTokenURI(string memory baseTokenURI_)
                      public
                      onlyRole(DEFAULT_STEERING_ROLE)
                  {
                      _setBaseTokenURI(baseTokenURI_);
                  }
                  /**
                   * @notice Sets the base token URI prefix.
                   */
                  function _setBaseTokenURI(string memory baseTokenURI_) internal virtual {
                      _baseTokenURI = baseTokenURI_;
                  }
                  /**
                   * @notice Returns the `baseTokenURI`.
                   */
                  function baseTokenURI() public view virtual returns (string memory) {
                      return _baseTokenURI;
                  }
                  /**
                   * @notice Returns the base token URI * without any additional characters (e.g. a slash).
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return _baseTokenURI;
                  }
              }
              /**
               * @notice ERC721ACommon extension that adds BaseTokenURI.
               */
              abstract contract ERC721ACommonBaseTokenURI is ERC721ACommon, BaseTokenURI {
                  /**
                   * @notice Overrides supportsInterface as required by inheritance.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721ACommon, AccessControlEnumerable)
                      returns (bool)
                  {
                      return
                          ERC721ACommon.supportsInterface(interfaceId) ||
                          AccessControlEnumerable.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Inheritance resolution.
                   */
                  function _baseURI()
                      internal
                      view
                      virtual
                      override(ERC721A, BaseTokenURI)
                      returns (string memory)
                  {
                      return BaseTokenURI._baseURI();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../token/ERC721/extensions/IERC721Metadata.sol";
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @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] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (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
                   * ====
                   *
                   * [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 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
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {Address} from "@openzeppelin/contracts/utils/Address.sol";
              import {DefaultOperatorFiltererUpgradeable} from
                  "operator-filter-registry/src/upgradeable/DefaultOperatorFiltererUpgradeable.sol";
              import {MythicsV1} from "./MythicsV1.sol";
              /**
               * @title Mythics V2
               * @notice Adding capabilities to interact with the operator filter registry.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsV2 is MythicsV1 {
                  using Address for address;
                  /**
                   * @notice Calling the operator filter registry with given calldata.
                   * @dev The registry contract did not foresee role-based contract access
                   * control -- only the contract itself, or its (EIP-173) owner is allowed to
                   * change subscription settings. To work around this, we enforce
                   * authorisation here and forward arbitrary calldata to the registry.
                   * Use with care!
                   */
                  function callOperatorFilterRegistry(bytes calldata cdata)
                      external
                      onlyRole(DEFAULT_STEERING_ROLE)
                      returns (bytes memory)
                  {
                      return address(OPERATOR_FILTER_REGISTRY).functionCall(cdata);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright (c) 2023 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {AccessControlEnumerable as ACE} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
              contract AccessControlEnumerable is ACE {
                  /// @notice The default role intended to perform access-restricted actions.
                  /// @dev We are using this instead of DEFAULT_ADMIN_ROLE because the latter
                  /// is intended to grant/revoke roles and will be secured differently.
                  bytes32 public constant DEFAULT_STEERING_ROLE =
                      keccak256("DEFAULT_STEERING_ROLE");
                  /// @dev Overrides supportsInterface so that inheriting contracts can
                  /// reference this contract instead of OZ's version for further overrides.
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ACE)
                      returns (bool)
                  {
                      return ACE.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright (c) 2022 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {ERC721A} from "erc721a/contracts/ERC721A.sol";
              import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
              import {AccessControlEnumerable} from "../utils/AccessControlEnumerable.sol";
              import {AccessControlPausable} from "../utils/AccessControlPausable.sol";
              import {ERC4906} from "./ERC4906.sol";
              /**
              @notice An ERC721A contract with common functionality:
               - Pausable with toggling functions exposed to Owner only
               - ERC2981 royalties
               */
              contract ERC721ACommon is ERC721A, AccessControlPausable, ERC2981, ERC4906 {
                  constructor(
                      address admin,
                      address steerer,
                      string memory name,
                      string memory symbol,
                      address payable royaltyReciever,
                      uint96 royaltyBasisPoints
                  ) ERC721A(name, symbol) {
                      _setDefaultRoyalty(royaltyReciever, royaltyBasisPoints);
                      _grantRole(DEFAULT_ADMIN_ROLE, admin);
                      _grantRole(DEFAULT_STEERING_ROLE, steerer);
                  }
                  /// @notice Requires that the token exists.
                  modifier tokenExists(uint256 tokenId) {
                      require(ERC721A._exists(tokenId), "ERC721ACommon: Token doesn't exist");
                      _;
                  }
                  /// @notice Requires that msg.sender owns or is approved for the token.
                  modifier onlyApprovedOrOwner(uint256 tokenId) {
                      require(
                          _ownershipOf(tokenId).addr == _msgSender() ||
                              getApproved(tokenId) == _msgSender(),
                          "ERC721ACommon: Not approved nor owner"
                      );
                      _;
                  }
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual override {
                      require(!paused(), "ERC721ACommon: paused");
                      super._beforeTokenTransfers(from, to, startTokenId, quantity);
                  }
                  /// @notice Overrides supportsInterface as required by inheritance.
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721A, AccessControlEnumerable, ERC2981, ERC4906)
                      returns (bool)
                  {
                      return
                          ERC721A.supportsInterface(interfaceId) ||
                          ERC2981.supportsInterface(interfaceId) ||
                          AccessControlEnumerable.supportsInterface(interfaceId) ||
                          ERC4906.supportsInterface(interfaceId);
                  }
                  /// @notice Sets the royalty receiver and percentage (in units of basis
                  /// points = 0.01%).
                  function setDefaultRoyalty(address receiver, uint96 basisPoints)
                      public
                      virtual
                      onlyRole(DEFAULT_STEERING_ROLE)
                  {
                      _setDefaultRoyalty(receiver, basisPoints);
                  }
                  function emitMetadataUpdateForAll()
                      external
                      onlyRole(DEFAULT_STEERING_ROLE)
                  {
                      // EIP4906 is unfortunately quite vague on whether the `toTokenId` in
                      // the following event is included or not. We hence use `totalSupply()`
                      // to ensure that the last actual `tokenId` is included in any case.
                      _refreshMetadata(0, totalSupply());
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721Metadata is IERC721 {
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(
                      uint256 x,
                      uint256 y,
                      uint256 denominator
                  ) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1);
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(
                      uint256 x,
                      uint256 y,
                      uint256 denominator,
                      Rounding rounding
                  ) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10**64) {
                              value /= 10**64;
                              result += 64;
                          }
                          if (value >= 10**32) {
                              value /= 10**32;
                              result += 32;
                          }
                          if (value >= 10**16) {
                              value /= 10**16;
                              result += 16;
                          }
                          if (value >= 10**8) {
                              value /= 10**8;
                              result += 8;
                          }
                          if (value >= 10**4) {
                              value /= 10**4;
                              result += 4;
                          }
                          if (value >= 10**2) {
                              value /= 10**2;
                              result += 2;
                          }
                          if (value >= 10**1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              import {OperatorFiltererUpgradeable} from "./OperatorFiltererUpgradeable.sol";
              import {CANONICAL_CORI_SUBSCRIPTION} from "../lib/Constants.sol";
              /**
               * @title  DefaultOperatorFiltererUpgradeable
               * @notice Inherits from OperatorFiltererUpgradeable and automatically subscribes to the default OpenSea subscription
               *         when the init function is called.
               */
              abstract contract DefaultOperatorFiltererUpgradeable is OperatorFiltererUpgradeable {
                  /// @dev The upgradeable initialize function that should be called when the contract is being deployed.
                  function __DefaultOperatorFilterer_init() internal onlyInitializing {
                      OperatorFiltererUpgradeable.__OperatorFilterer_init(CANONICAL_CORI_SUBSCRIPTION, true);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {DefaultOperatorFiltererUpgradeable} from
                  "operator-filter-registry/src/upgradeable/DefaultOperatorFiltererUpgradeable.sol";
              import {MythicsBase} from "./MythicsBase.sol";
              import {
                  BaseSellableUpgradeable,
                  AccessControlEnumerableUpgradeable,
                  SteerableAccessControlEnumerableUpgradeable
              } from "./BaseSellableUpgradeable.sol";
              import {Oddsoleum} from "../Oddsoleum/Oddsoleum.sol";
              import {MythicsEggRedeemer, MythicsEggRedemptionLib} from "./MythicsEggRedeemer.sol";
              import {MythicEggSampler} from "../Egg/MythicEggSampler.sol";
              library MythicsStorage {
                  /**
                   * @notice Encodes an open choice that the stored user can submit for a given purchase ID.
                   * @dev A zero-address as `chooser` indicates that this choice is no longer available.
                   * @param chooser the address of the user that is eligible to submit the choice.
                   * @param numChoices the number of choices.
                   */
                  struct OpenChoice {
                      address chooser;
                      uint8 numChoices;
                  }
                  /**
                   * @notice Encodes a final choice for a given mythics token ID.
                   * @param numChoicesMinusOne the number of choices minus one (optimisation).
                   * @param choice the choice submitted by the user
                   */
                  struct FinalChoice {
                      uint8 numChoicesMinusOne;
                      uint8 choice;
                  }
                  bytes32 internal constant STORAGE_SLOT = keccak256("Mythics.storage.location");
                  /**
                   * @notice This is the storage layout for the Mythics contract.
                   * @dev The fields in this struct MUST NOT be removed, renamed, or reordered. Only additionas are allowed to keep
                   * the storage layout compatible between upgrades.
                   */
                  struct Layout {
                      /**
                       * @notice The number of tokens that have been minted.
                       */
                      uint256 numMinted;
                      /**
                       * @notice The base URI for the token metadata.
                       */
                      string baseTokenURI;
                      /**
                       * @notice The number of purchases handled by the seller interface.
                       * @dev Counter to get sequential purchaseIDs
                       */
                      uint64 numPurchases;
                      /**
                       * @notice The MythicsEggRedeemer contract.
                       */
                      MythicsEggRedeemer eggRedeemer;
                      /**
                       * @notice Open choices added on purchase.
                       * @dev Elements MUST be deleted after locking in a choice.
                       */
                      mapping(uint256 purchaseId => OpenChoice) openChoices;
                      /**
                       * @notice Locked-in choices for the given tokenId.
                       * @dev Elements MUST and MUST ONLY be added for minted tokens.
                       */
                      mapping(uint256 tokenId => FinalChoice) finalChoices;
                  }
                  function layout() internal pure returns (Layout storage l) {
                      bytes32 slot = STORAGE_SLOT;
                      assembly {
                          l.slot := slot
                      }
                  }
              }
              interface MythicsV1Events {
                  /**
                   * @notice Emitted when a Mythic is purchased (not minted) to draw random choices for the buyer off-chain.
                   */
                  event RandomiseMythics(uint256 indexed purchaseId, address indexed chooser, uint8 indexed numChoices);
                  /**
                   * @notice Emitted when the Mythics choice was locked in for a given purchase (and the Mythic was minted).
                   */
                  event MythicChosen(uint256 indexed purchaseId, address indexed chooser, uint256 indexed tokenId, uint8 choice);
              }
              /**
               * @title Mythics V1
               * @notice Mythics V1 allowing tokens to be purchased by redeeming MythicEgss through the MythicsEggRedeemer or by
               * burning Oddities through the Oddsoleum.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsV1 is MythicsBase, BaseSellableUpgradeable, DefaultOperatorFiltererUpgradeable, MythicsV1Events {
                  /**
                   * @notice Thrown if a user attempt to lock in a choice that exceeds the number of available choices.
                   */
                  error ChoiceOutOfBounds(uint256 purchaseId, uint8 numChoices, uint8 choice);
                  /**
                   * @notice Thrown if a user attempts to submit a choice for a purchase that was already locked in.
                   */
                  error PurchaseChoiceAlreadyLockedIn(uint256 purchaseId);
                  /**
                   * @notice Thrown if the caller attempts to lock in an open choice they are not eligible for.
                   */
                  error CallerIsNotChooser(uint256 purchaseId, address chooser, address caller);
                  struct InitArgsV1 {
                      address mainAdmin;
                      address secondaryAdmin;
                      address steerer;
                      string baseTokenURI;
                      MythicsEggRedeemer eggRedeemer;
                      address royaltyReceiver;
                  }
                  function initializeV1(InitArgsV1 memory init) public virtual reinitializer(2) {
                      __MythicsBase_init();
                      __BaseSellable_init();
                      __DefaultOperatorFilterer_init();
                      _grantRole(DEFAULT_ADMIN_ROLE, init.mainAdmin);
                      _grantRole(DEFAULT_ADMIN_ROLE, init.secondaryAdmin);
                      _changeAdmin(init.mainAdmin);
                      _grantRole(DEFAULT_STEERING_ROLE, init.steerer);
                      MythicsStorage.layout().baseTokenURI = init.baseTokenURI;
                      MythicsStorage.layout().eggRedeemer = init.eggRedeemer;
                      _setDefaultRoyalty(init.royaltyReceiver, 500);
                  }
                  /**
                   * @notice Pauses all ERC721 transfers.
                   * @dev This includes mints and hence also purchases throught the Seller interface.
                   */
                  function pause() public virtual onlyRole(DEFAULT_STEERING_ROLE) {
                      _pause();
                  }
                  /**
                   * @notice Unpauses paused ERC721 transfers.
                   */
                  function unpause() public virtual onlyRole(DEFAULT_STEERING_ROLE) {
                      _unpause();
                  }
                  /**
                   * @notice Loads and returns the base URI for the token metadata according to the v1 storage layout.
                   */
                  function _baseURI() internal view virtual override returns (string memory) {
                      return MythicsStorage.layout().baseTokenURI;
                  }
                  /**
                   * @notice Sets the base tokenURI.
                   */
                  function setBaseTokenURI(string calldata newBaseTokenURI) public virtual onlyRole(DEFAULT_STEERING_ROLE) {
                      MythicsStorage.layout().baseTokenURI = newBaseTokenURI;
                  }
                  /**
                   * @notice Returns the total supply of Mythics.
                   */
                  function totalSupply() public view virtual returns (uint256) {
                      return MythicsStorage.layout().numMinted;
                  }
                  /**
                   * @notice Overriding the OZ's minting hook to increment the numMinted counter.
                   * @dev Must not be called directly as this would mess with sequential token IDs. Use _mintNextTokenId instead.
                   */
                  function _mint(address to, uint256 tokenId) internal virtual override {
                      unchecked {
                          // Impossible to overflow in practice, the amount of gas required to mint more than 2**256 is prohibitive.
                          MythicsStorage.layout().numMinted++;
                      }
                      super._mint(to, tokenId);
                  }
                  /**
                   * @notice Convenience function to mint the next tokenId.
                   */
                  function _mintNextTokenId(address to) internal virtual returns (uint256) {
                      uint256 tokenId = MythicsStorage.layout().numMinted;
                      _mint(to, tokenId);
                      return tokenId;
                  }
                  /**
                   * @notice Tracks purchases and emits an event to randomise mythic choices off-chain.
                   */
                  function _trackPurchaseAndRandomiseMythics(address chooser, uint8 numChoices) internal returns (uint256) {
                      assert(numChoices > 0);
                      MythicsStorage.Layout storage layout = MythicsStorage.layout();
                      uint256 purchaseId = layout.numPurchases++;
                      // Optimisation: Not tracking the purchase in `openChoices` for `numChoices == 1` since there is only one choice
                      // and we will lock it in immediately after this call in `_setChoice` deleting the entry again.
                      if (numChoices > 1) {
                          layout.openChoices[purchaseId] = MythicsStorage.OpenChoice({chooser: chooser, numChoices: numChoices});
                      }
                      emit RandomiseMythics(purchaseId, chooser, numChoices);
                      return purchaseId;
                  }
                  /**
                   * @notice Locks in a choice for a given purchase and mythic token ID.
                   */
                  function _setChoice(address chooser, uint256 purchaseId, uint256 tokenId, uint8 numChoices, uint8 choice)
                      internal
                  {
                      assert(numChoices > 0);
                      MythicsStorage.Layout storage layout = MythicsStorage.layout();
                      if (choice >= numChoices) {
                          revert ChoiceOutOfBounds(purchaseId, numChoices, choice);
                      }
                      if (numChoices > 1) {
                          // Optimisation: Not deleting `openChoices` for `numChoices == 1` since we did not store anything in that
                          // case. See also `_trackPurchaseAndRandomiseMythics`.
                          delete layout.openChoices[purchaseId];
                      }
                      layout.finalChoices[tokenId] = MythicsStorage.FinalChoice({numChoicesMinusOne: numChoices - 1, choice: choice});
                      emit MythicChosen(purchaseId, chooser, tokenId, choice);
                  }
                  /**
                   * @notice Convenience wrapper functions for purchases without choices, i.e. `numChoices = 1`, e.g. used for
                   * Oddities and stone eggs.
                   */
                  function _mintWithoutChoice(address to) internal {
                      uint8 numChoices = 1;
                      uint256 purchaseId = _trackPurchaseAndRandomiseMythics(to, numChoices);
                      uint256 tokenId = _mintNextTokenId(to);
                      _setChoice(to, purchaseId, tokenId, numChoices, 0);
                  }
                  /**
                   * @notice Encode as choice submission.
                   * @param purchaseId the purchase ID the choice should be submitted for
                   * @param choice the chosen Mythic for the given purchase ID
                   */
                  struct ChoiceSubmission {
                      uint256 purchaseId;
                      uint8 choice;
                  }
                  /**
                   * @notice Locks in the choice for a given purchase with an open choice and mints the Mythics token.
                   * @dev Reverts if the caller is not the stored chooser or if a choice was already submitted for the given purchase.
                   */
                  function _chooseAndMint(ChoiceSubmission calldata submission) internal {
                      MythicsStorage.Layout storage layout = MythicsStorage.layout();
                      MythicsStorage.OpenChoice memory openChoice = layout.openChoices[submission.purchaseId];
                      if (openChoice.chooser == address(0)) {
                          revert PurchaseChoiceAlreadyLockedIn(submission.purchaseId);
                      }
                      if (msg.sender != openChoice.chooser) {
                          revert CallerIsNotChooser(submission.purchaseId, openChoice.chooser, msg.sender);
                      }
                      uint8 numChoices = openChoice.numChoices;
                      uint256 tokenId = _mintNextTokenId(msg.sender);
                      _setChoice(msg.sender, submission.purchaseId, tokenId, numChoices, submission.choice);
                  }
                  /**
                   * @notice Locks in choices for given purchases and mints the Mythics tokens.
                   */
                  function chooseAndMint(ChoiceSubmission[] calldata choices) public whenNotPaused {
                      for (uint256 i; i < choices.length; ++i) {
                          _chooseAndMint(choices[i]);
                      }
                  }
                  /**
                   * @inheritdoc BaseSellableUpgradeable
                   * @dev Mints Mythics tokens for standard sales (e.g. through the Oddsoleum) or stone egg redemptions or records
                   * open choices for non-stone eggs.
                   */
                  function _handleSale(address to, uint64 num, bytes calldata data) internal virtual override whenNotPaused {
                      if (msg.sender != address(MythicsStorage.layout().eggRedeemer)) {
                          for (uint256 i; i < num; ++i) {
                              _mintWithoutChoice(to);
                          }
                          return;
                      }
                      MythicsEggRedemptionLib.PurchasePayload[] memory payloads = MythicsEggRedemptionLib.decode(data);
                      assert(num == payloads.length);
                      for (uint256 i; i < num; ++i) {
                          MythicEggSampler.EggType eggType = payloads[i].eggType;
                          if (eggType == MythicEggSampler.EggType.Stone) {
                              _mintWithoutChoice(to);
                              continue;
                          }
                          assert(uint8(eggType) <= 2);
                          uint8 numChoices = uint8(eggType) + 1;
                          _trackPurchaseAndRandomiseMythics(to, numChoices);
                      }
                  }
                  /**
                   * @notice Sets the egg redeemer contract.
                   */
                  function setEggRedeemer(MythicsEggRedeemer newRedeemer) public onlyRole(DEFAULT_STEERING_ROLE) {
                      MythicsStorage.layout().eggRedeemer = newRedeemer;
                  }
                  /**
                   * @notice Returns the egg redeemer contract.
                   */
                  function eggRedeemer() public view returns (MythicsEggRedeemer) {
                      return MythicsStorage.layout().eggRedeemer;
                  }
                  /**
                   * @notice Sets the default royalty receiver and fee in basis points.
                   */
                  function setDefaultRoyalty(address receiver, uint96 feeBasisPoints)
                      public
                      virtual
                      onlyRole(DEFAULT_STEERING_ROLE)
                  {
                      _setDefaultRoyalty(receiver, feeBasisPoints);
                  }
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(MythicsBase, AccessControlEnumerableUpgradeable)
                      returns (bool)
                  {
                      return MythicsBase.supportsInterface(interfaceId);
                  }
                  // =========================================================================
                  //                           Operator filtering
                  // =========================================================================
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
                   */
                  function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) {
                      super.setApprovalForAll(operator, approved);
                  }
                  /**
                   * @dev See {IERC721-approve}.
                   *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
                   */
                  function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) {
                      super.approve(operator, tokenId);
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
                      super.transferFrom(from, to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   *      In this example the added modifier ensures that the operator is allowed by the OperatorFilterRegistry.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
                      public
                      override
                      onlyAllowedOperator(from)
                  {
                      super.safeTransferFrom(from, to, tokenId, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControlEnumerable.sol";
              import "./AccessControl.sol";
              import "../utils/structs/EnumerableSet.sol";
              /**
               * @dev Extension of {AccessControl} that allows enumerating the members of each role.
               */
              abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
                  using EnumerableSet for EnumerableSet.AddressSet;
                  mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @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
                   * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
                   * for more information.
                   */
                  function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
                      return _roleMembers[role].at(index);
                  }
                  /**
                   * @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) public view virtual override returns (uint256) {
                      return _roleMembers[role].length();
                  }
                  /**
                   * @dev Overload {_grantRole} to track enumerable memberships
                   */
                  function _grantRole(bytes32 role, address account) internal virtual override {
                      super._grantRole(role, account);
                      _roleMembers[role].add(account);
                  }
                  /**
                   * @dev Overload {_revokeRole} to track enumerable memberships
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual override {
                      super._revokeRole(role, account);
                      _roleMembers[role].remove(account);
                  }
              }
              // SPDX-License-Identifier: MIT
              // ERC721A Contracts v4.2.3
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              import './IERC721A.sol';
              /**
               * @dev Interface of ERC721 token receiver.
               */
              interface ERC721A__IERC721Receiver {
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              /**
               * @title ERC721A
               *
               * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
               * Non-Fungible Token Standard, including the Metadata extension.
               * Optimized for lower gas during batch mints.
               *
               * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
               * starting from `_startTokenId()`.
               *
               * Assumptions:
               *
               * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
               * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
               */
              contract ERC721A is IERC721A {
                  // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
                  struct TokenApprovalRef {
                      address value;
                  }
                  // =============================================================
                  //                           CONSTANTS
                  // =============================================================
                  // Mask of an entry in packed address data.
                  uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
                  // The bit position of `numberMinted` in packed address data.
                  uint256 private constant _BITPOS_NUMBER_MINTED = 64;
                  // The bit position of `numberBurned` in packed address data.
                  uint256 private constant _BITPOS_NUMBER_BURNED = 128;
                  // The bit position of `aux` in packed address data.
                  uint256 private constant _BITPOS_AUX = 192;
                  // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
                  uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
                  // The bit position of `startTimestamp` in packed ownership.
                  uint256 private constant _BITPOS_START_TIMESTAMP = 160;
                  // The bit mask of the `burned` bit in packed ownership.
                  uint256 private constant _BITMASK_BURNED = 1 << 224;
                  // The bit position of the `nextInitialized` bit in packed ownership.
                  uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
                  // The bit mask of the `nextInitialized` bit in packed ownership.
                  uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
                  // The bit position of `extraData` in packed ownership.
                  uint256 private constant _BITPOS_EXTRA_DATA = 232;
                  // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
                  uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
                  // The mask of the lower 160 bits for addresses.
                  uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
                  // The maximum `quantity` that can be minted with {_mintERC2309}.
                  // This limit is to prevent overflows on the address data entries.
                  // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
                  // is required to cause an overflow, which is unrealistic.
                  uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
                  // The `Transfer` event signature is given by:
                  // `keccak256(bytes("Transfer(address,address,uint256)"))`.
                  bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
                      0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
                  // =============================================================
                  //                            STORAGE
                  // =============================================================
                  // The next token ID to be minted.
                  uint256 private _currentIndex;
                  // The number of tokens burned.
                  uint256 private _burnCounter;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to ownership details
                  // An empty struct value does not necessarily mean the token is unowned.
                  // See {_packedOwnershipOf} implementation for details.
                  //
                  // Bits Layout:
                  // - [0..159]   `addr`
                  // - [160..223] `startTimestamp`
                  // - [224]      `burned`
                  // - [225]      `nextInitialized`
                  // - [232..255] `extraData`
                  mapping(uint256 => uint256) private _packedOwnerships;
                  // Mapping owner address to address data.
                  //
                  // Bits Layout:
                  // - [0..63]    `balance`
                  // - [64..127]  `numberMinted`
                  // - [128..191] `numberBurned`
                  // - [192..255] `aux`
                  mapping(address => uint256) private _packedAddressData;
                  // Mapping from token ID to approved address.
                  mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
                  // Mapping from owner to operator approvals
                  mapping(address => mapping(address => bool)) private _operatorApprovals;
                  // =============================================================
                  //                          CONSTRUCTOR
                  // =============================================================
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                      _currentIndex = _startTokenId();
                  }
                  // =============================================================
                  //                   TOKEN COUNTING OPERATIONS
                  // =============================================================
                  /**
                   * @dev Returns the starting token ID.
                   * To change the starting token ID, please override this function.
                   */
                  function _startTokenId() internal view virtual returns (uint256) {
                      return 0;
                  }
                  /**
                   * @dev Returns the next token ID to be minted.
                   */
                  function _nextTokenId() internal view virtual returns (uint256) {
                      return _currentIndex;
                  }
                  /**
                   * @dev Returns the total number of tokens in existence.
                   * Burned tokens will reduce the count.
                   * To get the total number of tokens minted, please see {_totalMinted}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      // Counter underflow is impossible as _burnCounter cannot be incremented
                      // more than `_currentIndex - _startTokenId()` times.
                      unchecked {
                          return _currentIndex - _burnCounter - _startTokenId();
                      }
                  }
                  /**
                   * @dev Returns the total amount of tokens minted in the contract.
                   */
                  function _totalMinted() internal view virtual returns (uint256) {
                      // Counter underflow is impossible as `_currentIndex` does not decrement,
                      // and it is initialized to `_startTokenId()`.
                      unchecked {
                          return _currentIndex - _startTokenId();
                      }
                  }
                  /**
                   * @dev Returns the total number of tokens burned.
                   */
                  function _totalBurned() internal view virtual returns (uint256) {
                      return _burnCounter;
                  }
                  // =============================================================
                  //                    ADDRESS DATA OPERATIONS
                  // =============================================================
                  /**
                   * @dev Returns the number of tokens in `owner`'s account.
                   */
                  function balanceOf(address owner) public view virtual override returns (uint256) {
                      if (owner == address(0)) revert BalanceQueryForZeroAddress();
                      return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
                  }
                  /**
                   * Returns the number of tokens minted by `owner`.
                   */
                  function _numberMinted(address owner) internal view returns (uint256) {
                      return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
                  }
                  /**
                   * Returns the number of tokens burned by or on behalf of `owner`.
                   */
                  function _numberBurned(address owner) internal view returns (uint256) {
                      return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
                  }
                  /**
                   * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
                   */
                  function _getAux(address owner) internal view returns (uint64) {
                      return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
                  }
                  /**
                   * Sets the auxiliary 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 virtual {
                      uint256 packed = _packedAddressData[owner];
                      uint256 auxCasted;
                      // Cast `aux` with assembly to avoid redundant masking.
                      assembly {
                          auxCasted := aux
                      }
                      packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
                      _packedAddressData[owner] = packed;
                  }
                  // =============================================================
                  //                            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 30000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      // The interface IDs are constants representing the first 4 bytes
                      // of the XOR of all function selectors in the interface.
                      // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
                      // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
                      return
                          interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                          interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                          interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
                  }
                  // =============================================================
                  //                        IERC721Metadata
                  // =============================================================
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  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, _toString(tokenId))) : '';
                  }
                  /**
                   * @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, it can be overridden in child contracts.
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return '';
                  }
                  // =============================================================
                  //                     OWNERSHIPS OPERATIONS
                  // =============================================================
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                      return address(uint160(_packedOwnershipOf(tokenId)));
                  }
                  /**
                   * @dev Gas spent here starts off proportional to the maximum mint batch size.
                   * It gradually moves to O(1) as tokens get transferred around over time.
                   */
                  function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
                      return _unpackedOwnership(_packedOwnershipOf(tokenId));
                  }
                  /**
                   * @dev Returns the unpacked `TokenOwnership` struct at `index`.
                   */
                  function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
                      return _unpackedOwnership(_packedOwnerships[index]);
                  }
                  /**
                   * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
                   */
                  function _initializeOwnershipAt(uint256 index) internal virtual {
                      if (_packedOwnerships[index] == 0) {
                          _packedOwnerships[index] = _packedOwnershipOf(index);
                      }
                  }
                  /**
                   * Returns the packed ownership data of `tokenId`.
                   */
                  function _packedOwnershipOf(uint256 tokenId) private view returns (uint256) {
                      uint256 curr = tokenId;
                      unchecked {
                          if (_startTokenId() <= curr)
                              if (curr < _currentIndex) {
                                  uint256 packed = _packedOwnerships[curr];
                                  // If not burned.
                                  if (packed & _BITMASK_BURNED == 0) {
                                      // Invariant:
                                      // There will always be an initialized ownership slot
                                      // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                                      // before an unintialized ownership slot
                                      // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                                      // Hence, `curr` will not underflow.
                                      //
                                      // We can directly compare the packed value.
                                      // If the address is zero, packed will be zero.
                                      while (packed == 0) {
                                          packed = _packedOwnerships[--curr];
                                      }
                                      return packed;
                                  }
                              }
                      }
                      revert OwnerQueryForNonexistentToken();
                  }
                  /**
                   * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
                   */
                  function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
                      ownership.addr = address(uint160(packed));
                      ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
                      ownership.burned = packed & _BITMASK_BURNED != 0;
                      ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
                  }
                  /**
                   * @dev Packs ownership data into a single uint256.
                   */
                  function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
                      assembly {
                          // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          owner := and(owner, _BITMASK_ADDRESS)
                          // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                          result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
                      }
                  }
                  /**
                   * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
                   */
                  function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
                      // For branchless setting of the `nextInitialized` flag.
                      assembly {
                          // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                          result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
                      }
                  }
                  // =============================================================
                  //                      APPROVAL OPERATIONS
                  // =============================================================
                  /**
                   * @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) public payable virtual override {
                      address owner = ownerOf(tokenId);
                      if (_msgSenderERC721A() != owner)
                          if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                              revert ApprovalCallerNotOwnerNorApproved();
                          }
                      _tokenApprovals[tokenId].value = to;
                      emit Approval(owner, to, tokenId);
                  }
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) public view virtual override returns (address) {
                      if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
                      return _tokenApprovals[tokenId].value;
                  }
                  /**
                   * @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) public virtual override {
                      _operatorApprovals[_msgSenderERC721A()][operator] = approved;
                      emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
                  }
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @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. See {_mint}.
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool) {
                      return
                          _startTokenId() <= tokenId &&
                          tokenId < _currentIndex && // If within bounds,
                          _packedOwnerships[tokenId] & _BITMASK_BURNED == 0; // and not burned.
                  }
                  /**
                   * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
                   */
                  function _isSenderApprovedOrOwner(
                      address approvedAddress,
                      address owner,
                      address msgSender
                  ) private pure returns (bool result) {
                      assembly {
                          // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          owner := and(owner, _BITMASK_ADDRESS)
                          // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                          msgSender := and(msgSender, _BITMASK_ADDRESS)
                          // `msgSender == owner || msgSender == approvedAddress`.
                          result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
                      }
                  }
                  /**
                   * @dev Returns the storage slot and value for the approved address of `tokenId`.
                   */
                  function _getApprovedSlotAndAddress(uint256 tokenId)
                      private
                      view
                      returns (uint256 approvedAddressSlot, address approvedAddress)
                  {
                      TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
                      // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
                      assembly {
                          approvedAddressSlot := tokenApproval.slot
                          approvedAddress := sload(approvedAddressSlot)
                      }
                  }
                  // =============================================================
                  //                      TRANSFER OPERATIONS
                  // =============================================================
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *
                   * 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
                  ) public payable virtual override {
                      uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
                      if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();
                      (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                      // The nested ifs save around 20+ gas over a compound boolean condition.
                      if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                          if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                      if (to == address(0)) revert TransferToZeroAddress();
                      _beforeTokenTransfers(from, to, tokenId, 1);
                      // Clear approvals from the previous owner.
                      assembly {
                          if approvedAddress {
                              // This is equivalent to `delete _tokenApprovals[tokenId]`.
                              sstore(approvedAddressSlot, 0)
                          }
                      }
                      // 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 {
                          // We can directly increment and decrement the balances.
                          --_packedAddressData[from]; // Updates: `balance -= 1`.
                          ++_packedAddressData[to]; // Updates: `balance += 1`.
                          // Updates:
                          // - `address` to the next owner.
                          // - `startTimestamp` to the timestamp of transfering.
                          // - `burned` to `false`.
                          // - `nextInitialized` to `true`.
                          _packedOwnerships[tokenId] = _packOwnershipData(
                              to,
                              _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                          );
                          // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                          if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                              uint256 nextTokenId = tokenId + 1;
                              // If the next slot's address is zero and not burned (i.e. packed value is zero).
                              if (_packedOwnerships[nextTokenId] == 0) {
                                  // If the next slot is within bounds.
                                  if (nextTokenId != _currentIndex) {
                                      // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                                      _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                                  }
                              }
                          }
                      }
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfers(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public payable virtual override {
                      safeTransferFrom(from, to, tokenId, '');
                  }
                  /**
                   * @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 memory _data
                  ) public payable virtual override {
                      transferFrom(from, to, tokenId);
                      if (to.code.length != 0)
                          if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                              revert TransferToNonERC721ReceiverImplementer();
                          }
                  }
                  /**
                   * @dev Hook that is called before a set of serially-ordered token IDs
                   * are about to be transferred. This includes minting.
                   * And also called before burning one token.
                   *
                   * `startTokenId` - the first token ID to be transferred.
                   * `quantity` - the amount to be transferred.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
                   * transferred to `to`.
                   * - When `from` is zero, `tokenId` will be minted for `to`.
                   * - When `to` is zero, `tokenId` will be burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _beforeTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev Hook that is called after a set of serially-ordered token IDs
                   * have been transferred. This includes minting.
                   * And also called after one token has been burned.
                   *
                   * `startTokenId` - the first token ID to be transferred.
                   * `quantity` - the amount to be transferred.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
                   * transferred to `to`.
                   * - When `from` is zero, `tokenId` has been minted for `to`.
                   * - When `to` is zero, `tokenId` has been burned by `from`.
                   * - `from` and `to` are never both zero.
                   */
                  function _afterTokenTransfers(
                      address from,
                      address to,
                      uint256 startTokenId,
                      uint256 quantity
                  ) internal virtual {}
                  /**
                   * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
                   *
                   * `from` - Previous owner of the given token ID.
                   * `to` - Target address that will receive the token.
                   * `tokenId` - Token ID to be transferred.
                   * `_data` - Optional data to send along with the call.
                   *
                   * Returns whether the call correctly returned the expected magic value.
                   */
                  function _checkContractOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory _data
                  ) private returns (bool) {
                      try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
                          bytes4 retval
                      ) {
                          return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
                      } catch (bytes memory reason) {
                          if (reason.length == 0) {
                              revert TransferToNonERC721ReceiverImplementer();
                          } else {
                              assembly {
                                  revert(add(32, reason), mload(reason))
                              }
                          }
                      }
                  }
                  // =============================================================
                  //                        MINT OPERATIONS
                  // =============================================================
                  /**
                   * @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 for each mint.
                   */
                  function _mint(address to, uint256 quantity) internal virtual {
                      uint256 startTokenId = _currentIndex;
                      if (quantity == 0) revert MintZeroQuantity();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are incredibly unrealistic.
                      // `balance` and `numberMinted` have a maximum limit of 2**64.
                      // `tokenId` has a maximum limit of 2**256.
                      unchecked {
                          // Updates:
                          // - `balance += quantity`.
                          // - `numberMinted += quantity`.
                          //
                          // We can directly add to the `balance` and `numberMinted`.
                          _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                          // Updates:
                          // - `address` to the owner.
                          // - `startTimestamp` to the timestamp of minting.
                          // - `burned` to `false`.
                          // - `nextInitialized` to `quantity == 1`.
                          _packedOwnerships[startTokenId] = _packOwnershipData(
                              to,
                              _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                          );
                          uint256 toMasked;
                          uint256 end = startTokenId + quantity;
                          // Use assembly to loop and emit the `Transfer` event for gas savings.
                          // The duplicated `log4` removes an extra check and reduces stack juggling.
                          // The assembly, together with the surrounding Solidity code, have been
                          // delicately arranged to nudge the compiler into producing optimized opcodes.
                          assembly {
                              // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                              toMasked := and(to, _BITMASK_ADDRESS)
                              // Emit the `Transfer` event.
                              log4(
                                  0, // Start of data (0, since no data).
                                  0, // End of data (0, since no data).
                                  _TRANSFER_EVENT_SIGNATURE, // Signature.
                                  0, // `address(0)`.
                                  toMasked, // `to`.
                                  startTokenId // `tokenId`.
                              )
                              // The `iszero(eq(,))` check ensures that large values of `quantity`
                              // that overflows uint256 will make the loop run out of gas.
                              // The compiler will optimize the `iszero` away for performance.
                              for {
                                  let tokenId := add(startTokenId, 1)
                              } iszero(eq(tokenId, end)) {
                                  tokenId := add(tokenId, 1)
                              } {
                                  // Emit the `Transfer` event. Similar to above.
                                  log4(0, 0, _TRANSFER_EVENT_SIGNATURE, 0, toMasked, tokenId)
                              }
                          }
                          if (toMasked == 0) revert MintToZeroAddress();
                          _currentIndex = end;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, quantity);
                  }
                  /**
                   * @dev Mints `quantity` tokens and transfers them to `to`.
                   *
                   * This function is intended for efficient minting only during contract creation.
                   *
                   * It emits only one {ConsecutiveTransfer} as defined in
                   * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
                   * instead of a sequence of {Transfer} event(s).
                   *
                   * Calling this function outside of contract creation WILL make your contract
                   * non-compliant with the ERC721 standard.
                   * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
                   * {ConsecutiveTransfer} event is only permissible during contract creation.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `quantity` must be greater than 0.
                   *
                   * Emits a {ConsecutiveTransfer} event.
                   */
                  function _mintERC2309(address to, uint256 quantity) internal virtual {
                      uint256 startTokenId = _currentIndex;
                      if (to == address(0)) revert MintToZeroAddress();
                      if (quantity == 0) revert MintZeroQuantity();
                      if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) revert MintERC2309QuantityExceedsLimit();
                      _beforeTokenTransfers(address(0), to, startTokenId, quantity);
                      // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
                      unchecked {
                          // Updates:
                          // - `balance += quantity`.
                          // - `numberMinted += quantity`.
                          //
                          // We can directly add to the `balance` and `numberMinted`.
                          _packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                          // Updates:
                          // - `address` to the owner.
                          // - `startTimestamp` to the timestamp of minting.
                          // - `burned` to `false`.
                          // - `nextInitialized` to `quantity == 1`.
                          _packedOwnerships[startTokenId] = _packOwnershipData(
                              to,
                              _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                          );
                          emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                          _currentIndex = startTokenId + quantity;
                      }
                      _afterTokenTransfers(address(0), to, startTokenId, 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.
                   *
                   * See {_mint}.
                   *
                   * Emits a {Transfer} event for each mint.
                   */
                  function _safeMint(
                      address to,
                      uint256 quantity,
                      bytes memory _data
                  ) internal virtual {
                      _mint(to, quantity);
                      unchecked {
                          if (to.code.length != 0) {
                              uint256 end = _currentIndex;
                              uint256 index = end - quantity;
                              do {
                                  if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                                      revert TransferToNonERC721ReceiverImplementer();
                                  }
                              } while (index < end);
                              // Reentrancy protection.
                              if (_currentIndex != end) revert();
                          }
                      }
                  }
                  /**
                   * @dev Equivalent to `_safeMint(to, quantity, '')`.
                   */
                  function _safeMint(address to, uint256 quantity) internal virtual {
                      _safeMint(to, quantity, '');
                  }
                  // =============================================================
                  //                        BURN OPERATIONS
                  // =============================================================
                  /**
                   * @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 {
                      uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
                      address from = address(uint160(prevOwnershipPacked));
                      (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
                      if (approvalCheck) {
                          // The nested ifs save around 20+ gas over a compound boolean condition.
                          if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                              if (!isApprovedForAll(from, _msgSenderERC721A())) revert TransferCallerNotOwnerNorApproved();
                      }
                      _beforeTokenTransfers(from, address(0), tokenId, 1);
                      // Clear approvals from the previous owner.
                      assembly {
                          if approvedAddress {
                              // This is equivalent to `delete _tokenApprovals[tokenId]`.
                              sstore(approvedAddressSlot, 0)
                          }
                      }
                      // 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 {
                          // Updates:
                          // - `balance -= 1`.
                          // - `numberBurned += 1`.
                          //
                          // We can directly decrement the balance, and increment the number burned.
                          // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                          _packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
                          // Updates:
                          // - `address` to the last owner.
                          // - `startTimestamp` to the timestamp of burning.
                          // - `burned` to `true`.
                          // - `nextInitialized` to `true`.
                          _packedOwnerships[tokenId] = _packOwnershipData(
                              from,
                              (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                          );
                          // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                          if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                              uint256 nextTokenId = tokenId + 1;
                              // If the next slot's address is zero and not burned (i.e. packed value is zero).
                              if (_packedOwnerships[nextTokenId] == 0) {
                                  // If the next slot is within bounds.
                                  if (nextTokenId != _currentIndex) {
                                      // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                                      _packedOwnerships[nextTokenId] = prevOwnershipPacked;
                                  }
                              }
                          }
                      }
                      emit Transfer(from, address(0), tokenId);
                      _afterTokenTransfers(from, address(0), tokenId, 1);
                      // Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
                      unchecked {
                          _burnCounter++;
                      }
                  }
                  // =============================================================
                  //                     EXTRA DATA OPERATIONS
                  // =============================================================
                  /**
                   * @dev Directly sets the extra data for the ownership data `index`.
                   */
                  function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
                      uint256 packed = _packedOwnerships[index];
                      if (packed == 0) revert OwnershipNotInitializedForExtraData();
                      uint256 extraDataCasted;
                      // Cast `extraData` with assembly to avoid redundant masking.
                      assembly {
                          extraDataCasted := extraData
                      }
                      packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
                      _packedOwnerships[index] = packed;
                  }
                  /**
                   * @dev Called during each token transfer to set the 24bit `extraData` field.
                   * Intended to be overridden by the cosumer contract.
                   *
                   * `previousExtraData` - the value of `extraData` before transfer.
                   *
                   * 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 _extraData(
                      address from,
                      address to,
                      uint24 previousExtraData
                  ) internal view virtual returns (uint24) {}
                  /**
                   * @dev Returns the next extra data for the packed ownership data.
                   * The returned result is shifted into position.
                   */
                  function _nextExtraData(
                      address from,
                      address to,
                      uint256 prevOwnershipPacked
                  ) private view returns (uint256) {
                      uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
                      return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
                  }
                  // =============================================================
                  //                       OTHER OPERATIONS
                  // =============================================================
                  /**
                   * @dev Returns the message sender (defaults to `msg.sender`).
                   *
                   * If you are writing GSN compatible contracts, you need to override this function.
                   */
                  function _msgSenderERC721A() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  /**
                   * @dev Converts a uint256 to its ASCII string decimal representation.
                   */
                  function _toString(uint256 value) internal pure virtual returns (string memory str) {
                      assembly {
                          // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                          // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                          // We will need 1 word for the trailing zeros padding, 1 word for the length,
                          // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
                          let m := add(mload(0x40), 0xa0)
                          // Update the free memory pointer to allocate.
                          mstore(0x40, m)
                          // Assign the `str` to the end.
                          str := sub(m, 0x20)
                          // Zeroize the slot after the string.
                          mstore(str, 0)
                          // Cache the end of the memory to calculate the length later.
                          let end := str
                          // We write the string from rightmost digit to leftmost digit.
                          // The following is essentially a do-while loop that also handles the zero case.
                          // prettier-ignore
                          for { let temp := value } 1 {} {
                              str := sub(str, 1)
                              // Write the character to the pointer.
                              // The ASCII index of the '0' character is 48.
                              mstore8(str, add(48, mod(temp, 10)))
                              // Keep dividing `temp` until zero.
                              temp := div(temp, 10)
                              // prettier-ignore
                              if iszero(temp) { break }
                          }
                          let length := sub(end, str)
                          // Move the pointer 32 bytes leftwards to make room for the length.
                          str := sub(str, 0x20)
                          // Store the length.
                          mstore(str, length)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol)
              pragma solidity ^0.8.0;
              import "../../interfaces/IERC2981.sol";
              import "../../utils/introspection/ERC165.sol";
              /**
               * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
               *
               * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
               * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
               *
               * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
               * fee is specified in basis points by default.
               *
               * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
               * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
               * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
               *
               * _Available since v4.5._
               */
              abstract contract ERC2981 is IERC2981, ERC165 {
                  struct RoyaltyInfo {
                      address receiver;
                      uint96 royaltyFraction;
                  }
                  RoyaltyInfo private _defaultRoyaltyInfo;
                  mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
                      return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @inheritdoc IERC2981
                   */
                  function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
                      RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];
                      if (royalty.receiver == address(0)) {
                          royalty = _defaultRoyaltyInfo;
                      }
                      uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();
                      return (royalty.receiver, royaltyAmount);
                  }
                  /**
                   * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
                   * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
                   * override.
                   */
                  function _feeDenominator() internal pure virtual returns (uint96) {
                      return 10000;
                  }
                  /**
                   * @dev Sets the royalty information that all ids in this contract will default to.
                   *
                   * Requirements:
                   *
                   * - `receiver` cannot be the zero address.
                   * - `feeNumerator` cannot be greater than the fee denominator.
                   */
                  function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
                      require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                      require(receiver != address(0), "ERC2981: invalid receiver");
                      _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
                  }
                  /**
                   * @dev Removes default royalty information.
                   */
                  function _deleteDefaultRoyalty() internal virtual {
                      delete _defaultRoyaltyInfo;
                  }
                  /**
                   * @dev Sets the royalty information for a specific token id, overriding the global default.
                   *
                   * Requirements:
                   *
                   * - `receiver` cannot be the zero address.
                   * - `feeNumerator` cannot be greater than the fee denominator.
                   */
                  function _setTokenRoyalty(
                      uint256 tokenId,
                      address receiver,
                      uint96 feeNumerator
                  ) internal virtual {
                      require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                      require(receiver != address(0), "ERC2981: Invalid parameters");
                      _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
                  }
                  /**
                   * @dev Resets royalty information for the token id back to the global default.
                   */
                  function _resetTokenRoyalty(uint256 tokenId) internal virtual {
                      delete _tokenRoyaltyInfo[tokenId];
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright (c) 2021 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
              import {AccessControlEnumerable} from "./AccessControlEnumerable.sol";
              /// @notice A Pausable contract that can only be toggled by a member of the
              /// STEERING role.
              contract AccessControlPausable is AccessControlEnumerable, Pausable {
                  /// @notice Pauses the contract.
                  function pause() public onlyRole(DEFAULT_STEERING_ROLE) {
                      Pausable._pause();
                  }
                  /// @notice Unpauses the contract.
                  function unpause() public onlyRole(DEFAULT_STEERING_ROLE) {
                      Pausable._unpause();
                  }
              }
              // SPDX-License-Identifier: CC0-1.0
              pragma solidity ^0.8.0;
              import {IERC165, ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
              interface IERC4906Events {
                  /// @dev This event emits when the metadata of a token is changed.
                  /// So that the third-party platforms such as NFT market could
                  /// timely update the images and related attributes of the NFT.
                  event MetadataUpdate(uint256 _tokenId);
                  /// @dev This event emits when the metadata of a range of tokens is changed.
                  /// So that the third-party platforms such as NFT market could
                  /// timely update the images and related attributes of the NFTs.
                  event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
              }
              /// @title EIP-721 Metadata Update Extension
              // solhint-disable-next-line no-empty-blocks
              interface IERC4906 is IERC165, IERC4906Events {
              }
              contract ERC4906 is IERC4906, ERC165 {
                  function _refreshMetadata(uint256 tokenId) internal {
                      emit MetadataUpdate(tokenId);
                  }
                  function _refreshMetadata(uint256 fromTokenId, uint256 toTokenId) internal {
                      emit BatchMetadataUpdate(fromTokenId, toTokenId);
                  }
                  /// @dev See {IERC165-supportsInterface}.
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC165, IERC165)
                      returns (bool)
                  {
                      return
                          interfaceId == bytes4(0x49064906) ||
                          ERC165.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 is IERC165 {
                  /**
                   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                   */
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                   */
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes calldata data
                  ) external;
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                   * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                   * understand this adds an external call which potentially creates a reentrancy vulnerability.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              import {IOperatorFilterRegistry} from "../IOperatorFilterRegistry.sol";
              import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
              /**
               * @title  OperatorFiltererUpgradeable
               * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
               *         registrant's entries in the OperatorFilterRegistry when the init function is called.
               * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
               *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
               *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
               */
              abstract contract OperatorFiltererUpgradeable is Initializable {
                  /// @notice Emitted when an operator is not allowed.
                  error OperatorNotAllowed(address operator);
                  IOperatorFilterRegistry constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);
                  /// @dev The upgradeable initialize function that should be called when the contract is being upgraded.
                  function __OperatorFilterer_init(address subscriptionOrRegistrantToCopy, bool subscribe)
                      internal
                      onlyInitializing
                  {
                      // 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));
                                  }
                              }
                          }
                      }
                  }
                  /**
                   * @dev A helper modifier to check if the operator is allowed.
                   */
                  modifier onlyAllowedOperator(address from) virtual {
                      // Allow spending tokens from addresses with balance
                      // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                      // from an EOA.
                      if (from != msg.sender) {
                          _checkFilterOperator(msg.sender);
                      }
                      _;
                  }
                  /**
                   * @dev A helper modifier to check if the operator approval is allowed.
                   */
                  modifier onlyAllowedOperatorApproval(address operator) virtual {
                      _checkFilterOperator(operator);
                      _;
                  }
                  /**
                   * @dev A helper function to check if the operator is allowed.
                   */
                  function _checkFilterOperator(address operator) internal view virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                          // under normal circumstances, this function will revert rather than return false, but inheriting or
                          // upgraded contracts may specify their own OperatorFilterRegistry implementations, which may behave
                          // differently
                          if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                              revert OperatorNotAllowed(operator);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              address constant CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS = 0x000000000000AAeB6D7670E522A718067333cd4E;
              address constant CANONICAL_CORI_SUBSCRIPTION = 0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6;
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
              import {UUPSUpgradeable} from "openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
              import {ERC721Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
              import {ERC721RoyaltyUpgradeable} from
                  "openzeppelin-contracts-upgradeable/token/ERC721/extensions/ERC721RoyaltyUpgradeable.sol";
              import {ERC721PausableUpgradeable} from
                  "openzeppelin-contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
              import {
                  SteerableAccessControlEnumerableUpgradeable,
                  AccessControlEnumerableUpgradeable
              } from "./SteerableAccessControlEnumerableUpgradeable.sol";
              /**
               * @notice Base contract for Mythics implementations.
               * @dev This contract is intended be inherited by all implementations and thus MUST NOT be changed.
               */
              contract MythicsBase is
                  Initializable,
                  SteerableAccessControlEnumerableUpgradeable,
                  ERC721Upgradeable,
                  ERC721RoyaltyUpgradeable,
                  ERC721PausableUpgradeable,
                  UUPSUpgradeable
              {
                  constructor() {
                      _disableInitializers();
                  }
                  function __MythicsBase_init() internal onlyInitializing {
                      __AccessControlEnumerable_init();
                      __ERC721_init("Moonbirds: Mythics", "MYTHICS");
                      __ERC721Royalty_init();
                      __ERC721Pausable_init();
                      __UUPSUpgradeable_init();
                  }
                  function __MythicsBase_init_unchained() internal onlyInitializing {}
                  /**
                   * @notice Only the admin is authorised to upgrade the implementation.
                   */
                  function _authorizeUpgrade(address newImplementation) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
                  /**
                   * @dev Needed for inheritance resolution.
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize)
                      internal
                      virtual
                      override(ERC721Upgradeable, ERC721PausableUpgradeable)
                  {
                      ERC721PausableUpgradeable._beforeTokenTransfer(from, to, firstTokenId, batchSize);
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721Upgradeable, AccessControlEnumerableUpgradeable, ERC721RoyaltyUpgradeable)
                      returns (bool)
                  {
                      return ERC721Upgradeable.supportsInterface(interfaceId)
                          || ERC721RoyaltyUpgradeable.supportsInterface(interfaceId)
                          || AccessControlEnumerableUpgradeable.supportsInterface(interfaceId);
                  }
                  function _burn(uint256 tokenId) internal virtual override(ERC721Upgradeable, ERC721RoyaltyUpgradeable) {
                      ERC721RoyaltyUpgradeable._burn(tokenId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
              import {ISellable} from "proof/sellers/interfaces/ISellable.sol";
              import {
                  AccessControlEnumerableUpgradeable,
                  SteerableAccessControlEnumerableUpgradeable
              } from "./SteerableAccessControlEnumerableUpgradeable.sol";
              /**
               * @notice A base contract for selling content via authorised sellers.
               */
              abstract contract BaseSellableUpgradeable is Initializable, ISellable, SteerableAccessControlEnumerableUpgradeable {
                  /**
                   * @notice Authorised sellers.
                   */
                  bytes32 public constant AUTHORISED_SELLER_ROLE = keccak256("AUTHORISED_SELLER_ROLE");
                  function __BaseSellable_init() internal onlyInitializing {
                      __AccessControlEnumerable_init();
                      __BaseSellable_init_unchained();
                  }
                  function __BaseSellable_init_unchained() internal onlyInitializing {
                      _setRoleAdmin(AUTHORISED_SELLER_ROLE, DEFAULT_STEERING_ROLE);
                  }
                  /**
                   * @notice Handles the sale of sellable content via an authorised seller.
                   * @dev Delegates the implementation to `_handleSale`.
                   */
                  function handleSale(address to, uint64 num, bytes calldata data)
                      external
                      payable
                      onlyRole(AUTHORISED_SELLER_ROLE)
                  {
                      _handleSale(to, num, data);
                  }
                  /**
                   * @notice Handles the sale of sellable content.
                   */
                  function _handleSale(address to, uint64 num, bytes calldata data) internal virtual;
                  /**
                   * @notice Revokes approval for all sellers.
                   */
                  function _revokeAllSellers() internal {
                      uint256 num = getRoleMemberCount(AUTHORISED_SELLER_ROLE);
                      for (uint256 i = 0; i < num; i++) {
                          // Akin to a popFront
                          address seller = getRoleMember(AUTHORISED_SELLER_ROLE, 0);
                          _revokeRole(AUTHORISED_SELLER_ROLE, seller);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
              import {SettableCallbackerWithAccessControl} from "proof/sellers/presets/CallbackerWithAccessControl.sol";
              import {Seller} from "proof/sellers/base/Seller.sol";
              import {MythicEggSampler} from "../Egg/MythicEggSampler.sol";
              import {NonRollingRateLimited} from "./NonRollingRateLimited.sol";
              interface OddsoleumEvents {
                  event OdditySacrificed(address indexed owner, uint256 tokenId);
                  /**
                   * @notice Emitted if a burner attempts to sacrifice an Oddity that is not in the queue or was not approved to be
                   * transferred by the Oddsoleum contract.
                   * @dev This will likely only happen in the case of a race condition, where Oddity nomination is revoked after
                   * selecting it to be burned.
                   */
                  event CannotBurnIneligibleOddity(uint256 indexed tokenId, bool queued, bool approved);
              }
              /**
               * @title Oddsoleum
               * @notice Allows Oddities to enter a queue for being sacrificed on the altar of the Oddgod.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract Oddsoleum is Seller, SettableCallbackerWithAccessControl, OddsoleumEvents, NonRollingRateLimited {
                  /**
                   * @notice The role allowed to burn oddities.
                   */
                  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
                  /**
                   * @notice The receiver of burned Oddities.
                   * @dev The original Oddities contract does not allow burning, so we send the tokens to the dead address instead.
                   */
                  address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
                  /**
                   * @notice The Oddities contract.
                   */
                  IERC721 public immutable oddities;
                  /**
                   * @notice Keeps track of Oddities that are in the queue to be sacrificed.
                   * @dev Keyed by owner to automatically unqueue Oddities if they are transferred. Consequently, tokens will still
                   * be queued after a round-trip, which can't be avoided as it would require a callback from the Oddities contract
                   * upon transfer.
                   */
                  mapping(address owner => mapping(uint256 tokenId => bool)) private _queued;
                  constructor(address admin, address steerer, IERC721 oddities_)
                      SettableCallbackerWithAccessControl(admin, steerer)
                      NonRollingRateLimited(50, 1 days)
                  {
                      _setRoleAdmin(BURNER_ROLE, DEFAULT_STEERING_ROLE);
                      oddities = oddities_;
                  }
                  /**
                   * @notice Adds the given Oddities to the senders queue.
                   * @dev Oddity ownership is not relevant here as senders only have access to their own flag set. Upon burn, the
                   * contract will only consider the flag of the current owner of the given Oddity.
                   */
                  function addToQueue(uint256[] calldata tokenIds) external {
                      for (uint256 i = 0; i < tokenIds.length; ++i) {
                          _queued[msg.sender][tokenIds[i]] = true;
                      }
                  }
                  /**
                   * @notice Removes the given Oddities from the senders queue.
                   * @dev Oddity ownership is not relevant here as senders only have access to their own flag set. Upon burn, the
                   * contract will only consider the flag of the current owner of the given Oddity.
                   */
                  function removeFromQueue(uint256[] calldata tokenIds) external {
                      for (uint256 i = 0; i < tokenIds.length; ++i) {
                          _queued[msg.sender][tokenIds[i]] = false;
                      }
                  }
                  /**
                   * @notice Returns whether the given Oddities are in the queue.
                   * @dev This does not imply that they can be sacrificed, as the owner may not have approved this contract to burn
                   * them.
                   */
                  function queued(uint256[] calldata tokenIds) public view returns (bool[] memory) {
                      bool[] memory queued_ = new bool[](tokenIds.length);
                      for (uint256 i = 0; i < tokenIds.length; ++i) {
                          address owner = oddities.ownerOf(tokenIds[i]);
                          queued_[i] = _queued[owner][tokenIds[i]];
                      }
                      return queued_;
                  }
                  /**
                   * @notice Returns whether the given Oddities can be sacrificed.
                   * @dev True iff the Oddity is in the queue and the owner has approved this contract to burn it.
                   */
                  function burnable(uint256[] calldata tokenIds) external view returns (bool[] memory) {
                      bool[] memory burnable_ = new bool[](tokenIds.length);
                      for (uint256 i = 0; i < tokenIds.length; ++i) {
                          address owner = oddities.ownerOf(tokenIds[i]);
                          burnable_[i] = _burnable(owner, tokenIds[i]);
                      }
                      return burnable_;
                  }
                  /**
                   * @notice Returns whether the given Oddities can be sacrificed.
                   */
                  function _burnable(address owner, uint256 tokenId) internal view returns (bool) {
                      return _queued[owner][tokenId] && _approved(owner, tokenId);
                  }
                  /**
                   * @notice Returns whether the given Oddities can be sacrificed.
                   */
                  function _approved(address owner, uint256 tokenId) internal view returns (bool) {
                      return (oddities.isApprovedForAll(owner, address(this)) || oddities.getApproved(tokenId) == address(this));
                  }
                  /**
                   * @notice Burns the given Oddity by sending it to a burn address.
                   */
                  function _burn(address owner, uint256 tokenId) internal returns (bool) {
                      bool queued_ = _queued[owner][tokenId];
                      bool approved = _approved(owner, tokenId);
                      if (!(queued_ && approved)) {
                          emit CannotBurnIneligibleOddity(tokenId, queued_, approved);
                          return false;
                      }
                      oddities.transferFrom(owner, BURN_ADDRESS, tokenId);
                      emit OdditySacrificed(owner, tokenId);
                      return true;
                  }
                  /**
                   * @notice Sacrifices the given Oddity by burning it and awards a Mythic to the original owner in return.
                   */
                  function _sacrifice(uint256 tokenId) internal returns (bool) {
                      address owner = oddities.ownerOf(tokenId);
                      bool burned = _burn(owner, tokenId);
                      if (!burned) {
                          return false;
                      }
                      _purchase(owner, 1, /* total cost */ 0, "");
                      return true;
                  }
                  /**
                   * @notice Sacrifices the given Oddities by burning them and awards Mythics to the original owners in return.
                   */
                  function sacrifice(uint256[] calldata tokenIds) external onlyRole(BURNER_ROLE) {
                      uint64 numSacrificed;
                      for (uint256 i = 0; i < tokenIds.length; ++i) {
                          bool sacrificed = _sacrifice(tokenIds[i]);
                          unchecked {
                              if (sacrificed) {
                                  ++numSacrificed;
                              }
                          }
                      }
                      _checkAndTrackRateLimit(numSacrificed);
                  }
                  /**
                   * @notice Sets the maximum number of activations per day.
                   */
                  function setMaxSacrificesPerPeriod(uint32 maxSacrificesPerPeriod) external onlyRole(DEFAULT_STEERING_ROLE) {
                      _setMaxActionsPerPeriod(maxSacrificesPerPeriod);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {IRedeemableToken} from "proof/redemption/interfaces/IRedeemableToken.sol";
              import {MythicsV1} from "./MythicsV1.sol";
              import {MythicsEgg, MythicEggSampler} from "../Egg/MythicsEgg.sol";
              import {ISellable, ImmutableSellableCallbacker, SellableCallbacker} from "proof/sellers/base/SellableCallbacker.sol";
              import {Seller} from "proof/sellers/base/Seller.sol";
              import {RedeemableTokenRedeemer} from "proof/redemption/RedeemableTokenRedeemer.sol";
              /**
               * @notice Helper library to encode/decode the purchase payload sent to the Mythics seller interface.
               */
              library MythicsEggRedemptionLib {
                  struct PurchasePayload {
                      uint256 eggId;
                      MythicEggSampler.EggType eggType;
                  }
                  function encode(PurchasePayload[] memory redemptions) internal pure returns (bytes memory) {
                      return abi.encode(redemptions);
                  }
                  function decode(bytes memory data) internal pure returns (PurchasePayload[] memory) {
                      return abi.decode(data, (PurchasePayload[]));
                  }
              }
              /**
               * @title Mythics: Egg redeemer
               * @notice Redeems activated mythic eggs for a Mythic (either a token in the case of a stone egg or an open choice for
               * non-stone eggs).
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsEggRedeemer is Seller, ImmutableSellableCallbacker, RedeemableTokenRedeemer {
                  using MythicsEggRedemptionLib for MythicsEggRedemptionLib.PurchasePayload[];
                  error EggNotActivated(uint256 eggId);
                  error EggNotRevealed(uint256 eggId);
                  MythicsEgg public immutable eggs;
                  constructor(ISellable mythics, MythicsEgg eggs_) ImmutableSellableCallbacker(mythics) {
                      eggs = eggs_;
                  }
                  /**
                   * @notice Redeems the given passes and purchases pieces in the Diamond Exhibition.
                   */
                  function redeem(uint256[] calldata eggIds) external {
                      MythicsEggRedemptionLib.PurchasePayload[] memory payloads =
                          new MythicsEggRedemptionLib.PurchasePayload[](eggIds.length);
                      for (uint256 i = 0; i < eggIds.length; ++i) {
                          if (!eggs.activated(eggIds[i])) {
                              revert EggNotActivated(eggIds[i]);
                          }
                          (MythicEggSampler.EggType eggType, bool revealed) = eggs.eggType(eggIds[i]);
                          if (!revealed) {
                              revert EggNotRevealed(eggIds[i]);
                          }
                          _redeem(eggs, eggIds[i]);
                          payloads[i] = MythicsEggRedemptionLib.PurchasePayload({eggId: eggIds[i], eggType: eggType});
                      }
                      _purchase(msg.sender, uint64(eggIds.length), /* total cost */ 0, payloads.encode());
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {IEntropyOracle} from "proof/entropy/IEntropyOracle.sol";
              import {MythicsEggErrors} from "./MythicsEggErrors.sol";
              import {
                  StochasticSampler, StochasticSamplerWithCDFStorage, StochasticSamplerWithOracle
              } from "./StochasticSampling.sol";
              /**
               * @title Mythics: Egg type sampling module
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              abstract contract MythicEggSampler is StochasticSamplerWithCDFStorage, StochasticSamplerWithOracle, MythicsEggErrors {
                  /**
                   * @notice The different types of eggs.
                   */
                  enum EggType {
                      Stone,
                      Runic,
                      Legendary
                  }
                  /**
                   * @notice Number of egg types
                   */
                  uint8 public constant NUM_EGG_TYPES = 3;
                  /**
                   * @notice Trait ID for the egg type
                   */
                  uint8 private constant _EGG_TYPE_TRAIT_ID = 0;
                  /**
                   * @notice Token-specific parameters for sampling the egg type
                   * @dev Will be determined at mint.
                   * @param revealBlockNumber Number of the block whose entropy will be used to reaveal the egg type.
                   * @param distributionVersion The version/index of probability distribution to sample the egg type.
                   * @param mixHash Part of the block mixHash to blind the entropy oracle.
                   */
                  struct SamplingParams {
                      uint64 revealBlockNumber;
                      uint16 distributionVersion;
                      uint128 mixHash;
                  }
                  /**
                   * @notice Egg-type sampling parameters keyed by token ID.
                   */
                  mapping(uint256 => SamplingParams) private _samplingParams;
                  /**
                   * @dev Constructor helper function.
                   */
                  function _numPerTrait() private pure returns (uint256[] memory) {
                      uint256[] memory numPerTrait = new uint256[](1);
                      numPerTrait[_EGG_TYPE_TRAIT_ID] = NUM_EGG_TYPES;
                      return numPerTrait;
                  }
                  constructor(IEntropyOracle oracle)
                      StochasticSamplerWithCDFStorage(_numPerTrait())
                      StochasticSamplerWithOracle(oracle)
                  {}
                  /**
                   * @notice Returns the egg-type sampling parameters for a given token ID.
                   */
                  function samplingParams(uint256 tokenId) public view returns (SamplingParams memory) {
                      if (!_exists(tokenId)) {
                          revert NonexistentEgg(tokenId);
                      }
                      return _samplingParams[tokenId];
                  }
                  /**
                   * @inheritdoc StochasticSamplerWithCDFStorage
                   * @dev Reads the token-specific parameters.
                   */
                  function _distributionVersion(uint256 tokenId, uint256 traitId) internal view virtual override returns (uint256) {
                      assert(traitId == _EGG_TYPE_TRAIT_ID);
                      return _samplingParams[tokenId].distributionVersion;
                  }
                  /**
                   * @inheritdoc StochasticSamplerWithOracle
                   * @dev Reads the token-specific parameters.
                   */
                  function _revealBlockNumber(uint256 tokenId) internal view virtual override returns (uint256) {
                      return _samplingParams[tokenId].revealBlockNumber;
                  }
                  /**
                   * @notice Registers a token for egg-type sampling using the currently set probability distribution.
                   * @dev Must be called upon token mint.
                   */
                  function _registerForSampling(uint256 tokenId) internal {
                      uint256 revealBlockNumber = block.number;
                      _samplingParams[tokenId] = SamplingParams({
                          revealBlockNumber: uint64(revealBlockNumber),
                          distributionVersion: uint16(_latestDistributionVersion(_EGG_TYPE_TRAIT_ID)),
                          // Smearing out single-bit-of-influence from the prevrandao since we're just using 128 bits (mainly to
                          // prevent the forge fuzzer from finding breaking runs which would force us to add circular testing logic).
                          mixHash: uint128(uint256(keccak256(abi.encode(block.prevrandao))))
                      });
                      entropyOracle.requestEntropy(revealBlockNumber);
                  }
                  /**
                   * @notice Sets the probability distribution for egg types.
                   */
                  function _setEggProbabilities(uint64[NUM_EGG_TYPES] memory pdf) internal {
                      uint64[] memory p = new uint64[](NUM_EGG_TYPES);
                      for (uint256 i = 0; i < NUM_EGG_TYPES; i++) {
                          p[i] = pdf[i];
                      }
                      _pushProbabilities(_EGG_TYPE_TRAIT_ID, p);
                  }
                  /**
                   * @inheritdoc StochasticSamplerWithOracle
                   * @dev Mixes the seed with the token-specific parameters to blind the EntropyOracle.
                   */
                  function _seed(uint256 tokenId)
                      internal
                      view
                      virtual
                      override(StochasticSampler, StochasticSamplerWithOracle)
                      returns (bytes32, bool)
                  {
                      (bytes32 seed, bool revealed) = StochasticSamplerWithOracle._seed(tokenId);
                      return (keccak256(abi.encode(seed, samplingParams(tokenId))), revealed);
                  }
                  /**
                   * @notice Returns the egg type of a given token ID and a boolean flag to indicate whether it was already revealed.
                   */
                  function eggType(uint256 tokenId) public view returns (EggType, bool) {
                      (uint256 sample, bool revealed) = _sampleTrait(tokenId, _EGG_TYPE_TRAIT_ID);
                      return (EggType(sample), revealed);
                  }
                  /**
                   * @notice Returns whether a token exists.
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              /**
               * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
               */
              interface IAccessControlEnumerable is IAccessControl {
                  /**
                   * @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
                   * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
                   * 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: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              import "../utils/Context.sol";
              import "../utils/Strings.sol";
              import "../utils/introspection/ERC165.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it.
               */
              abstract contract AccessControl is Context, IAccessControl, ERC165 {
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      Strings.toHexString(account),
                                      " is missing role ",
                                      Strings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @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.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @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 revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.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
              // ERC721A Contracts v4.2.3
              // Creator: Chiru Labs
              pragma solidity ^0.8.4;
              /**
               * @dev Interface of ERC721A.
               */
              interface IERC721A {
                  /**
                   * The caller must own the token or be an approved operator.
                   */
                  error ApprovalCallerNotOwnerNorApproved();
                  /**
                   * The token does not exist.
                   */
                  error ApprovalQueryForNonexistentToken();
                  /**
                   * 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();
                  /**
                   * The `quantity` minted with ERC2309 exceeds the safety limit.
                   */
                  error MintERC2309QuantityExceedsLimit();
                  /**
                   * The `extraData` cannot be set on an unintialized ownership slot.
                   */
                  error OwnershipNotInitializedForExtraData();
                  // =============================================================
                  //                            STRUCTS
                  // =============================================================
                  struct TokenOwnership {
                      // The address of the owner.
                      address addr;
                      // Stores the start time of ownership with minimal overhead for tokenomics.
                      uint64 startTimestamp;
                      // Whether the token has been burned.
                      bool burned;
                      // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
                      uint24 extraData;
                  }
                  // =============================================================
                  //                         TOKEN COUNTERS
                  // =============================================================
                  /**
                   * @dev Returns the total number of tokens in existence.
                   * Burned tokens will reduce the count.
                   * To get the total number of tokens minted, please see {_totalMinted}.
                   */
                  function totalSupply() external view returns (uint256);
                  // =============================================================
                  //                            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 30000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
                  // =============================================================
                  //                            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 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`,
                   * checking first that contract recipients are aware of the ERC721 protocol
                   * to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be have been allowed to move
                   * this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement
                   * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes calldata data
                  ) external payable;
                  /**
                   * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
                   */
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) external payable;
                  /**
                   * @dev Transfers `tokenId` 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 payable;
                  /**
                   * @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 payable;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom}
                   * for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool _approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
                  // =============================================================
                  //                        IERC721Metadata
                  // =============================================================
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view returns (string memory);
                  // =============================================================
                  //                           IERC2309
                  // =============================================================
                  /**
                   * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
                   * (inclusive) is transferred from `from` to `to`, as defined in the
                   * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
                   *
                   * See {_mintERC2309} for more details.
                   */
                  event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
              pragma solidity ^0.8.0;
              import "../utils/introspection/IERC165.sol";
              /**
               * @dev Interface for the NFT Royalty Standard.
               *
               * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
               * support for royalty payments across all NFT marketplaces and ecosystem participants.
               *
               * _Available since v4.5._
               */
              interface IERC2981 is IERC165 {
                  /**
                   * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                   * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice)
                      external
                      view
                      returns (address receiver, uint256 royaltyAmount);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which allows children to implement an emergency stop
               * mechanism that can be triggered by an authorized account.
               *
               * This module is used through inheritance. It will make available the
               * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
               * the functions of your contract. Note that they will not be pausable by
               * simply including this module, only once the modifiers are put in place.
               */
              abstract contract Pausable is Context {
                  /**
                   * @dev Emitted when the pause is triggered by `account`.
                   */
                  event Paused(address account);
                  /**
                   * @dev Emitted when the pause is lifted by `account`.
                   */
                  event Unpaused(address account);
                  bool private _paused;
                  /**
                   * @dev Initializes the contract in unpaused state.
                   */
                  constructor() {
                      _paused = false;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is not paused.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  modifier whenNotPaused() {
                      _requireNotPaused();
                      _;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is paused.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  modifier whenPaused() {
                      _requirePaused();
                      _;
                  }
                  /**
                   * @dev Returns true if the contract is paused, and false otherwise.
                   */
                  function paused() public view virtual returns (bool) {
                      return _paused;
                  }
                  /**
                   * @dev Throws if the contract is paused.
                   */
                  function _requireNotPaused() internal view virtual {
                      require(!paused(), "Pausable: paused");
                  }
                  /**
                   * @dev Throws if the contract is not paused.
                   */
                  function _requirePaused() internal view virtual {
                      require(paused(), "Pausable: not paused");
                  }
                  /**
                   * @dev Triggers stopped state.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  function _pause() internal virtual whenNotPaused {
                      _paused = true;
                      emit Paused(_msgSender());
                  }
                  /**
                   * @dev Returns to normal state.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  function _unpause() internal virtual whenPaused {
                      _paused = false;
                      emit Unpaused(_msgSender());
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165 {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.13;
              interface IOperatorFilterRegistry {
                  /**
                   * @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);
                  /**
                   * @notice Registers an address with the registry. May be called by address itself or by EIP-173 owner.
                   */
                  function register(address registrant) external;
                  /**
                   * @notice Registers an address with the registry and "subscribes" to another address's filtered operators and codeHashes.
                   */
                  function registerAndSubscribe(address registrant, address subscription) external;
                  /**
                   * @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;
                  /**
                   * @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 addr) external;
                  /**
                   * @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;
                  /**
                   * @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;
                  /**
                   * @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;
                  /**
                   * @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;
                  /**
                   * @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 registrantToSubscribe) external;
                  /**
                   * @notice Unsubscribe an address from its current subscribed registrant, and optionally copy its filtered operators and codeHashes.
                   */
                  function unsubscribe(address registrant, bool copyExistingEntries) external;
                  /**
                   * @notice Get the subscription address of a given registrant, if any.
                   */
                  function subscriptionOf(address addr) external returns (address registrant);
                  /**
                   * @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 returns (address[] memory);
                  /**
                   * @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 returns (address);
                  /**
                   * @notice Copy filtered operators and codeHashes from a different registrantToCopy to addr.
                   */
                  function copyEntriesOf(address registrant, address registrantToCopy) external;
                  /**
                   * @notice Returns true if operator is filtered by a given address or its subscription.
                   */
                  function isOperatorFiltered(address registrant, address operator) external returns (bool);
                  /**
                   * @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 returns (bool);
                  /**
                   * @notice Returns true if a codeHash is filtered by a given address or its subscription.
                   */
                  function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
                  /**
                   * @notice Returns a list of filtered operators for a given address or its subscription.
                   */
                  function filteredOperators(address addr) external returns (address[] memory);
                  /**
                   * @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 addr) external returns (bytes32[] memory);
                  /**
                   * @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 returns (address);
                  /**
                   * @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 returns (bytes32);
                  /**
                   * @notice Returns true if an address has registered
                   */
                  function isRegistered(address addr) external returns (bool);
                  /**
                   * @dev Convenience method to compute the code hash of an arbitrary contract
                   */
                  function codeHashOf(address addr) external returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.2;
              import "../../utils/AddressUpgradeable.sol";
              /**
               * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
               * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
               * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
               * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
               *
               * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
               * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
               * case an upgrade adds a module that needs to be initialized.
               *
               * For example:
               *
               * [.hljs-theme-light.nopadding]
               * ```solidity
               * 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 (last updated v4.9.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.
                   *
                   * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                   */
                  function upgradeTo(address newImplementation) public 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.
                   *
                   * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) public 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
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
              pragma solidity ^0.8.0;
              import "./IERC721Upgradeable.sol";
              import "./IERC721ReceiverUpgradeable.sol";
              import "./extensions/IERC721MetadataUpgradeable.sol";
              import "../../utils/AddressUpgradeable.sol";
              import "../../utils/ContextUpgradeable.sol";
              import "../../utils/StringsUpgradeable.sol";
              import "../../utils/introspection/ERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
               * the Metadata extension, but not including the Enumerable extension, which is available separately as
               * {ERC721Enumerable}.
               */
              contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
                  using AddressUpgradeable for address;
                  using StringsUpgradeable for uint256;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to owner address
                  mapping(uint256 => address) private _owners;
                  // Mapping owner address to token count
                  mapping(address => uint256) private _balances;
                  // Mapping from token ID to approved address
                  mapping(uint256 => address) private _tokenApprovals;
                  // Mapping from owner to operator approvals
                  mapping(address => mapping(address => bool)) private _operatorApprovals;
                  /**
                   * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
                   */
                  function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
                      __ERC721_init_unchained(name_, symbol_);
                  }
                  function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
                      return
                          interfaceId == type(IERC721Upgradeable).interfaceId ||
                          interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view virtual override returns (uint256) {
                      require(owner != address(0), "ERC721: address zero is not a valid owner");
                      return _balances[owner];
                  }
                  /**
                   * @dev See {IERC721-ownerOf}.
                   */
                  function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                      address owner = _ownerOf(tokenId);
                      require(owner != address(0), "ERC721: invalid token ID");
                      return owner;
                  }
                  /**
                   * @dev See {IERC721Metadata-name}.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC721Metadata-tokenURI}.
                   */
                  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                      _requireMinted(tokenId);
                      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 overridden 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 = ERC721Upgradeable.ownerOf(tokenId);
                      require(to != owner, "ERC721: approval to current owner");
                      require(
                          _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                          "ERC721: approve caller is not token owner or approved for all"
                      );
                      _approve(to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-getApproved}.
                   */
                  function getApproved(uint256 tokenId) public view virtual override returns (address) {
                      _requireMinted(tokenId);
                      return _tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      _setApprovalForAll(_msgSender(), operator, approved);
                  }
                  /**
                   * @dev See {IERC721-isApprovedForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                      //solhint-disable-next-line max-line-length
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _transfer(from, to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                      safeTransferFrom(from, to, tokenId, "");
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _safeTransfer(from, to, tokenId, data);
                  }
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * `data` is additional data, it has no specified format and it is sent in call to `to`.
                   *
                   * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
                   * implement alternative mechanisms to perform token transfer, such as signature-based.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
                      _transfer(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
                  }
                  /**
                   * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
                   */
                  function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
                      return _owners[tokenId];
                  }
                  /**
                   * @dev Returns whether `tokenId` exists.
                   *
                   * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                   *
                   * Tokens start existing when they are minted (`_mint`),
                   * and stop existing when they are burned (`_burn`).
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool) {
                      return _ownerOf(tokenId) != address(0);
                  }
                  /**
                   * @dev Returns whether `spender` is allowed to manage `tokenId`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                      address owner = ERC721Upgradeable.ownerOf(tokenId);
                      return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
                  }
                  /**
                   * @dev Safely mints `tokenId` and transfers it to `to`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeMint(address to, uint256 tokenId) internal virtual {
                      _safeMint(to, tokenId, "");
                  }
                  /**
                   * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                   * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
                   */
                  function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
                      _mint(to, tokenId);
                      require(
                          _checkOnERC721Received(address(0), to, tokenId, data),
                          "ERC721: transfer to non ERC721Receiver implementer"
                      );
                  }
                  /**
                   * @dev Mints `tokenId` and transfers it to `to`.
                   *
                   * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - `to` cannot be the zero address.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _mint(address to, uint256 tokenId) internal virtual {
                      require(to != address(0), "ERC721: mint to the zero address");
                      require(!_exists(tokenId), "ERC721: token already minted");
                      _beforeTokenTransfer(address(0), to, tokenId, 1);
                      // Check that tokenId was not minted by `_beforeTokenTransfer` hook
                      require(!_exists(tokenId), "ERC721: token already minted");
                      unchecked {
                          // Will not overflow unless all 2**256 token ids are minted to the same owner.
                          // Given that tokens are minted one by one, it is impossible in practice that
                          // this ever happens. Might change if we allow batch minting.
                          // The ERC fails to describe this case.
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(address(0), to, tokenId);
                      _afterTokenTransfer(address(0), to, tokenId, 1);
                  }
                  /**
                   * @dev Destroys `tokenId`.
                   * The approval is cleared when the token is burned.
                   * This is an internal function that does not check if the sender is authorized to operate on the token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _burn(uint256 tokenId) internal virtual {
                      address owner = ERC721Upgradeable.ownerOf(tokenId);
                      _beforeTokenTransfer(owner, address(0), tokenId, 1);
                      // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
                      owner = ERC721Upgradeable.ownerOf(tokenId);
                      // Clear approvals
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // Cannot overflow, as that would require more tokens to be burned/transferred
                          // out than the owner initially received through minting and transferring in.
                          _balances[owner] -= 1;
                      }
                      delete _owners[tokenId];
                      emit Transfer(owner, address(0), tokenId);
                      _afterTokenTransfer(owner, address(0), tokenId, 1);
                  }
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _transfer(address from, address to, uint256 tokenId) internal virtual {
                      require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      require(to != address(0), "ERC721: transfer to the zero address");
                      _beforeTokenTransfer(from, to, tokenId, 1);
                      // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
                      require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      // Clear approvals from the previous owner
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                          // `from`'s balance is the number of token held, which is at least one before the current
                          // transfer.
                          // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                          // all 2**256 token ids to be minted, which in practice is impossible.
                          _balances[from] -= 1;
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfer(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits an {Approval} event.
                   */
                  function _approve(address to, uint256 tokenId) internal virtual {
                      _tokenApprovals[tokenId] = to;
                      emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
                  }
                  /**
                   * @dev Approve `operator` to operate on all of `owner` tokens
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
                      require(owner != operator, "ERC721: approve to caller");
                      _operatorApprovals[owner][operator] = approved;
                      emit ApprovalForAll(owner, operator, approved);
                  }
                  /**
                   * @dev Reverts if the `tokenId` has not been minted yet.
                   */
                  function _requireMinted(uint256 tokenId) internal view virtual {
                      require(_exists(tokenId), "ERC721: invalid token ID");
                  }
                  /**
                   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
                   * The call is not executed if the target address is not a contract.
                   *
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) private returns (bool) {
                      if (to.isContract()) {
                          try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                              return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
                          } catch (bytes memory reason) {
                              if (reason.length == 0) {
                                  revert("ERC721: transfer to non ERC721Receiver implementer");
                              } else {
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      revert(add(32, reason), mload(reason))
                                  }
                              }
                          }
                      } else {
                          return true;
                      }
                  }
                  /**
                   * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
                   * - When `from` is zero, the tokens will be minted for `to`.
                   * - When `to` is zero, ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
                   * - When `from` is zero, the tokens were minted for `to`.
                   * - When `to` is zero, ``from``'s tokens were burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
                   *
                   * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
                   * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
                   * that `ownerOf(tokenId)` is `a`.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function __unsafe_increaseBalance(address account, uint256 amount) internal {
                      _balances[account] += amount;
                  }
                  /**
                   * @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[44] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Royalty.sol)
              pragma solidity ^0.8.0;
              import "../ERC721Upgradeable.sol";
              import "../../common/ERC2981Upgradeable.sol";
              import "../../../utils/introspection/ERC165Upgradeable.sol";
              import "../../../proxy/utils/Initializable.sol";
              /**
               * @dev Extension of ERC721 with the ERC2981 NFT Royalty Standard, a standardized way to retrieve royalty payment
               * information.
               *
               * Royalty information can be specified globally for all token ids via {ERC2981-_setDefaultRoyalty}, and/or individually for
               * specific token ids via {ERC2981-_setTokenRoyalty}. The latter takes precedence over the first.
               *
               * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
               * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
               * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
               *
               * _Available since v4.5._
               */
              abstract contract ERC721RoyaltyUpgradeable is Initializable, ERC2981Upgradeable, ERC721Upgradeable {
                  function __ERC721Royalty_init() internal onlyInitializing {
                  }
                  function __ERC721Royalty_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Upgradeable, ERC2981Upgradeable) returns (bool) {
                      return super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {ERC721-_burn}. This override additionally clears the royalty information for the token.
                   */
                  function _burn(uint256 tokenId) internal virtual override {
                      super._burn(tokenId);
                      _resetTokenRoyalty(tokenId);
                  }
                  /**
                   * @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.2) (token/ERC721/extensions/ERC721Pausable.sol)
              pragma solidity ^0.8.0;
              import "../ERC721Upgradeable.sol";
              import "../../../security/PausableUpgradeable.sol";
              import "../../../proxy/utils/Initializable.sol";
              /**
               * @dev ERC721 token with pausable token transfers, minting and burning.
               *
               * Useful for scenarios such as preventing trades until the end of an evaluation
               * period, or having an emergency switch for freezing all token transfers in the
               * event of a large bug.
               *
               * IMPORTANT: This contract does not include public pause and unpause functions. In
               * addition to inheriting this contract, you must define both functions, invoking the
               * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
               * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
               * make the contract unpausable.
               */
              abstract contract ERC721PausableUpgradeable is Initializable, ERC721Upgradeable, PausableUpgradeable {
                  function __ERC721Pausable_init() internal onlyInitializing {
                      __Pausable_init_unchained();
                  }
                  function __ERC721Pausable_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev See {ERC721-_beforeTokenTransfer}.
                   *
                   * Requirements:
                   *
                   * - the contract must not be paused.
                   */
                  function _beforeTokenTransfer(
                      address from,
                      address to,
                      uint256 firstTokenId,
                      uint256 batchSize
                  ) internal virtual override {
                      super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
                      require(!paused(), "ERC721Pausable: token transfer while paused");
                  }
                  /**
                   * @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
              // Copyright (c) 2023 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {AccessControlEnumerableUpgradeable} from
                  "openzeppelin-contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
              contract SteerableAccessControlEnumerableUpgradeable is AccessControlEnumerableUpgradeable {
                  /// @notice The default role intended to perform access-restricted actions.
                  /// @dev We are using this instead of DEFAULT_ADMIN_ROLE because the latter
                  /// is intended to grant/revoke roles and will be secured differently.
                  bytes32 public constant DEFAULT_STEERING_ROLE = keccak256("DEFAULT_STEERING_ROLE");
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              /**
               * @notice Basic interface for a contract providing sellable content.
               */
              interface ISellable {
                  /**
                   * @notice Handles the sale of sellable content.
                   * @dev This is usually only callable by Sellers.
                   */
                  function handleSale(address to, uint64 num, bytes calldata data) external payable;
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.16;
              import {AccessControlEnumerable} from "ethier/utils/AccessControlEnumerable.sol";
              import {ISellable, ImmutableSellableCallbacker, SettableSellableCallbacker} from "../base/SellableCallbacker.sol";
              /**
               * @notice Base contract for seller presets that call back to a sellable contract.
               */
              contract AccessControlled is AccessControlEnumerable {
                  constructor(address admin, address steerer) {
                      _grantRole(DEFAULT_ADMIN_ROLE, admin);
                      _grantRole(DEFAULT_STEERING_ROLE, steerer);
                  }
              }
              /**
               * @notice Base contract for seller presets that call back to a sellable contract.
               */
              contract CallbackerWithAccessControl is ImmutableSellableCallbacker, AccessControlled {
                  constructor(address admin, address steerer, ISellable sellable_)
                      ImmutableSellableCallbacker(sellable_)
                      AccessControlled(admin, steerer)
                  {}
              }
              /**
               * @notice Base contract for seller presets that call back to a sellable contract.
               */
              contract SettableCallbackerWithAccessControl is SettableSellableCallbacker, AccessControlled {
                  constructor(address admin, address steerer) AccessControlled(admin, steerer) {}
                  function setSellable(ISellable sellable_) external onlyRole(DEFAULT_STEERING_ROLE) {
                      _setSellable(sellable_);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {ReentrancyGuard} from "openzeppelin-contracts/security/ReentrancyGuard.sol";
              import {PurchaseExecuter} from "../interfaces/PurchaseExecuter.sol";
              /**
               * @notice Abstract base contract for all `Seller`s.
               * @dev The intention of this contract is to provide an extensible base for various kinds of Seller modules that can be
               * flexibly composed to build more complex sellers - allowing effective code reuse.
               * Derived contracts are intended to implement their logic by overriding and extending the `_checkAndModifyPurchase` and
               * `_beforePurchase` hooks (calling the parent implementation(s) to compose logic). The former is intended to perform
               * manipulations and checks of the input data; the latter to update the internal state of the module.
               * Final sellers will compose these modules and expose an addition external purchase function for buyers.
               */
              abstract contract Seller is PurchaseExecuter, ReentrancyGuard {
                  uint256 internal constant _UNDEFINED_COST = type(uint256).max;
                  /**
                   * @notice Internal function handling a given purchase, performing checks and input manipulations depending on the
                   * logic in the hooks.
                   * @param to The receiver of the purchase
                   * @param num Number of requested purchases
                   * @param externalTotalCost Total cost of the purchase
                   * @dev This function is intended to be wrapped in an external method for final sellers. Since we cannot foresee
                   * what logic will be implemented in the hooks, we added a reentrancy guard for safety.
                   */
                  function _purchase(address to, uint64 num, uint256 externalTotalCost, bytes memory data)
                      internal
                      virtual
                      nonReentrant
                  {
                      uint256 totalCost;
                      (to, num, totalCost) = _checkAndModifyPurchase(to, num, externalTotalCost, data);
                      _beforePurchase(to, num, totalCost, data);
                      _executePurchase(to, num, totalCost, data);
                  }
                  // =================================================================================================================
                  //                           Hooks
                  // =================================================================================================================
                  /**
                   * @notice Hook that is called before handling a purchase (even before `_beforePurchase`)
                   * @dev The intent of this hook is to manipulate the input data and perform  checks before actually handling the
                   * purchase.
                   * @param to The receiver of the purchase
                   * @param num Number of requested purchases
                   * @param totalCost Total cost of the purchase
                   * @dev This function MUST return sensible values, since these will be used to perfom the purchase.
                   */
                  function _checkAndModifyPurchase(address to, uint64 num, uint256 totalCost, bytes memory)
                      internal
                      view
                      virtual
                      returns (address, uint64, uint256)
                  {
                      return (to, num, totalCost);
                  }
                  /**
                   * @notice Hook that is called before handling a purchase.
                   * @dev The intent of this hook is to update the internal state of the seller (module) if necessary.
                   * It is critical that the updates happen here and not in `_checkAndModifyPurchase` because only after calling that
                   * function the purchase parameters can be considered fixed.
                   */
                  function _beforePurchase(address to, uint64 num, uint256 totalCost, bytes memory data) internal virtual {
                      // solhint-disable-line no-empty-blocks
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              /**
               * @notice Module to rate limit a certain action per discrete block of time.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract NonRollingRateLimited {
                  /**
                   * @notice Thrown if on attempts to exceed the rate limit.
                   */
                  error ExceedingRateLimit(uint256 requested, uint256 numLeft);
                  /**
                   * @notice The duration of a period in seconds.
                   */
                  uint64 private immutable _periodLength;
                  /**
                   * @notice The index of the last period for which an action has been performed.
                   */
                  uint64 private _lastPeriod;
                  /**
                   * @notice The maximum number of actions that can be performed in a period.
                   */
                  uint64 private __maxActionsPerPeriod;
                  /**
                   * @notice The number of actions that have been performed in the current period.
                   * @dev Will automatically be reset to 0 in `rateLimited` at the start of each period.
                   */
                  uint64 private __performedCurrentPeriod;
                  constructor(uint64 maxActionsPerPeriod, uint64 periodLength) {
                      _periodLength = periodLength;
                      _setMaxActionsPerPeriod(maxActionsPerPeriod);
                  }
                  /**
                   * @notice Helper function to get the index of the current period.
                   */
                  function _currentPeriod() private view returns (uint64) {
                      return uint64(block.timestamp / _periodLength);
                  }
                  /**
                   * @notice Sets the maximum number of actions per period.
                   */
                  function _setMaxActionsPerPeriod(uint64 maxActionsPerPeriod) internal {
                      __maxActionsPerPeriod = maxActionsPerPeriod;
                  }
                  /**
                   * @notice Returns the maximum number of actions per period.
                   */
                  function _maxActionsPerPeriod() internal view returns (uint64) {
                      return __maxActionsPerPeriod;
                  }
                  /**
                   * @notice Keeps track of the number of performed actions.
                   * @dev Reverts if the maximum number of actions per period is exceeded.
                   */
                  function _checkAndTrackRateLimit(uint64 requested) internal {
                      uint64 performed = _performedCurrentPeriod();
                      uint64 left = __maxActionsPerPeriod - performed;
                      if (requested > left) {
                          revert ExceedingRateLimit(requested, left);
                      }
                      __performedCurrentPeriod = performed + requested;
                      _lastPeriod = _currentPeriod();
                  }
                  /**
                   * @notice The number of actions performed in the current period.
                   */
                  function _performedCurrentPeriod() internal view returns (uint64) {
                      if (_currentPeriod() > _lastPeriod) {
                          return 0;
                      }
                      return __performedCurrentPeriod;
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.0;
              /**
               * @notice Interface for a redeemable Voucher token preventing double spending
               * through internal book-keeping (e.g. burning the token, token property, etc.).
               * @dev Voucher tokens are intendent to be redeemed through a redeemer contract.
               */
              interface IRedeemableToken {
                  /**
                   * @notice Thrown if the redemption caller is not allowed to spend a given
                   * voucher.
                   */
                  error RedeemerCallerNotAllowedToSpendVoucher(address sender, uint256 tokenId);
                  /**
                   * @notice Interface through which a `IRedeemer` contract informs the
                   * voucher about its redemption.
                   * @param sender The address that initiate the redemption on the
                   * redeemer contract.
                   * @param tokenId The voucher token to be redeemed.
                   * @dev This function MUST be called by redeemer contracts.
                   * @dev MUST revert with `RedeemerNotApproved` if the calling redeemer
                   * contract is not approved to spend this voucher.
                   * @dev MUST revert with `RedeemerCallerNotAllowedToSpendVoucher` if
                   * sender is not allowed to spend tokenId.
                   */
                  function redeem(address sender, uint256 tokenId) external;
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {ERC721A, ERC721ACommon, BaseTokenURI, ERC721ACommonBaseTokenURI} from "ethier/erc721/BaseTokenURI.sol";
              import {OperatorFilterOS} from "ethier/erc721/OperatorFilterOS.sol";
              import {ERC4906} from "ethier/erc721/ERC4906.sol";
              import {IEntropyOracle} from "proof/entropy/IEntropyOracle.sol";
              import {RedeemableERC721ACommon} from "proof/redemption/voucher/RedeemableERC721ACommon.sol";
              import {SellableERC721ACommon} from "proof/sellers/sellable/SellableERC721ACommon.sol";
              import {MythicEggSampler} from "./MythicEggSampler.sol";
              import {MythicEggActivator} from "./MythicEggActivator.sol";
              /**
               * @title Mythics: Egg
               * @notice A redeemable token claimable by all diamond nested Moonbirds.
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              contract MythicsEgg is
                  ERC721ACommonBaseTokenURI,
                  OperatorFilterOS,
                  SellableERC721ACommon,
                  RedeemableERC721ACommon,
                  MythicEggSampler,
                  MythicEggActivator
              {
                  constructor(address admin, address steerer, address payable secondaryReceiver, IEntropyOracle oracle)
                      ERC721ACommon(admin, steerer, "Mythics: Egg", "EGG", secondaryReceiver, 500)
                      BaseTokenURI("https://metadata.proof.xyz/mythics/egg/")
                      MythicEggSampler(oracle)
                  {
                      _setEggProbabilities([uint64(0), uint64(40), uint64(60)]);
                  }
                  // =================================================================================================================
                  //                          Information Getter
                  // =================================================================================================================
                  /**
                   * @notice Encodes information about a token.
                   * @dev Intended to be used off-chain.
                   */
                  struct TokenInfo {
                      bool revealed;
                      EggType eggType;
                      bool activated;
                  }
                  /**
                   * @notice Returns information about given egg token.
                   * @dev Not optimised, intended to be used off-chain only.
                   */
                  function tokenInfos(uint256[] calldata tokenIds) external view returns (TokenInfo[] memory) {
                      TokenInfo[] memory infos = new TokenInfo[](tokenIds.length);
                      for (uint256 i = 0; i < tokenIds.length; i++) {
                          (EggType eggT, bool revealed) = eggType(tokenIds[i]);
                          infos[i] = TokenInfo({revealed: revealed, eggType: eggT, activated: activated(tokenIds[i])});
                      }
                      return infos;
                  }
                  // =================================================================================================================
                  //                          Steering
                  // =================================================================================================================
                  /**
                   * @notice Sets the probability distribution for egg types.
                   */
                  function setEggProbabilities(uint64[NUM_EGG_TYPES] memory pdf) external onlyRole(DEFAULT_STEERING_ROLE) {
                      _setEggProbabilities(pdf);
                  }
                  /**
                   * @notice Sets the entropy oracle.
                   */
                  function setEntropyOracle(IEntropyOracle newOracle) external onlyRole(DEFAULT_STEERING_ROLE) {
                      entropyOracle = newOracle;
                  }
                  /**
                   * @notice Sets the maximum number of activations per day.
                   */
                  function setMaxNumActivationsPerDay(uint32 maxNumActivationsPerDay) external onlyRole(DEFAULT_STEERING_ROLE) {
                      _setMaxNumActivationsPerDay(maxNumActivationsPerDay);
                  }
                  /**
                   * @notice Activates an array of eggs.
                   */
                  function activate(uint256[] calldata tokenIds) external onlyRole(DEFAULT_STEERING_ROLE) {
                      _activate(tokenIds);
                  }
                  // =================================================================================================================
                  //                          Inheritance Resolution
                  // =================================================================================================================
                  /**
                   * @inheritdoc SellableERC721ACommon
                   * @dev Registers the minted tokens for sampling.
                   */
                  function _handleSale(address to, uint64 num, bytes calldata data) internal virtual override {
                      uint256 startTokenId = _nextTokenId();
                      for (uint256 i; i < num; ++i) {
                          _registerForSampling(startTokenId + i);
                      }
                      super._handleSale(to, num, data);
                  }
                  function _exists(uint256 tokenId)
                      internal
                      view
                      virtual
                      override(ERC721A, MythicEggActivator, MythicEggSampler)
                      returns (bool)
                  {
                      return ERC721A._exists(tokenId);
                  }
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721ACommon, ERC721ACommonBaseTokenURI, SellableERC721ACommon, RedeemableERC721ACommon, ERC4906)
                      returns (bool)
                  {
                      return RedeemableERC721ACommon.supportsInterface(interfaceId)
                          || SellableERC721ACommon.supportsInterface(interfaceId) || ERC4906.supportsInterface(interfaceId)
                          || ERC721ACommonBaseTokenURI.supportsInterface(interfaceId);
                  }
                  function _baseURI() internal view virtual override(ERC721A, ERC721ACommonBaseTokenURI) returns (string memory) {
                      return ERC721ACommonBaseTokenURI._baseURI();
                  }
                  function setApprovalForAll(address operator, bool approved) public virtual override(ERC721A, OperatorFilterOS) {
                      OperatorFilterOS.setApprovalForAll(operator, approved);
                  }
                  function approve(address operator, uint256 tokenId) public payable virtual override(ERC721A, OperatorFilterOS) {
                      OperatorFilterOS.approve(operator, tokenId);
                  }
                  function transferFrom(address from, address to, uint256 tokenId)
                      public
                      payable
                      virtual
                      override(ERC721A, OperatorFilterOS)
                  {
                      OperatorFilterOS.transferFrom(from, to, tokenId);
                  }
                  function safeTransferFrom(address from, address to, uint256 tokenId)
                      public
                      payable
                      virtual
                      override(ERC721A, OperatorFilterOS)
                  {
                      OperatorFilterOS.safeTransferFrom(from, to, tokenId);
                  }
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data)
                      public
                      payable
                      virtual
                      override(ERC721A, OperatorFilterOS)
                  {
                      OperatorFilterOS.safeTransferFrom(from, to, tokenId, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {ISellable} from "../interfaces/ISellable.sol";
              import {PurchaseExecuter} from "../interfaces/PurchaseExecuter.sol";
              /**
               * @notice Executes a purchase by calling the purchase interface of a `ISellable`  contract.
               */
              abstract contract SellableCallbacker is PurchaseExecuter {
                  /**
                   * @notice Emitted when the callback to the `ISellable` contract fails.
                   */
                  error CallbackFailed(bytes reason);
                  function _sellable(address to, uint64 num, uint256 cost, bytes memory data) internal virtual returns (ISellable);
                  /**
                   * @notice Executes a purchase by calling the sale interface of a `ISellable` contract.
                   */
                  function _executePurchase(address to, uint64 num, uint256 cost, bytes memory data) internal virtual override {
                      ISellable sellable = _sellable(to, num, cost, data);
                      try sellable.handleSale{value: cost}(to, num, data) {}
                      catch (bytes memory reason) {
                          // TODO(dave): the reason is empty if the above call runs OutOfFund. Explore ways to bubble this up more cleanly.
                          revert CallbackFailed(reason);
                      }
                  }
              }
              /**
               * @notice Executes a purchase by calling the purchase interface of a `ISellable`  contract.
               */
              abstract contract ImmutableSellableCallbacker is SellableCallbacker {
                  /**
                   * @notice The `ISellable` contract that will be called to execute the purchase.
                   */
                  ISellable public immutable sellable;
                  constructor(ISellable sellable_) {
                      sellable = sellable_;
                  }
                  function _sellable(address, uint64, uint256, bytes memory) internal virtual override returns (ISellable) {
                      return sellable;
                  }
              }
              /**
               * @notice Executes a purchase by calling the purchase interface of a `ISellable`  contract.
               */
              abstract contract SettableSellableCallbacker is SellableCallbacker {
                  /**
                   * @notice The `ISellable` contract that will be called to execute the purchase.
                   */
                  ISellable private _sellable_;
                  function _sellable(address, uint64, uint256, bytes memory) internal virtual override returns (ISellable) {
                      return _sellable_;
                  }
                  function _setSellable(ISellable sellable_) internal {
                      _sellable_ = sellable_;
                  }
                  function sellable() public view returns (ISellable) {
                      return _sellable_;
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {IRedeemableToken} from "./interfaces/IRedeemableToken.sol";
              /**
               * @notice Base contract to redeem redeemable tokens.
               */
              contract RedeemableTokenRedeemer {
                  /**
                   * @notice Emitted when the callback to the `IRedeemableToken` contract fails.
                   */
                  error RedeemableCallbackFailed(IRedeemableToken token, uint256 tokenId, bytes reason);
                  /**
                   * @notice Redeems a redeemable token
                   */
                  function _redeem(IRedeemableToken redeemable, uint256 tokenId) internal {
                      try redeemable.redeem(msg.sender, tokenId) {}
                      catch (bytes memory reason) {
                          revert RedeemableCallbackFailed(redeemable, tokenId, reason);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 Proof Holdings Inc.
              pragma solidity ^0.8.0;
              interface IEntropyOracleEvents {
                  /**
                   * @notice Emitted when entropy is requested, to signal the oracle.
                   */
                  event EntropyRequested(uint256 indexed blockNumber);
                  /**
                   * @notice Emitted when an entropy request is fulfilled.
                   */
                  event EntropyProvided(uint256 indexed blockNumber, bytes32 entropy);
              }
              interface IEntropyOracle is IEntropyOracleEvents {
                  /**
                   * @notice Equivalent to requestEntropy(block.number). This is safe as the request will only be fulfilled once the
                   * block is mined.
                   */
                  function requestEntropy() external;
                  /**
                   * @notice Signal to the oracle that entropy is requested for the specified block. The request will only be
                   * fulfilled once the block is mined.
                   * @dev NOTE that this must be used with care. If a historical block is requested, the entropy may be known by a bad
                   * actor. It is only safe to request entropy for a historical block i.f.f. said block was commited to before it was
                   * mined.
                   */
                  function requestEntropy(uint256 blockNumber) external;
                  /**
                   * @notice Entropy values, keyed by block number.
                   * @dev Not all blocks will have entropy available; check that the returned value is non-zero.
                   */
                  function blockEntropy(uint256) external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              interface MythicsEggErrors {
                  /**
                   * @notice Thrown if one attempts an action on a nonexistent egg.
                   */
                  error NonexistentEgg(uint256 tokenId);
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {IEntropyOracle} from "proof/entropy/IEntropyOracle.sol";
              /**
               * @notice Helper libray for sampling from a discrete probability distribution.
               */
              library StochasticSamplingLib {
                  /**
                   * @notice Computes the cumulative probability distribution from a discrete probability distribution.
                   */
                  function computeCDF(uint64[] memory pdf) internal pure returns (uint64[] memory) {
                      uint64[] memory cdf = new uint64[](pdf.length);
                      cdf[0] = pdf[0];
                      for (uint256 i = 1; i < pdf.length; ++i) {
                          cdf[i] = cdf[i - 1] + pdf[i];
                      }
                      return cdf;
                  }
                  /**
                   * @notice Samples from a discrete cumulative probability distribution.
                   * @dev This function assumes that rand is uniform in [0,2^256) and that `cdf[cdf.length - 1] << 2^256`. If not the
                   * outcome will be biased
                   */
                  function sampleWithCDF(uint256 rand, uint64[] memory cdf) internal pure returns (uint256) {
                      rand = rand % cdf[cdf.length - 1];
                      for (uint256 i; i < cdf.length; ++i) {
                          if (rand < cdf[i]) {
                              return i;
                          }
                      }
                      // This will never be reached given the above bounds of rand.
                      assert(false);
                      return 0;
                  }
              }
              /**
               * @notice A contract that can sample token traits from discrete probability distributions.
               * @dev The probability distributions and seed derivation functions are implemented in the inheriting contracts.
               * @dev The functions defined here might be gas-heavy and are therefore intended to be used in view-calls only.
               */
              abstract contract StochasticSampler {
                  /**
                   * @notice Returns a random seed for a given token and a boolean indicating whether the seed is available.
                   */
                  function _seed(uint256 tokenId) internal view virtual returns (bytes32, bool);
                  /**
                   * @notice Returns the cumulative probability distribution for a given trait of a given token.
                   */
                  function _cdf(uint256 tokenId, uint256 traitId) internal view virtual returns (uint64[] memory);
                  /**
                   * @notice Samples a trait for a given token.
                   * @dev Returns the sampled trait and a boolean indicating whether the trait was already revealed (i.e. if the seed
                   * for the given token is available).
                   */
                  function _sampleTrait(uint256 tokenId, uint256 traitId) internal view returns (uint256, bool) {
                      (bytes32 seed, bool revealed) = _seed(tokenId);
                      seed = keccak256(abi.encodePacked(seed, traitId));
                      return (StochasticSamplingLib.sampleWithCDF(uint256(seed), _cdf(tokenId, traitId)), revealed);
                  }
              }
              /**
               * @notice A contract that can sample token traits from discrete probability distributions loaded from storage.
               */
              abstract contract StochasticSamplerWithCDFStorage is StochasticSampler {
                  using StochasticSamplingLib for uint64[];
                  /**
                   * @notice Thrown if the traitId is invalid, i.e. if it exceeds the number of traits.
                   */
                  error InvalidTraitId(uint256 traitId);
                  /**
                   * @notice Thrown if the length of the given PDF does not match the number of realisations in a given trait.
                   */
                  error IncorrectPDFLength(uint256 gotLength, uint256 traitId, uint256 wantLength);
                  /**
                   * @notice Thrown if the given PDF cannot be normalised, i.e. if the sum of the probabilities is zero.
                   */
                  error ConstantZeroPDF();
                  /**
                   * @notice The number of realisations for each trait.
                   */
                  uint256[] private _numPerTrait;
                  /**
                   * @notice The cumulative probability distributions for each trait.
                   * @dev Indexed by traitId, distributionVersion, sample.
                   * @dev The distributionVersion is intended to allow having multiple "versions" of the probability distributions.
                   */
                  uint64[][][] private _cdfs;
                  constructor(uint256[] memory numPerTrait) {
                      _numPerTrait = numPerTrait;
                      for (uint256 i; i < numPerTrait.length; ++i) {
                          _cdfs.push(new uint64[][](0));
                      }
                      assert(_cdfs.length == numPerTrait.length);
                  }
                  /**
                   * @notice Adds a new probability distribution for a given trait.
                   */
                  function _pushProbabilities(uint256 traitId, uint64[] memory pdf) internal {
                      if (traitId >= _numPerTrait.length) {
                          revert InvalidTraitId(traitId);
                      }
                      if (pdf.length != _numPerTrait[traitId]) {
                          revert IncorrectPDFLength(pdf.length, traitId, _numPerTrait[traitId]);
                      }
                      uint64[] memory cdf = pdf.computeCDF();
                      if (cdf[cdf.length - 1] == 0) {
                          revert ConstantZeroPDF();
                      }
                      _cdfs[traitId].push(cdf);
                  }
                  /**
                   * @notice Returns the version/index of the latest probability distribution for a given trait.
                   */
                  function _latestDistributionVersion(uint256 traitId) internal view returns (uint256) {
                      return _cdfs[traitId].length - 1;
                  }
                  /**
                   * @notice Returns the version/index of the probability distribution that is used for a given token and trait.
                   * @dev This function is intended to be overridden by inheriting contracts.
                   */
                  function _distributionVersion(uint256 tokenId, uint256 traitId) internal view virtual returns (uint256);
                  /**
                   * @inheritdoc StochasticSampler
                   * @dev Returns the probability distribution that is index by `_distributionVersion`.
                   */
                  function _cdf(uint256 tokenId, uint256 traitId) internal view virtual override returns (uint64[] memory) {
                      if (traitId >= _numPerTrait.length) {
                          revert InvalidTraitId(traitId);
                      }
                      return _cdfs[traitId][_distributionVersion(tokenId, traitId)];
                  }
              }
              /**
               * @notice A contract that can sample token traits from discrete probability distributions using entropy provided by the
               * EntropyOracle.
               */
              abstract contract StochasticSamplerWithOracle is StochasticSampler {
                  /**
                   * @notice The entropy oracle.
                   */
                  IEntropyOracle public entropyOracle;
                  constructor(IEntropyOracle entropyOracle_) {
                      entropyOracle = entropyOracle_;
                  }
                  /**
                   * @inheritdoc StochasticSampler
                   * @dev Uses the entropy of the block at `_revealBlockNumber(tokenId)`.
                   */
                  function _seed(uint256 tokenId) internal view virtual override returns (bytes32, bool) {
                      bytes32 entropy = entropyOracle.blockEntropy(_revealBlockNumber(tokenId));
                      return (keccak256(abi.encode(entropy, tokenId)), entropy != 0);
                  }
                  /**
                   * @notice The blocknumber at which a given token will be revealed.
                   * @dev The entropy provided by `entropyOracle` for this block will be used as seed for trait sampling.
                   */
                  function _revealBlockNumber(uint256 tokenId) internal view virtual returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControl {
                  /**
                   * @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: 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.9.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
                   *
                   * 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.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.9.0) (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._
               */
              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) {
                          AddressUpgradeable.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) {
                          AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), 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.9.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165Upgradeable.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721Upgradeable is IERC165Upgradeable {
                  /**
                   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                   */
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                   */
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                   * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                   * understand this adds an external call which potentially creates a reentrancy vulnerability.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
              pragma solidity ^0.8.0;
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              interface IERC721ReceiverUpgradeable {
                  /**
                   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                   * by `operator` from `from`, this function is called.
                   *
                   * It must return its Solidity selector to confirm the token transfer.
                   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                   *
                   * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721Upgradeable.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721MetadataUpgradeable is IERC721Upgradeable {
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts 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.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/MathUpgradeable.sol";
              import "./math/SignedMathUpgradeable.sol";
              /**
               * @dev String operations.
               */
              library StringsUpgradeable {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = MathUpgradeable.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, MathUpgradeable.log256(value) + 1);
                      }
                  }
                  /**
                   * @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] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                  function __ERC165_init() internal onlyInitializing {
                  }
                  function __ERC165_init_unchained() internal onlyInitializing {
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165Upgradeable).interfaceId;
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)
              pragma solidity ^0.8.0;
              import "../../interfaces/IERC2981Upgradeable.sol";
              import "../../utils/introspection/ERC165Upgradeable.sol";
              import "../../proxy/utils/Initializable.sol";
              /**
               * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
               *
               * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
               * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
               *
               * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
               * fee is specified in basis points by default.
               *
               * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
               * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
               * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
               *
               * _Available since v4.5._
               */
              abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
                  function __ERC2981_init() internal onlyInitializing {
                  }
                  function __ERC2981_init_unchained() internal onlyInitializing {
                  }
                  struct RoyaltyInfo {
                      address receiver;
                      uint96 royaltyFraction;
                  }
                  RoyaltyInfo private _defaultRoyaltyInfo;
                  mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
                      return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @inheritdoc IERC2981Upgradeable
                   */
                  function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
                      RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
                      if (royalty.receiver == address(0)) {
                          royalty = _defaultRoyaltyInfo;
                      }
                      uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
                      return (royalty.receiver, royaltyAmount);
                  }
                  /**
                   * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
                   * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
                   * override.
                   */
                  function _feeDenominator() internal pure virtual returns (uint96) {
                      return 10000;
                  }
                  /**
                   * @dev Sets the royalty information that all ids in this contract will default to.
                   *
                   * Requirements:
                   *
                   * - `receiver` cannot be the zero address.
                   * - `feeNumerator` cannot be greater than the fee denominator.
                   */
                  function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
                      require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                      require(receiver != address(0), "ERC2981: invalid receiver");
                      _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
                  }
                  /**
                   * @dev Removes default royalty information.
                   */
                  function _deleteDefaultRoyalty() internal virtual {
                      delete _defaultRoyaltyInfo;
                  }
                  /**
                   * @dev Sets the royalty information for a specific token id, overriding the global default.
                   *
                   * Requirements:
                   *
                   * - `receiver` cannot be the zero address.
                   * - `feeNumerator` cannot be greater than the fee denominator.
                   */
                  function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
                      require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
                      require(receiver != address(0), "ERC2981: Invalid parameters");
                      _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
                  }
                  /**
                   * @dev Resets royalty information for the token id back to the global default.
                   */
                  function _resetTokenRoyalty(uint256 tokenId) internal virtual {
                      delete _tokenRoyaltyInfo[tokenId];
                  }
                  /**
                   * @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[48] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
              pragma solidity ^0.8.0;
              import "../utils/ContextUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Contract module which allows children to implement an emergency stop
               * mechanism that can be triggered by an authorized account.
               *
               * This module is used through inheritance. It will make available the
               * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
               * the functions of your contract. Note that they will not be pausable by
               * simply including this module, only once the modifiers are put in place.
               */
              abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
                  /**
                   * @dev Emitted when the pause is triggered by `account`.
                   */
                  event Paused(address account);
                  /**
                   * @dev Emitted when the pause is lifted by `account`.
                   */
                  event Unpaused(address account);
                  bool private _paused;
                  /**
                   * @dev Initializes the contract in unpaused state.
                   */
                  function __Pausable_init() internal onlyInitializing {
                      __Pausable_init_unchained();
                  }
                  function __Pausable_init_unchained() internal onlyInitializing {
                      _paused = false;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is not paused.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  modifier whenNotPaused() {
                      _requireNotPaused();
                      _;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is paused.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  modifier whenPaused() {
                      _requirePaused();
                      _;
                  }
                  /**
                   * @dev Returns true if the contract is paused, and false otherwise.
                   */
                  function paused() public view virtual returns (bool) {
                      return _paused;
                  }
                  /**
                   * @dev Throws if the contract is paused.
                   */
                  function _requireNotPaused() internal view virtual {
                      require(!paused(), "Pausable: paused");
                  }
                  /**
                   * @dev Throws if the contract is not paused.
                   */
                  function _requirePaused() internal view virtual {
                      require(paused(), "Pausable: not paused");
                  }
                  /**
                   * @dev Triggers stopped state.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  function _pause() internal virtual whenNotPaused {
                      _paused = true;
                      emit Paused(_msgSender());
                  }
                  /**
                   * @dev Returns to normal state.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  function _unpause() internal virtual whenPaused {
                      _paused = false;
                      emit Unpaused(_msgSender());
                  }
                  /**
                   * @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.5.0) (access/AccessControlEnumerable.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControlEnumerableUpgradeable.sol";
              import "./AccessControlUpgradeable.sol";
              import "../utils/structs/EnumerableSetUpgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Extension of {AccessControl} that allows enumerating the members of each role.
               */
              abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerableUpgradeable, AccessControlUpgradeable {
                  function __AccessControlEnumerable_init() internal onlyInitializing {
                  }
                  function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
                  }
                  using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
                  mapping(bytes32 => EnumerableSetUpgradeable.AddressSet) private _roleMembers;
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControlEnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @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
                   * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
                   * for more information.
                   */
                  function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
                      return _roleMembers[role].at(index);
                  }
                  /**
                   * @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) public view virtual override returns (uint256) {
                      return _roleMembers[role].length();
                  }
                  /**
                   * @dev Overload {_grantRole} to track enumerable memberships
                   */
                  function _grantRole(bytes32 role, address account) internal virtual override {
                      super._grantRole(role, account);
                      _roleMembers[role].add(account);
                  }
                  /**
                   * @dev Overload {_revokeRole} to track enumerable memberships
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual override {
                      super._revokeRole(role, account);
                      _roleMembers[role].remove(account);
                  }
                  /**
                   * @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) (security/ReentrancyGuard.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Contract module that helps prevent reentrant calls to a function.
               *
               * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
               * available, which can be applied to functions to make sure there are no nested
               * (reentrant) calls to them.
               *
               * Note that because there is a single `nonReentrant` guard, functions marked as
               * `nonReentrant` may not call one another. This can be worked around by making
               * those functions `private`, and then adding `external` `nonReentrant` entry
               * points to them.
               *
               * TIP: If you would like to learn more about reentrancy and alternative ways
               * to protect against it, check out our blog post
               * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
               */
              abstract contract ReentrancyGuard {
                  // Booleans are more expensive than uint256 or any type that takes up a full
                  // word because each write operation emits an extra SLOAD to first read the
                  // slot's contents, replace the bits taken up by the boolean, and then write
                  // back. This is the compiler's defense against contract upgrades and
                  // pointer aliasing, and it cannot be disabled.
                  // The values being non-zero value makes deployment a bit more expensive,
                  // but in exchange the refund on every call to nonReentrant will be lower in
                  // amount. Since refunds are capped to a percentage of the total
                  // transaction's gas, it is best to keep them low in cases like this one, to
                  // increase the likelihood of the full refund coming into effect.
                  uint256 private constant _NOT_ENTERED = 1;
                  uint256 private constant _ENTERED = 2;
                  uint256 private _status;
                  constructor() {
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      _nonReentrantBefore();
                      _;
                      _nonReentrantAfter();
                  }
                  function _nonReentrantBefore() private {
                      // On the first call to nonReentrant, _status will be _NOT_ENTERED
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                  }
                  function _nonReentrantAfter() private {
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              /**
               * @notice Interface to execute purchases in `Seller`s.
               * @dev This executes the final purchase. This can be anything from minting ERC721 tokens to transfering funds, etc.
               */
              abstract contract PurchaseExecuter {
                  function _executePurchase(address to, uint64 num, uint256 cost, bytes memory data) internal virtual;
              }
              // SPDX-License-Identifier: MIT
              // Copyright (c) 2023 the ethier authors (github.com/divergencetech/ethier)
              pragma solidity >=0.8.0 <0.9.0;
              import {Address} from "@openzeppelin/contracts/utils/Address.sol";
              import {DefaultOperatorFilterer} from "operator-filter-registry/src/DefaultOperatorFilterer.sol";
              import {ERC721A, ERC721ACommon} from "./ERC721ACommon.sol";
              /**
               * @notice ERC721ACommon extension that adds Opensea's operator filtering.
               */
              abstract contract OperatorFilterOS is ERC721ACommon, DefaultOperatorFilterer {
                  using Address for address;
                  /**
                   * @notice Calling the operator filter registry with given calldata.
                   * @dev The registry contract did not foresee role-based contract access
                   * control -- only the contract itself, or its (EIP-173) owner is allowed to
                   * change subscription settings. To work around this, we enforce
                   * authorisation here and forward arbitrary calldata to the registry.
                   * Use with care!
                   */
                  function callOperatorFilterRegistry(bytes calldata cdata)
                      external
                      onlyRole(DEFAULT_STEERING_ROLE)
                      returns (bytes memory)
                  {
                      return address(OPERATOR_FILTER_REGISTRY).functionCall(cdata);
                  }
                  // =========================================================================
                  //                           Operator filtering
                  // =========================================================================
                  function setApprovalForAll(address operator, bool approved)
                      public
                      virtual
                      override
                      onlyAllowedOperatorApproval(operator)
                  {
                      super.setApprovalForAll(operator, approved);
                  }
                  function approve(address operator, uint256 tokenId)
                      public
                      payable
                      virtual
                      override
                      onlyAllowedOperatorApproval(operator)
                  {
                      super.approve(operator, tokenId);
                  }
                  function transferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public payable virtual override onlyAllowedOperator(from) {
                      super.transferFrom(from, to, tokenId);
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId
                  ) public payable virtual override onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId);
                  }
                  function safeTransferFrom(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) public payable virtual override onlyAllowedOperator(from) {
                      super.safeTransferFrom(from, to, tokenId, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.16;
              import {ERC721ACommon} from "ethier/erc721/ERC721ACommon.sol";
              import {BaseRedeemableToken} from "./BaseRedeemableToken.sol";
              /**
               * @notice An ERC721 token intended to act as freely tradeable voucher.
               * @dev This is mainly a convenience wrapper.
               */
              abstract contract RedeemableERC721ACommon is BaseRedeemableToken, ERC721ACommon {
                  // =========================================================================
                  //                           Internals
                  // =========================================================================
                  /**
                   * @notice Allows spending if the sender is the owner of or approved for
                   * transfers of a given token.
                   */
                  function _isSenderAllowedToSpend(address sender, uint256 tokenId)
                      internal
                      view
                      virtual
                      override
                      returns (bool result)
                  {
                      address tokenOwner = ownerOf(tokenId);
                      return (sender == tokenOwner) || isApprovedForAll(tokenOwner, sender) || (sender == getApproved(tokenId));
                  }
                  /**
                   * @notice Redeeming a voucher token burns it.
                   */
                  function _doRedeem(address, uint256 tokenId) internal virtual override {
                      _burn(tokenId);
                  }
                  /**
                   * @notice Overrides supportsInterface as required by inheritance.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(BaseRedeemableToken, ERC721ACommon)
                      returns (bool)
                  {
                      return BaseRedeemableToken.supportsInterface(interfaceId) || ERC721ACommon.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {ERC721ACommon} from "ethier/erc721/ERC721ACommon.sol";
              import {AccessControlEnumerable, BaseSellable} from "./BaseSellable.sol";
              /**
               * @notice Base contract for sellable ERC721ACommon tokens.
               */
              abstract contract SellableERC721ACommon is BaseSellable, ERC721ACommon {
                  /**
                   * @inheritdoc BaseSellable
                   */
                  function _handleSale(address to, uint64 num, bytes calldata) internal virtual override {
                      _mint(to, num);
                  }
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(ERC721ACommon, AccessControlEnumerable)
                      returns (bool)
                  {
                      return ERC721ACommon.supportsInterface(interfaceId) || AccessControlEnumerable.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.15;
              import {BitMaps} from "openzeppelin-contracts/utils/structs/BitMaps.sol";
              import {ERC4906} from "ethier/erc721/ERC4906.sol";
              import {MythicsEggErrors} from "./MythicsEggErrors.sol";
              interface MythicEggActivatorEvents {
                  /**
                   * @notice Emitted when an egg has been activated.
                   */
                  event EggActivated(uint256 indexed tokenId);
              }
              /**
               * @title Mythics: Egg activation module
               * @author David Huber (@cxkoda)
               * @custom:reviewer Arran Schlosberg (@divergencearran)
               */
              abstract contract MythicEggActivator is ERC4906, MythicsEggErrors, MythicEggActivatorEvents {
                  using BitMaps for BitMaps.BitMap;
                  /**
                   * @notice Throws if activating an egg that has already been activated.
                   */
                  error EggAlreadyActivated(uint256);
                  /**
                   * @notice Thrown if one tries to activate too many eggs at once.
                   */
                  error ActivatingTooManyEggs(uint256 requested, uint256 numLeft);
                  /**
                   * @notice Keeps track of eggs that have already been activated.
                   */
                  BitMaps.BitMap private _activated;
                  /**
                   * @notice The maximum number of eggs that can be activated during one day.
                   */
                  uint32 internal _maxNumActivationsPerDay = 50;
                  /**
                   * @notice The last day on which an egg was activated.
                   */
                  uint32 private _lastActivationDay;
                  /**
                   * @notice The number of eggs that have been activated today.
                   * @dev Will be reset to 0 in `_activate` at the start of each day.
                   */
                  uint32 private _numActivatedToday;
                  /**
                   * @notice Helper function to get the current day number.
                   */
                  function _currentDay() private view returns (uint32) {
                      return uint32(block.timestamp / (1 days));
                  }
                  /**
                   * @notice Sets the maximum number of activations per day.
                   */
                  function _setMaxNumActivationsPerDay(uint32 maxNumActivationsPerDay) internal {
                      _maxNumActivationsPerDay = maxNumActivationsPerDay;
                  }
                  /**
                   * @notice Activates an array of eggs.
                   * @dev Will revert if the number of eggs to activate exceeds the maximum number of activations per day.
                   */
                  function _activate(uint256[] calldata tokenIds) internal {
                      if (_currentDay() > _lastActivationDay) {
                          _numActivatedToday = 0;
                          _lastActivationDay = _currentDay();
                      }
                      uint256 numLeft = _maxNumActivationsPerDay - _numActivatedToday;
                      if (tokenIds.length > numLeft) {
                          revert ActivatingTooManyEggs(tokenIds.length, numLeft);
                      }
                      for (uint256 i; i < tokenIds.length; ++i) {
                          uint256 tokenId = tokenIds[i];
                          if (activated(tokenId)) {
                              revert EggAlreadyActivated(tokenId);
                          }
                          if (!_exists(tokenId)) {
                              revert NonexistentEgg(tokenId);
                          }
                          _activated.set(tokenId);
                          _refreshMetadata(tokenId);
                          emit EggActivated(tokenId);
                      }
                      _numActivatedToday += uint32(tokenIds.length);
                  }
                  /**
                   * @notice Returns whether an egg has already been activated.
                   */
                  function activated(uint256 tokenId) public view returns (bool) {
                      if (!_exists(tokenId)) {
                          revert NonexistentEgg(tokenId);
                      }
                      return _activated.get(tokenId);
                  }
                  /**
                   * @notice Returns whether a token exists.
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool);
              }
              // 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.9.0) (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.8.3._
               */
              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.9.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 StorageSlotUpgradeable {
                  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
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165Upgradeable {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library MathUpgradeable {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMathUpgradeable {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
              pragma solidity ^0.8.0;
              import "../utils/introspection/IERC165Upgradeable.sol";
              /**
               * @dev Interface for the NFT Royalty Standard.
               *
               * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
               * support for royalty payments across all NFT marketplaces and ecosystem participants.
               *
               * _Available since v4.5._
               */
              interface IERC2981Upgradeable is IERC165Upgradeable {
                  /**
                   * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
                   * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
                   */
                  function royaltyInfo(
                      uint256 tokenId,
                      uint256 salePrice
                  ) external view returns (address receiver, uint256 royaltyAmount);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControlUpgradeable.sol";
              /**
               * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
               */
              interface IAccessControlEnumerableUpgradeable is IAccessControlUpgradeable {
                  /**
                   * @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
                   * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
                   * 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: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControlUpgradeable.sol";
              import "../utils/ContextUpgradeable.sol";
              import "../utils/StringsUpgradeable.sol";
              import "../utils/introspection/ERC165Upgradeable.sol";
              import "../proxy/utils/Initializable.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```solidity
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```solidity
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
               * to enforce additional security measures for this role.
               */
              abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
                  function __AccessControl_init() internal onlyInitializing {
                  }
                  function __AccessControl_init_unchained() internal onlyInitializing {
                  }
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      StringsUpgradeable.toHexString(account),
                                      " is missing role ",
                                      StringsUpgradeable.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @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.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @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 revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
                  /**
                   * @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.9.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.
               *
               * ```solidity
               * 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 EnumerableSetUpgradeable {
                  // 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 {OperatorFilterer} from "./OperatorFilterer.sol";
              import {CANONICAL_CORI_SUBSCRIPTION} from "./lib/Constants.sol";
              /**
               * @title  DefaultOperatorFilterer
               * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
               * @dev    Please note that if your token contract does not provide an owner with EIP-173, it must provide
               *         administration methods on the contract itself to interact with the registry otherwise the subscription
               *         will be locked to the options set during construction.
               */
              abstract contract DefaultOperatorFilterer is OperatorFilterer {
                  /// @dev The constructor that is called when the contract is being deployed.
                  constructor() OperatorFilterer(CANONICAL_CORI_SUBSCRIPTION, true) {}
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity ^0.8.16;
              import {AccessControlEnumerable} from "ethier/utils/AccessControlEnumerable.sol";
              import {IRedeemableToken} from "proof/redemption/interfaces/IRedeemableToken.sol";
              /**
               * @notice Base implementation of a voucher token with approvable redeemer contracts.
               */
              abstract contract BaseRedeemableToken is IRedeemableToken, AccessControlEnumerable {
                  /**
                   * @notice Authorised redeemers.
                   */
                  bytes32 public constant REDEEMER_ROLE = keccak256("REDEEMER_ROLE");
                  /**
                   * @notice A role that cannot be granted or revoked.
                   * @dev Used to lock in members of the `REDEEMER_ROLE` role.
                   */
                  bytes32 private constant _NOOP_ROLE = keccak256("NOOP_ROLE");
                  constructor() {
                      _setRoleAdmin(REDEEMER_ROLE, DEFAULT_STEERING_ROLE);
                      _setRoleAdmin(_NOOP_ROLE, _NOOP_ROLE);
                  }
                  // =========================================================================
                  //                           Redemption
                  // =========================================================================
                  /**
                   * @notice Redeems a voucher token with given tokenId.
                   * @dev Can only be called by approved redeemer contracts.
                   * @dev Reverts if `sender` is not the owner of or approved to transfer the token.
                   */
                  function redeem(address sender, uint256 tokenId) external onlyRole(REDEEMER_ROLE) {
                      if (!_isSenderAllowedToSpend(sender, tokenId)) {
                          revert IRedeemableToken.RedeemerCallerNotAllowedToSpendVoucher(sender, tokenId);
                      }
                      _doRedeem(sender, tokenId);
                  }
                  // =========================================================================
                  //                           Internal hooks
                  // =========================================================================
                  /**
                   * @notice Hook called by `redeem` to check if the sender is allowed to
                   * spend a given token (e.g. if it is the owner or transfer approved).
                   */
                  function _isSenderAllowedToSpend(address sender, uint256 tokenId) internal view virtual returns (bool result);
                  /**
                   * @notice Hook called by `redeem` to preform the redemption of a voucher
                   * token (e.g. burn).
                   */
                  function _doRedeem(address sender, uint256 tokenId) internal virtual;
                  // =========================================================================
                  //                           Steering
                  // =========================================================================
                  /**
                   * @notice Locks the `REDEEMER_ROLE` role.
                   */
                  function lockRedeemers() external onlyRole(DEFAULT_STEERING_ROLE) {
                      _lockRedeemers();
                  }
                  /**
                   * @notice Locks the `REDEEMER_ROLE` role.
                   */
                  function _lockRedeemers() internal {
                      _setRoleAdmin(REDEEMER_ROLE, _NOOP_ROLE);
                  }
                  /**
                   * @notice Revokes approval for all redeemers.
                   */
                  function _revokeAllRedeemers() internal {
                      uint256 num = getRoleMemberCount(REDEEMER_ROLE);
                      for (uint256 i = 0; i < num; i++) {
                          // Akin to a popFront
                          address redeemer = getRoleMember(REDEEMER_ROLE, 0);
                          _revokeRole(REDEEMER_ROLE, redeemer);
                      }
                  }
                  /**
                   * @notice Overrides supportsInterface as required by inheritance.
                   */
                  function supportsInterface(bytes4 interfaceId)
                      public
                      view
                      virtual
                      override(AccessControlEnumerable)
                      returns (bool)
                  {
                      return interfaceId == type(IRedeemableToken).interfaceId || super.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // Copyright 2023 PROOF Holdings Inc
              pragma solidity >=0.8.0 <0.9.0;
              import {AccessControlEnumerable} from "ethier/utils/AccessControlEnumerable.sol";
              import {ReentrancyGuard} from "openzeppelin-contracts/security/ReentrancyGuard.sol";
              import {ISellable} from "../interfaces/ISellable.sol";
              /**
               * @notice A base contract for selling content via authorised sellers.
               */
              abstract contract BaseSellable is ISellable, AccessControlEnumerable, ReentrancyGuard {
                  /**
                   * @notice Authorised sellers.
                   */
                  bytes32 public constant AUTHORISED_SELLER_ROLE = keccak256("AUTHORISED_SELLER_ROLE");
                  /**
                   * @notice A role that cannot be granted or revoked.
                   * @dev Used to lock in members of the `AUTHORISED_SELLER_ROLE` role.
                   */
                  bytes32 private constant _NOOP_ROLE = keccak256("NOOP_ROLE");
                  constructor() {
                      _setRoleAdmin(AUTHORISED_SELLER_ROLE, DEFAULT_STEERING_ROLE);
                      _setRoleAdmin(_NOOP_ROLE, _NOOP_ROLE);
                  }
                  /**
                   * @notice Handles the sale of sellable content via an authorised seller.
                   * @dev Delegates the implementation to `_handleSale`.
                   */
                  function handleSale(address to, uint64 num, bytes calldata data)
                      external
                      payable
                      onlyRole(AUTHORISED_SELLER_ROLE)
                      nonReentrant
                  {
                      _handleSale(to, num, data);
                  }
                  /**
                   * @notice Handles the sale of sellable content.
                   */
                  function _handleSale(address to, uint64 num, bytes calldata data) internal virtual;
                  /**
                   * @notice Locks the `AUTHORISED_SELLER_ROLE` role.
                   */
                  function lockSellers() external onlyRole(DEFAULT_STEERING_ROLE) {
                      _lockSellers();
                  }
                  /**
                   * @notice Locks the `AUTHORISED_SELLER_ROLE` role.
                   */
                  function _lockSellers() internal {
                      _setRoleAdmin(AUTHORISED_SELLER_ROLE, _NOOP_ROLE);
                  }
                  /**
                   * @notice Revokes approval for all sellers.
                   */
                  function _revokeAllSellers() internal {
                      uint256 num = getRoleMemberCount(AUTHORISED_SELLER_ROLE);
                      for (uint256 i = 0; i < num; i++) {
                          // Akin to a popFront
                          address seller = getRoleMember(AUTHORISED_SELLER_ROLE, 0);
                          _revokeRole(AUTHORISED_SELLER_ROLE, seller);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/BitMaps.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
               * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
               */
              library BitMaps {
                  struct BitMap {
                      mapping(uint256 => uint256) _data;
                  }
                  /**
                   * @dev Returns whether the bit at `index` is set.
                   */
                  function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
                      uint256 bucket = index >> 8;
                      uint256 mask = 1 << (index & 0xff);
                      return bitmap._data[bucket] & mask != 0;
                  }
                  /**
                   * @dev Sets the bit at `index` to the boolean `value`.
                   */
                  function setTo(
                      BitMap storage bitmap,
                      uint256 index,
                      bool value
                  ) internal {
                      if (value) {
                          set(bitmap, index);
                      } else {
                          unset(bitmap, index);
                      }
                  }
                  /**
                   * @dev Sets the bit at `index`.
                   */
                  function set(BitMap storage bitmap, uint256 index) internal {
                      uint256 bucket = index >> 8;
                      uint256 mask = 1 << (index & 0xff);
                      bitmap._data[bucket] |= mask;
                  }
                  /**
                   * @dev Unsets the bit at `index`.
                   */
                  function unset(BitMap storage bitmap, uint256 index) internal {
                      uint256 bucket = index >> 8;
                      uint256 mask = 1 << (index & 0xff);
                      bitmap._data[bucket] &= ~mask;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControlUpgradeable {
                  /**
                   * @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: MIT
              pragma solidity ^0.8.13;
              import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
              import {CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS} from "./lib/Constants.sol";
              /**
               * @title  OperatorFilterer
               * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
               *         registrant's entries in the OperatorFilterRegistry.
               * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
               *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
               *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
               *         Please note that if your token contract does not provide an owner with EIP-173, it must provide
               *         administration methods on the contract itself to interact with the registry otherwise the subscription
               *         will be locked to the options set during construction.
               */
              abstract contract OperatorFilterer {
                  /// @dev Emitted when an operator is not allowed.
                  error OperatorNotAllowed(address operator);
                  IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
                      IOperatorFilterRegistry(CANONICAL_OPERATOR_FILTER_REGISTRY_ADDRESS);
                  /// @dev The constructor that is called when the contract is being deployed.
                  constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
                      // 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 (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));
                              }
                          }
                      }
                  }
                  /**
                   * @dev A helper function to check if an operator is allowed.
                   */
                  modifier onlyAllowedOperator(address from) virtual {
                      // Allow spending tokens from addresses with balance
                      // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
                      // from an EOA.
                      if (from != msg.sender) {
                          _checkFilterOperator(msg.sender);
                      }
                      _;
                  }
                  /**
                   * @dev A helper function to check if an operator approval is allowed.
                   */
                  modifier onlyAllowedOperatorApproval(address operator) virtual {
                      _checkFilterOperator(operator);
                      _;
                  }
                  /**
                   * @dev A helper function to check if an operator is allowed.
                   */
                  function _checkFilterOperator(address operator) internal view virtual {
                      // Check registry code length to facilitate testing in environments without a deployed registry.
                      if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
                          // under normal circumstances, this function will revert rather than return false, but inheriting contracts
                          // may specify their own OperatorFilterRegistry implementations, which may behave differently
                          if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                              revert OperatorNotAllowed(operator);
                          }
                      }
                  }
              }
              

              File 8 of 8: GnosisSafe
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "./base/ModuleManager.sol";
              import "./base/OwnerManager.sol";
              import "./base/FallbackManager.sol";
              import "./base/GuardManager.sol";
              import "./common/EtherPaymentFallback.sol";
              import "./common/Singleton.sol";
              import "./common/SignatureDecoder.sol";
              import "./common/SecuredTokenTransfer.sol";
              import "./common/StorageAccessible.sol";
              import "./interfaces/ISignatureValidator.sol";
              import "./external/GnosisSafeMath.sol";
              /// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract GnosisSafe is
                  EtherPaymentFallback,
                  Singleton,
                  ModuleManager,
                  OwnerManager,
                  SignatureDecoder,
                  SecuredTokenTransfer,
                  ISignatureValidatorConstants,
                  FallbackManager,
                  StorageAccessible,
                  GuardManager
              {
                  using GnosisSafeMath for uint256;
                  string public constant VERSION = "1.3.0";
                  // keccak256(
                  //     "EIP712Domain(uint256 chainId,address verifyingContract)"
                  // );
                  bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
                  // keccak256(
                  //     "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
                  // );
                  bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
                  event SafeSetup(address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler);
                  event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
                  event SignMsg(bytes32 indexed msgHash);
                  event ExecutionFailure(bytes32 txHash, uint256 payment);
                  event ExecutionSuccess(bytes32 txHash, uint256 payment);
                  uint256 public nonce;
                  bytes32 private _deprecatedDomainSeparator;
                  // Mapping to keep track of all message hashes that have been approve by ALL REQUIRED owners
                  mapping(bytes32 => uint256) public signedMessages;
                  // Mapping to keep track of all hashes (message or transaction) that have been approve by ANY owners
                  mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
                  // This constructor ensures that this contract can only be used as a master copy for Proxy contracts
                  constructor() {
                      // By setting the threshold it is not possible to call setup anymore,
                      // so we create a Safe with 0 owners and threshold 1.
                      // This is an unusable Safe, perfect for the singleton
                      threshold = 1;
                  }
                  /// @dev Setup function sets initial storage of contract.
                  /// @param _owners List of Safe owners.
                  /// @param _threshold Number of required confirmations for a Safe transaction.
                  /// @param to Contract address for optional delegate call.
                  /// @param data Data payload for optional delegate call.
                  /// @param fallbackHandler Handler for fallback calls to this contract
                  /// @param paymentToken Token that should be used for the payment (0 is ETH)
                  /// @param payment Value that should be paid
                  /// @param paymentReceiver Adddress that should receive the payment (or 0 if tx.origin)
                  function setup(
                      address[] calldata _owners,
                      uint256 _threshold,
                      address to,
                      bytes calldata data,
                      address fallbackHandler,
                      address paymentToken,
                      uint256 payment,
                      address payable paymentReceiver
                  ) external {
                      // setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
                      setupOwners(_owners, _threshold);
                      if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
                      // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
                      setupModules(to, data);
                      if (payment > 0) {
                          // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
                          // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
                          handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
                      }
                      emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
                  }
                  /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
                  ///      Note: The fees are always transferred, even if the user transaction fails.
                  /// @param to Destination address of Safe transaction.
                  /// @param value Ether value of Safe transaction.
                  /// @param data Data payload of Safe transaction.
                  /// @param operation Operation type of Safe transaction.
                  /// @param safeTxGas Gas that should be used for the Safe transaction.
                  /// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                  /// @param gasPrice Gas price that should be used for the payment calculation.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
                  function execTransaction(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver,
                      bytes memory signatures
                  ) public payable virtual returns (bool success) {
                      bytes32 txHash;
                      // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                      {
                          bytes memory txHashData =
                              encodeTransactionData(
                                  // Transaction info
                                  to,
                                  value,
                                  data,
                                  operation,
                                  safeTxGas,
                                  // Payment info
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  // Signature info
                                  nonce
                              );
                          // Increase nonce and execute transaction.
                          nonce++;
                          txHash = keccak256(txHashData);
                          checkSignatures(txHash, txHashData, signatures);
                      }
                      address guard = getGuard();
                      {
                          if (guard != address(0)) {
                              Guard(guard).checkTransaction(
                                  // Transaction info
                                  to,
                                  value,
                                  data,
                                  operation,
                                  safeTxGas,
                                  // Payment info
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  // Signature info
                                  signatures,
                                  msg.sender
                              );
                          }
                      }
                      // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
                      // We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
                      require(gasleft() >= ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500, "GS010");
                      // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                      {
                          uint256 gasUsed = gasleft();
                          // If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
                          // We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
                          success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
                          gasUsed = gasUsed.sub(gasleft());
                          // If no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
                          // This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
                          require(success || safeTxGas != 0 || gasPrice != 0, "GS013");
                          // We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
                          uint256 payment = 0;
                          if (gasPrice > 0) {
                              payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
                          }
                          if (success) emit ExecutionSuccess(txHash, payment);
                          else emit ExecutionFailure(txHash, payment);
                      }
                      {
                          if (guard != address(0)) {
                              Guard(guard).checkAfterExecution(txHash, success);
                          }
                      }
                  }
                  function handlePayment(
                      uint256 gasUsed,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver
                  ) private returns (uint256 payment) {
                      // solhint-disable-next-line avoid-tx-origin
                      address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
                      if (gasToken == address(0)) {
                          // For ETH we will only adjust the gas price to not be higher than the actual used gas price
                          payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
                          require(receiver.send(payment), "GS011");
                      } else {
                          payment = gasUsed.add(baseGas).mul(gasPrice);
                          require(transferToken(gasToken, receiver, payment), "GS012");
                      }
                  }
                  /**
                   * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                   * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                   * @param data That should be signed (this is passed to an external validator contract)
                   * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                   */
                  function checkSignatures(
                      bytes32 dataHash,
                      bytes memory data,
                      bytes memory signatures
                  ) public view {
                      // Load threshold to avoid multiple storage loads
                      uint256 _threshold = threshold;
                      // Check that a threshold is set
                      require(_threshold > 0, "GS001");
                      checkNSignatures(dataHash, data, signatures, _threshold);
                  }
                  /**
                   * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                   * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                   * @param data That should be signed (this is passed to an external validator contract)
                   * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                   * @param requiredSignatures Amount of required valid signatures.
                   */
                  function checkNSignatures(
                      bytes32 dataHash,
                      bytes memory data,
                      bytes memory signatures,
                      uint256 requiredSignatures
                  ) public view {
                      // Check that the provided signature data is not too short
                      require(signatures.length >= requiredSignatures.mul(65), "GS020");
                      // There cannot be an owner with address 0.
                      address lastOwner = address(0);
                      address currentOwner;
                      uint8 v;
                      bytes32 r;
                      bytes32 s;
                      uint256 i;
                      for (i = 0; i < requiredSignatures; i++) {
                          (v, r, s) = signatureSplit(signatures, i);
                          if (v == 0) {
                              // If v is 0 then it is a contract signature
                              // When handling contract signatures the address of the contract is encoded into r
                              currentOwner = address(uint160(uint256(r)));
                              // Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
                              // This check is not completely accurate, since it is possible that more signatures than the threshold are send.
                              // Here we only check that the pointer is not pointing inside the part that is being processed
                              require(uint256(s) >= requiredSignatures.mul(65), "GS021");
                              // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
                              require(uint256(s).add(32) <= signatures.length, "GS022");
                              // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
                              uint256 contractSignatureLen;
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  contractSignatureLen := mload(add(add(signatures, s), 0x20))
                              }
                              require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "GS023");
                              // Check signature
                              bytes memory contractSignature;
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  // The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
                                  contractSignature := add(add(signatures, s), 0x20)
                              }
                              require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "GS024");
                          } else if (v == 1) {
                              // If v is 1 then it is an approved hash
                              // When handling approved hashes the address of the approver is encoded into r
                              currentOwner = address(uint160(uint256(r)));
                              // Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
                              require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "GS025");
                          } else if (v > 30) {
                              // If v > 30 then default va (27,28) has been adjusted for eth_sign flow
                              // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
                              currentOwner = ecrecover(keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", dataHash)), v - 4, r, s);
                          } else {
                              // Default is the ecrecover flow with the provided data hash
                              // Use ecrecover with the messageHash for EOA signatures
                              currentOwner = ecrecover(dataHash, v, r, s);
                          }
                          require(currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "GS026");
                          lastOwner = currentOwner;
                      }
                  }
                  /// @dev Allows to estimate a Safe transaction.
                  ///      This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
                  ///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
                  /// @param to Destination address of Safe transaction.
                  /// @param value Ether value of Safe transaction.
                  /// @param data Data payload of Safe transaction.
                  /// @param operation Operation type of Safe transaction.
                  /// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
                  /// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.
                  function requiredTxGas(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation
                  ) external returns (uint256) {
                      uint256 startGas = gasleft();
                      // We don't provide an error message here, as we use it to return the estimate
                      require(execute(to, value, data, operation, gasleft()));
                      uint256 requiredGas = startGas - gasleft();
                      // Convert response to string and return via error message
                      revert(string(abi.encodePacked(requiredGas)));
                  }
                  /**
                   * @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
                   * @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
                   */
                  function approveHash(bytes32 hashToApprove) external {
                      require(owners[msg.sender] != address(0), "GS030");
                      approvedHashes[msg.sender][hashToApprove] = 1;
                      emit ApproveHash(hashToApprove, msg.sender);
                  }
                  /// @dev Returns the chain id used by this contract.
                  function getChainId() public view returns (uint256) {
                      uint256 id;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          id := chainid()
                      }
                      return id;
                  }
                  function domainSeparator() public view returns (bytes32) {
                      return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
                  }
                  /// @dev Returns the bytes that are hashed to be signed by owners.
                  /// @param to Destination address.
                  /// @param value Ether value.
                  /// @param data Data payload.
                  /// @param operation Operation type.
                  /// @param safeTxGas Gas that should be used for the safe transaction.
                  /// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                  /// @param gasPrice Maximum gas price that should be used for this transaction.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param _nonce Transaction nonce.
                  /// @return Transaction hash bytes.
                  function encodeTransactionData(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address refundReceiver,
                      uint256 _nonce
                  ) public view returns (bytes memory) {
                      bytes32 safeTxHash =
                          keccak256(
                              abi.encode(
                                  SAFE_TX_TYPEHASH,
                                  to,
                                  value,
                                  keccak256(data),
                                  operation,
                                  safeTxGas,
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  _nonce
                              )
                          );
                      return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
                  }
                  /// @dev Returns hash to be signed by owners.
                  /// @param to Destination address.
                  /// @param value Ether value.
                  /// @param data Data payload.
                  /// @param operation Operation type.
                  /// @param safeTxGas Fas that should be used for the safe transaction.
                  /// @param baseGas Gas costs for data used to trigger the safe transaction.
                  /// @param gasPrice Maximum gas price that should be used for this transaction.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param _nonce Transaction nonce.
                  /// @return Transaction hash.
                  function getTransactionHash(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address refundReceiver,
                      uint256 _nonce
                  ) public view returns (bytes32) {
                      return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              /// @title Executor - A contract that can execute transactions
              /// @author Richard Meissner - <[email protected]>
              contract Executor {
                  function execute(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation,
                      uint256 txGas
                  ) internal returns (bool success) {
                      if (operation == Enum.Operation.DelegateCall) {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                          }
                      } else {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/SelfAuthorized.sol";
              /// @title Fallback Manager - A contract that manages fallback calls made to this contract
              /// @author Richard Meissner - <[email protected]>
              contract FallbackManager is SelfAuthorized {
                  event ChangedFallbackHandler(address handler);
                  // keccak256("fallback_manager.handler.address")
                  bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
                  function internalSetFallbackHandler(address handler) internal {
                      bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, handler)
                      }
                  }
                  /// @dev Allows to add a contract to handle fallback calls.
                  ///      Only fallback calls without value and with data will be forwarded.
                  ///      This can only be done via a Safe transaction.
                  /// @param handler contract to handle fallbacks calls.
                  function setFallbackHandler(address handler) public authorized {
                      internalSetFallbackHandler(handler);
                      emit ChangedFallbackHandler(handler);
                  }
                  // solhint-disable-next-line payable-fallback,no-complex-fallback
                  fallback() external {
                      bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let handler := sload(slot)
                          if iszero(handler) {
                              return(0, 0)
                          }
                          calldatacopy(0, 0, calldatasize())
                          // The msg.sender address is shifted to the left by 12 bytes to remove the padding
                          // Then the address without padding is stored right after the calldata
                          mstore(calldatasize(), shl(96, caller()))
                          // Add 20 bytes for the address appended add the end
                          let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if iszero(success) {
                              revert(0, returndatasize())
                          }
                          return(0, returndatasize())
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              import "../common/SelfAuthorized.sol";
              interface Guard {
                  function checkTransaction(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver,
                      bytes memory signatures,
                      address msgSender
                  ) external;
                  function checkAfterExecution(bytes32 txHash, bool success) external;
              }
              /// @title Fallback Manager - A contract that manages fallback calls made to this contract
              /// @author Richard Meissner - <[email protected]>
              contract GuardManager is SelfAuthorized {
                  event ChangedGuard(address guard);
                  // keccak256("guard_manager.guard.address")
                  bytes32 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
                  /// @dev Set a guard that checks transactions before execution
                  /// @param guard The address of the guard to be used or the 0 address to disable the guard
                  function setGuard(address guard) external authorized {
                      bytes32 slot = GUARD_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, guard)
                      }
                      emit ChangedGuard(guard);
                  }
                  function getGuard() internal view returns (address guard) {
                      bytes32 slot = GUARD_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          guard := sload(slot)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              import "../common/SelfAuthorized.sol";
              import "./Executor.sol";
              /// @title Module Manager - A contract that manages modules that can execute transactions via this contract
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract ModuleManager is SelfAuthorized, Executor {
                  event EnabledModule(address module);
                  event DisabledModule(address module);
                  event ExecutionFromModuleSuccess(address indexed module);
                  event ExecutionFromModuleFailure(address indexed module);
                  address internal constant SENTINEL_MODULES = address(0x1);
                  mapping(address => address) internal modules;
                  function setupModules(address to, bytes memory data) internal {
                      require(modules[SENTINEL_MODULES] == address(0), "GS100");
                      modules[SENTINEL_MODULES] = SENTINEL_MODULES;
                      if (to != address(0))
                          // Setup has to complete successfully or transaction fails.
                          require(execute(to, 0, data, Enum.Operation.DelegateCall, gasleft()), "GS000");
                  }
                  /// @dev Allows to add a module to the whitelist.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Enables the module `module` for the Safe.
                  /// @param module Module to be whitelisted.
                  function enableModule(address module) public authorized {
                      // Module address cannot be null or sentinel.
                      require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                      // Module cannot be added twice.
                      require(modules[module] == address(0), "GS102");
                      modules[module] = modules[SENTINEL_MODULES];
                      modules[SENTINEL_MODULES] = module;
                      emit EnabledModule(module);
                  }
                  /// @dev Allows to remove a module from the whitelist.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Disables the module `module` for the Safe.
                  /// @param prevModule Module that pointed to the module to be removed in the linked list
                  /// @param module Module to be removed.
                  function disableModule(address prevModule, address module) public authorized {
                      // Validate module address and check that it corresponds to module index.
                      require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                      require(modules[prevModule] == module, "GS103");
                      modules[prevModule] = modules[module];
                      modules[module] = address(0);
                      emit DisabledModule(module);
                  }
                  /// @dev Allows a Module to execute a Safe transaction without any further confirmations.
                  /// @param to Destination address of module transaction.
                  /// @param value Ether value of module transaction.
                  /// @param data Data payload of module transaction.
                  /// @param operation Operation type of module transaction.
                  function execTransactionFromModule(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation
                  ) public virtual returns (bool success) {
                      // Only whitelisted modules are allowed.
                      require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
                      // Execute transaction without further confirmations.
                      success = execute(to, value, data, operation, gasleft());
                      if (success) emit ExecutionFromModuleSuccess(msg.sender);
                      else emit ExecutionFromModuleFailure(msg.sender);
                  }
                  /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
                  /// @param to Destination address of module transaction.
                  /// @param value Ether value of module transaction.
                  /// @param data Data payload of module transaction.
                  /// @param operation Operation type of module transaction.
                  function execTransactionFromModuleReturnData(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation
                  ) public returns (bool success, bytes memory returnData) {
                      success = execTransactionFromModule(to, value, data, operation);
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Load free memory location
                          let ptr := mload(0x40)
                          // We allocate memory for the return data by setting the free memory location to
                          // current free memory location + data size + 32 bytes for data size value
                          mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
                          // Store the size
                          mstore(ptr, returndatasize())
                          // Store the data
                          returndatacopy(add(ptr, 0x20), 0, returndatasize())
                          // Point the return data to the correct memory location
                          returnData := ptr
                      }
                  }
                  /// @dev Returns if an module is enabled
                  /// @return True if the module is enabled
                  function isModuleEnabled(address module) public view returns (bool) {
                      return SENTINEL_MODULES != module && modules[module] != address(0);
                  }
                  /// @dev Returns array of modules.
                  /// @param start Start of the page.
                  /// @param pageSize Maximum number of modules that should be returned.
                  /// @return array Array of modules.
                  /// @return next Start of the next page.
                  function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {
                      // Init array with max page size
                      array = new address[](pageSize);
                      // Populate return array
                      uint256 moduleCount = 0;
                      address currentModule = modules[start];
                      while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
                          array[moduleCount] = currentModule;
                          currentModule = modules[currentModule];
                          moduleCount++;
                      }
                      next = currentModule;
                      // Set correct size of returned array
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          mstore(array, moduleCount)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/SelfAuthorized.sol";
              /// @title OwnerManager - Manages a set of owners and a threshold to perform actions.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract OwnerManager is SelfAuthorized {
                  event AddedOwner(address owner);
                  event RemovedOwner(address owner);
                  event ChangedThreshold(uint256 threshold);
                  address internal constant SENTINEL_OWNERS = address(0x1);
                  mapping(address => address) internal owners;
                  uint256 internal ownerCount;
                  uint256 internal threshold;
                  /// @dev Setup function sets initial storage of contract.
                  /// @param _owners List of Safe owners.
                  /// @param _threshold Number of required confirmations for a Safe transaction.
                  function setupOwners(address[] memory _owners, uint256 _threshold) internal {
                      // Threshold can only be 0 at initialization.
                      // Check ensures that setup function can only be called once.
                      require(threshold == 0, "GS200");
                      // Validate that threshold is smaller than number of added owners.
                      require(_threshold <= _owners.length, "GS201");
                      // There has to be at least one Safe owner.
                      require(_threshold >= 1, "GS202");
                      // Initializing Safe owners.
                      address currentOwner = SENTINEL_OWNERS;
                      for (uint256 i = 0; i < _owners.length; i++) {
                          // Owner address cannot be null.
                          address owner = _owners[i];
                          require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "GS203");
                          // No duplicate owners allowed.
                          require(owners[owner] == address(0), "GS204");
                          owners[currentOwner] = owner;
                          currentOwner = owner;
                      }
                      owners[currentOwner] = SENTINEL_OWNERS;
                      ownerCount = _owners.length;
                      threshold = _threshold;
                  }
                  /// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`.
                  /// @param owner New owner address.
                  /// @param _threshold New threshold.
                  function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized {
                      // Owner address cannot be null, the sentinel or the Safe itself.
                      require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this), "GS203");
                      // No duplicate owners allowed.
                      require(owners[owner] == address(0), "GS204");
                      owners[owner] = owners[SENTINEL_OWNERS];
                      owners[SENTINEL_OWNERS] = owner;
                      ownerCount++;
                      emit AddedOwner(owner);
                      // Change threshold if threshold was changed.
                      if (threshold != _threshold) changeThreshold(_threshold);
                  }
                  /// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
                  /// @param prevOwner Owner that pointed to the owner to be removed in the linked list
                  /// @param owner Owner address to be removed.
                  /// @param _threshold New threshold.
                  function removeOwner(
                      address prevOwner,
                      address owner,
                      uint256 _threshold
                  ) public authorized {
                      // Only allow to remove an owner, if threshold can still be reached.
                      require(ownerCount - 1 >= _threshold, "GS201");
                      // Validate owner address and check that it corresponds to owner index.
                      require(owner != address(0) && owner != SENTINEL_OWNERS, "GS203");
                      require(owners[prevOwner] == owner, "GS205");
                      owners[prevOwner] = owners[owner];
                      owners[owner] = address(0);
                      ownerCount--;
                      emit RemovedOwner(owner);
                      // Change threshold if threshold was changed.
                      if (threshold != _threshold) changeThreshold(_threshold);
                  }
                  /// @dev Allows to swap/replace an owner from the Safe with another address.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Replaces the owner `oldOwner` in the Safe with `newOwner`.
                  /// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
                  /// @param oldOwner Owner address to be replaced.
                  /// @param newOwner New owner address.
                  function swapOwner(
                      address prevOwner,
                      address oldOwner,
                      address newOwner
                  ) public authorized {
                      // Owner address cannot be null, the sentinel or the Safe itself.
                      require(newOwner != address(0) && newOwner != SENTINEL_OWNERS && newOwner != address(this), "GS203");
                      // No duplicate owners allowed.
                      require(owners[newOwner] == address(0), "GS204");
                      // Validate oldOwner address and check that it corresponds to owner index.
                      require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "GS203");
                      require(owners[prevOwner] == oldOwner, "GS205");
                      owners[newOwner] = owners[oldOwner];
                      owners[prevOwner] = newOwner;
                      owners[oldOwner] = address(0);
                      emit RemovedOwner(oldOwner);
                      emit AddedOwner(newOwner);
                  }
                  /// @dev Allows to update the number of required confirmations by Safe owners.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Changes the threshold of the Safe to `_threshold`.
                  /// @param _threshold New threshold.
                  function changeThreshold(uint256 _threshold) public authorized {
                      // Validate that threshold is smaller than number of owners.
                      require(_threshold <= ownerCount, "GS201");
                      // There has to be at least one Safe owner.
                      require(_threshold >= 1, "GS202");
                      threshold = _threshold;
                      emit ChangedThreshold(threshold);
                  }
                  function getThreshold() public view returns (uint256) {
                      return threshold;
                  }
                  function isOwner(address owner) public view returns (bool) {
                      return owner != SENTINEL_OWNERS && owners[owner] != address(0);
                  }
                  /// @dev Returns array of owners.
                  /// @return Array of Safe owners.
                  function getOwners() public view returns (address[] memory) {
                      address[] memory array = new address[](ownerCount);
                      // populate return array
                      uint256 index = 0;
                      address currentOwner = owners[SENTINEL_OWNERS];
                      while (currentOwner != SENTINEL_OWNERS) {
                          array[index] = currentOwner;
                          currentOwner = owners[currentOwner];
                          index++;
                      }
                      return array;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title Enum - Collection of enums
              /// @author Richard Meissner - <[email protected]>
              contract Enum {
                  enum Operation {Call, DelegateCall}
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments
              /// @author Richard Meissner - <[email protected]>
              contract EtherPaymentFallback {
                  event SafeReceived(address indexed sender, uint256 value);
                  /// @dev Fallback function accepts Ether transactions.
                  receive() external payable {
                      emit SafeReceived(msg.sender, msg.value);
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SecuredTokenTransfer - Secure token transfer
              /// @author Richard Meissner - <[email protected]>
              contract SecuredTokenTransfer {
                  /// @dev Transfers a token and returns if it was a success
                  /// @param token Token that should be transferred
                  /// @param receiver Receiver to whom the token should be transferred
                  /// @param amount The amount of tokens that should be transferred
                  function transferToken(
                      address token,
                      address receiver,
                      uint256 amount
                  ) internal returns (bool transferred) {
                      // 0xa9059cbb - keccack("transfer(address,uint256)")
                      bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // We write the return value to scratch space.
                          // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
                          let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                          switch returndatasize()
                              case 0 {
                                  transferred := success
                              }
                              case 0x20 {
                                  transferred := iszero(or(iszero(success), iszero(mload(0))))
                              }
                              default {
                                  transferred := 0
                              }
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SelfAuthorized - authorizes current contract to perform actions
              /// @author Richard Meissner - <[email protected]>
              contract SelfAuthorized {
                  function requireSelfCall() private view {
                      require(msg.sender == address(this), "GS031");
                  }
                  modifier authorized() {
                      // This is a function call as it minimized the bytecode size
                      requireSelfCall();
                      _;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SignatureDecoder - Decodes signatures that a encoded as bytes
              /// @author Richard Meissner - <[email protected]>
              contract SignatureDecoder {
                  /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
                  /// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures
                  /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
                  /// @param signatures concatenated rsv signatures
                  function signatureSplit(bytes memory signatures, uint256 pos)
                      internal
                      pure
                      returns (
                          uint8 v,
                          bytes32 r,
                          bytes32 s
                      )
                  {
                      // The signature format is a compact form of:
                      //   {bytes32 r}{bytes32 s}{uint8 v}
                      // Compact means, uint8 is not padded to 32 bytes.
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let signaturePos := mul(0x41, pos)
                          r := mload(add(signatures, add(signaturePos, 0x20)))
                          s := mload(add(signatures, add(signaturePos, 0x40)))
                          // Here we are loading the last 32 bytes, including 31 bytes
                          // of 's'. There is no 'mload8' to do this.
                          //
                          // 'byte' is not working due to the Solidity parser, so lets
                          // use the second best option, 'and'
                          v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title Singleton - Base for singleton contracts (should always be first super contract)
              ///         This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`)
              /// @author Richard Meissner - <[email protected]>
              contract Singleton {
                  // singleton always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.
                  // It should also always be ensured that the address is stored alone (uses a full word)
                  address private singleton;
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
              /// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
              contract StorageAccessible {
                  /**
                   * @dev Reads `length` bytes of storage in the currents contract
                   * @param offset - the offset in the current contract's storage in words to start reading from
                   * @param length - the number of words (32 bytes) of data to read
                   * @return the bytes that were read.
                   */
                  function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) {
                      bytes memory result = new bytes(length * 32);
                      for (uint256 index = 0; index < length; index++) {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let word := sload(add(offset, index))
                              mstore(add(add(result, 0x20), mul(index, 0x20)), word)
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Performs a delegetecall on a targetContract in the context of self.
                   * Internally reverts execution to avoid side effects (making it static).
                   *
                   * This method reverts with data equal to `abi.encode(bool(success), bytes(response))`.
                   * Specifically, the `returndata` after a call to this method will be:
                   * `success:bool || response.length:uint256 || response:bytes`.
                   *
                   * @param targetContract Address of the contract containing the code to execute.
                   * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
                   */
                  function simulateAndRevert(address targetContract, bytes memory calldataPayload) external {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
                          mstore(0x00, success)
                          mstore(0x20, returndatasize())
                          returndatacopy(0x40, 0, returndatasize())
                          revert(0, add(returndatasize(), 0x40))
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /**
               * @title GnosisSafeMath
               * @dev Math operations with safety checks that revert on error
               * Renamed from SafeMath to GnosisSafeMath to avoid conflicts
               * TODO: remove once open zeppelin update to solc 0.5.0
               */
              library GnosisSafeMath {
                  /**
                   * @dev Multiplies two numbers, reverts on overflow.
                   */
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                      if (a == 0) {
                          return 0;
                      }
                      uint256 c = a * b;
                      require(c / a == b);
                      return c;
                  }
                  /**
                   * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                   */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a);
                      uint256 c = a - b;
                      return c;
                  }
                  /**
                   * @dev Adds two numbers, reverts on overflow.
                   */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a);
                      return c;
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a >= b ? a : b;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              contract ISignatureValidatorConstants {
                  // bytes4(keccak256("isValidSignature(bytes,bytes)")
                  bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
              }
              abstract contract ISignatureValidator is ISignatureValidatorConstants {
                  /**
                   * @dev Should return whether the signature provided is valid for the provided data
                   * @param _data Arbitrary length data signed on the behalf of address(this)
                   * @param _signature Signature byte array associated with _data
                   *
                   * MUST return the bytes4 magic value 0x20c13b0b when function passes.
                   * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
                   * MUST allow external calls
                   */
                  function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);
              }