ETH Price: $2,638.03 (-0.11%)
Gas: 3.31 Gwei

Transaction Decoder

Block:
20834506 at Sep-26-2024 11:31:35 AM +UTC
Transaction Fee:
0.000734510171154633 ETH $1.94
Gas Used:
50,667 Gas / 14.496815899 Gwei

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
12.281578747966767899 Eth12.281629414966767899 Eth0.000050667
0x7931574A...d4AF0a151
0xC69088eB...49813093e
0.02506009492657523 Eth
Nonce: 558
0.024325584755420597 Eth
Nonce: 559
0.000734510171154633

Execution Trace

ERC1155SeaDropCloneable.safeTransferFrom( from=0xC69088eB5F015Fca5B385b8E3A0463749813093e, to=0xaFf82c8D46F4F6E6771f9f92e2E090e2204C1347, id=1, amount=12, data=0x )
  • ERC1155SeaDropCloneable.safeTransferFrom( from=0xC69088eB5F015Fca5B385b8E3A0463749813093e, to=0xaFf82c8D46F4F6E6771f9f92e2E090e2204C1347, id=1, amount=12, data=0x )
    • CreatorTokenTransferValidator.validateTransfer( caller=0xC69088eB5F015Fca5B385b8E3A0463749813093e, from=0xC69088eB5F015Fca5B385b8E3A0463749813093e, to=0xaFf82c8D46F4F6E6771f9f92e2E090e2204C1347, tokenId=1, 12 )
      File 1 of 3: ERC1155SeaDropCloneable
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ERC1155SeaDropContractOffererCloneable
      } from "./ERC1155SeaDropContractOffererCloneable.sol";
      /**
       * @title  ERC1155SeaDropCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @custom:contributor Limit Break (@limitbreak)
       * @notice A cloneable ERC1155 token contract that can mint as a
       *         Seaport contract offerer.
       *         Implements Limit Break's Creator Token Standards transfer
       *         validation for royalty enforcement.
       */
      contract ERC1155SeaDropCloneable is ERC1155SeaDropContractOffererCloneable {
          /**
           * @notice Initialize the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          implementation code. Also contains SeaDrop
           *                          implementation code.
           * @param allowedSeaport    The address of the Seaport contract allowed to
           *                          interact.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function initialize(
              address allowedConfigurer,
              address allowedSeaport,
              string memory name_,
              string memory symbol_,
              address initialOwner
          ) public initializer {
              // Initialize ownership.
              _initializeOwner(initialOwner);
              // Initialize ERC1155SeaDropContractOffererCloneable.
              __ERC1155SeaDropContractOffererCloneable_init(
                  allowedConfigurer,
                  allowedSeaport,
                  name_,
                  symbol_
              );
          }
          /**
           * @notice Burns a token, restricted to the owner or approved operator,
           *         and must have sufficient balance.
           *
           * @param from   The address to burn from.
           * @param id     The token id to burn.
           * @param amount The amount to burn.
           */
          function burn(address from, uint256 id, uint256 amount) external {
              // Burn the token.
              _burn(msg.sender, from, id, amount);
          }
          /**
           * @notice Burns a batch of tokens, restricted to the owner or
           *         approved operator, and must have sufficient balance.
           *
           * @param from    The address to burn from.
           * @param ids     The token ids to burn.
           * @param amounts The amounts to burn per token id.
           */
          function batchBurn(
              address from,
              uint256[] calldata ids,
              uint256[] calldata amounts
          ) external {
              // Burn the tokens.
              _batchBurn(msg.sender, from, ids, amounts);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { IERC1155SeaDrop } from "../interfaces/IERC1155SeaDrop.sol";
      import { ISeaDropToken } from "../interfaces/ISeaDropToken.sol";
      import {
          ERC1155ContractMetadataCloneable
      } from "./ERC1155ContractMetadataCloneable.sol";
      import {
          ERC1155SeaDropContractOffererStorage
      } from "../lib/ERC1155SeaDropContractOffererStorage.sol";
      import {
          ERC1155SeaDropErrorsAndEvents
      } from "../lib/ERC1155SeaDropErrorsAndEvents.sol";
      import { PublicDrop } from "../lib//ERC1155SeaDropStructs.sol";
      import { AllowListData } from "../lib/SeaDropStructs.sol";
      import {
          ERC1155ConduitPreapproved
      } from "../lib/ERC1155ConduitPreapproved.sol";
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      import { SpentItem } from "seaport-types/src/lib/ConsiderationStructs.sol";
      import {
          ContractOffererInterface
      } from "seaport-types/src/interfaces/ContractOffererInterface.sol";
      import {
          IERC165
      } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
      /**
       * @title  ERC1155SeaDropContractOffererCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @notice A cloneable ERC1155 token contract that can mint as a
       *         Seaport contract offerer.
       */
      contract ERC1155SeaDropContractOffererCloneable is
          ERC1155ContractMetadataCloneable,
          ERC1155SeaDropErrorsAndEvents
      {
          using ERC1155SeaDropContractOffererStorage for ERC1155SeaDropContractOffererStorage.Layout;
          /**
           * @notice Initialize the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          configure parameters. Also contains SeaDrop
           *                          implementation code.
           * @param allowedSeaport    The address of the Seaport contract allowed to
           *                          interact.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function __ERC1155SeaDropContractOffererCloneable_init(
              address allowedConfigurer,
              address allowedSeaport,
              string memory name_,
              string memory symbol_
          ) internal onlyInitializing {
              // Set the allowed Seaport to interact with this contract.
              if (allowedSeaport == address(0)) {
                  revert AllowedSeaportCannotBeZeroAddress();
              }
              ERC1155SeaDropContractOffererStorage.layout()._allowedSeaport[
                  allowedSeaport
              ] = true;
              // Set the allowed Seaport enumeration.
              address[] memory enumeratedAllowedSeaport = new address[](1);
              enumeratedAllowedSeaport[0] = allowedSeaport;
              ERC1155SeaDropContractOffererStorage
                  .layout()
                  ._enumeratedAllowedSeaport = enumeratedAllowedSeaport;
              // Emit an event noting the contract deployment.
              emit SeaDropTokenDeployed(SEADROP_TOKEN_TYPE.ERC1155_CLONE);
              // Initialize ERC1155ContractMetadataCloneable.
              __ERC1155ContractMetadataCloneable_init(
                  allowedConfigurer,
                  name_,
                  symbol_
              );
          }
          /**
           * @notice The fallback function is used as a dispatcher for SeaDrop
           *         methods.
           */
          fallback(bytes calldata) external returns (bytes memory output) {
              // Get the function selector.
              bytes4 selector = msg.sig;
              // Get the rest of the msg data after the selector.
              bytes calldata data = msg.data[4:];
              // Determine if we should forward the call to the implementation
              // contract with SeaDrop logic.
              bool callSeaDropImplementation = selector ==
                  ISeaDropToken.updateAllowedSeaport.selector ||
                  selector == ISeaDropToken.updateDropURI.selector ||
                  selector == ISeaDropToken.updateAllowList.selector ||
                  selector == ISeaDropToken.updateCreatorPayouts.selector ||
                  selector == ISeaDropToken.updatePayer.selector ||
                  selector == ISeaDropToken.updateAllowedFeeRecipient.selector ||
                  selector == ISeaDropToken.updateSigner.selector ||
                  selector == IERC1155SeaDrop.updatePublicDrop.selector ||
                  selector == ContractOffererInterface.previewOrder.selector ||
                  selector == ContractOffererInterface.generateOrder.selector ||
                  selector == ContractOffererInterface.getSeaportMetadata.selector ||
                  selector == IERC1155SeaDrop.getPublicDrop.selector ||
                  selector == IERC1155SeaDrop.getPublicDropIndexes.selector ||
                  selector == ISeaDropToken.getAllowedSeaport.selector ||
                  selector == ISeaDropToken.getCreatorPayouts.selector ||
                  selector == ISeaDropToken.getAllowListMerkleRoot.selector ||
                  selector == ISeaDropToken.getAllowedFeeRecipients.selector ||
                  selector == ISeaDropToken.getSigners.selector ||
                  selector == ISeaDropToken.getDigestIsUsed.selector ||
                  selector == ISeaDropToken.getPayers.selector;
              // Determine if we should require only the owner or configurer calling.
              bool requireOnlyOwnerOrConfigurer = selector ==
                  ISeaDropToken.updateAllowedSeaport.selector ||
                  selector == ISeaDropToken.updateDropURI.selector ||
                  selector == ISeaDropToken.updateAllowList.selector ||
                  selector == ISeaDropToken.updateCreatorPayouts.selector ||
                  selector == ISeaDropToken.updatePayer.selector ||
                  selector == ISeaDropToken.updateAllowedFeeRecipient.selector ||
                  selector == IERC1155SeaDrop.updatePublicDrop.selector;
              if (callSeaDropImplementation) {
                  // For update calls, ensure the sender is only the owner
                  // or configurer contract.
                  if (requireOnlyOwnerOrConfigurer) {
                      _onlyOwnerOrConfigurer();
                  } else if (selector == ISeaDropToken.updateSigner.selector) {
                      // For updateSigner, a signer can disallow themselves.
                      // Get the signer parameter.
                      address signer = address(bytes20(data[12:32]));
                      // If the signer is not allowed, ensure sender is only owner
                      // or configurer.
                      if (
                          msg.sender != signer ||
                          (msg.sender == signer &&
                              !ERC1155SeaDropContractOffererStorage
                                  .layout()
                                  ._allowedSigners[signer])
                      ) {
                          _onlyOwnerOrConfigurer();
                      }
                  }
                  // Forward the call to the implementation contract.
                  (bool success, bytes memory returnedData) = _CONFIGURER
                      .delegatecall(msg.data);
                  // Require that the call was successful.
                  if (!success) {
                      // Bubble up the revert reason.
                      assembly {
                          revert(add(32, returnedData), mload(returnedData))
                      }
                  }
                  // If the call was to generateOrder, mint the tokens.
                  if (selector == ContractOffererInterface.generateOrder.selector) {
                      _mintOrder(data);
                  }
                  // Return the data from the delegate call.
                  return returnedData;
              } else if (selector == IERC1155SeaDrop.getMintStats.selector) {
                  // Get the minter and token id.
                  (address minter, uint256 tokenId) = abi.decode(
                      data,
                      (address, uint256)
                  );
                  // Get the mint stats.
                  (
                      uint256 minterNumMinted,
                      uint256 minterNumMintedForTokenId,
                      uint256 totalMintedForTokenId,
                      uint256 maxSupply
                  ) = _getMintStats(minter, tokenId);
                  // Encode the return data.
                  return
                      abi.encode(
                          minterNumMinted,
                          minterNumMintedForTokenId,
                          totalMintedForTokenId,
                          maxSupply
                      );
              } else if (selector == ContractOffererInterface.ratifyOrder.selector) {
                  // This function is a no-op, nothing additional needs to happen here.
                  // Utilize assembly to efficiently return the ratifyOrder magic value.
                  assembly {
                      mstore(0, 0xf4dd92ce)
                      return(0x1c, 32)
                  }
              } else if (selector == ISeaDropToken.configurer.selector) {
                  // Return the configurer contract.
                  return abi.encode(_CONFIGURER);
              } else if (selector == IERC1155SeaDrop.multiConfigureMint.selector) {
                  // Ensure only the owner or configurer can call this function.
                  _onlyOwnerOrConfigurer();
                  // Mint the tokens.
                  _multiConfigureMint(data);
              } else {
                  // Revert if the function selector is not supported.
                  revert UnsupportedFunctionSelector(selector);
              }
          }
          /**
           * @notice Returns a set of mint stats for the address.
           *         This assists in enforcing maxSupply, maxTotalMintableByWallet,
           *         and maxTokenSupplyForStage checks.
           *
           * @dev    NOTE: Implementing contracts should always update these numbers
           *         before transferring any tokens with _safeMint() to mitigate
           *         consequences of malicious onERC1155Received() hooks.
           *
           * @param minter  The minter address.
           * @param tokenId The token id to return the stats for.
           */
          function _getMintStats(
              address minter,
              uint256 tokenId
          )
              internal
              view
              returns (
                  uint256 minterNumMinted,
                  uint256 minterNumMintedForTokenId,
                  uint256 totalMintedForTokenId,
                  uint256 maxSupply
              )
          {
              // Put the token supply on the stack.
              TokenSupply storage tokenSupply = _tokenSupply[tokenId];
              // Assign the return values.
              totalMintedForTokenId = tokenSupply.totalMinted;
              maxSupply = tokenSupply.maxSupply;
              minterNumMinted = _totalMintedByUser[minter];
              minterNumMintedForTokenId = _totalMintedByUserPerToken[minter][tokenId];
          }
          /**
           * @dev Handle ERC-1155 safeTransferFrom. If "from" is this contract,
           *      the sender can only be Seaport or the conduit.
           *
           * @param from   The address to transfer from.
           * @param to     The address to transfer to.
           * @param id     The token id to transfer.
           * @param amount The amount of tokens to transfer.
           * @param data   The data to pass to the onERC1155Received hook.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual override {
              if (from == address(this)) {
                  // Only Seaport or the conduit can use this function
                  // when "from" is this contract.
                  if (
                      msg.sender != _CONDUIT &&
                      !ERC1155SeaDropContractOffererStorage.layout()._allowedSeaport[
                          msg.sender
                      ]
                  ) {
                      revert InvalidCallerOnlyAllowedSeaport(msg.sender);
                  }
                  return;
              }
              ERC1155._safeTransfer(_by(), from, to, id, amount, data);
          }
          /**
           * @notice Returns whether the interface is supported.
           *
           * @param interfaceId The interface id to check against.
           */
          function supportsInterface(
              bytes4 interfaceId
          )
              public
              view
              virtual
              override(ERC1155ContractMetadataCloneable)
              returns (bool)
          {
              return
                  interfaceId == type(IERC1155SeaDrop).interfaceId ||
                  interfaceId == type(ContractOffererInterface).interfaceId ||
                  interfaceId == 0x2e778efc || // SIP-5 (getSeaportMetadata)
                  // ERC1155ContractMetadata returns supportsInterface true for
                  //     IERC1155ContractMetadata, ERC-4906, ERC-2981
                  // ERC1155A returns supportsInterface true for
                  //     ERC165, ERC1155, ERC1155MetadataURI
                  ERC1155ContractMetadataCloneable.supportsInterface(interfaceId);
          }
          /**
           * @dev Internal function to mint tokens during a generateOrder call
           *      from Seaport.
           *
           * @param data The original transaction calldata, without the selector.
           */
          function _mintOrder(bytes calldata data) internal {
              // Decode fulfiller, minimumReceived, and context from calldata.
              (
                  address fulfiller,
                  SpentItem[] memory minimumReceived,
                  ,
                  bytes memory context
              ) = abi.decode(data, (address, SpentItem[], SpentItem[], bytes));
              // Assign the minter from context[22:42]. We validate context has the
              // correct minimum length in the implementation's `_decodeOrder`.
              address minter;
              assembly {
                  minter := shr(96, mload(add(add(context, 0x20), 22)))
              }
              // If the minter is the zero address, set it to the fulfiller.
              if (minter == address(0)) {
                  minter = fulfiller;
              }
              // Set the token ids and quantities.
              uint256 minimumReceivedLength = minimumReceived.length;
              uint256[] memory tokenIds = new uint256[](minimumReceivedLength);
              uint256[] memory quantities = new uint256[](minimumReceivedLength);
              for (uint256 i = 0; i < minimumReceivedLength; ) {
                  tokenIds[i] = minimumReceived[i].identifier;
                  quantities[i] = minimumReceived[i].amount;
                  unchecked {
                      ++i;
                  }
              }
              // Mint the tokens.
              _batchMint(minter, tokenIds, quantities, "");
          }
          /**
           * @dev Internal function to mint tokens during a multiConfigureMint call
           *      from the configurer contract.
           *
           * @param data The original transaction calldata, without the selector.
           */
          function _multiConfigureMint(bytes calldata data) internal {
              // Decode the calldata.
              (
                  address recipient,
                  uint256[] memory tokenIds,
                  uint256[] memory amounts
              ) = abi.decode(data, (address, uint256[], uint256[]));
              _batchMint(recipient, tokenIds, amounts, "");
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { ISeaDropToken } from "./ISeaDropToken.sol";
      import { PublicDrop } from "../lib/ERC1155SeaDropStructs.sol";
      /**
       * @dev A helper interface to get and set parameters for ERC1155SeaDrop.
       *      The token does not expose these methods as part of its external
       *      interface to optimize contract size, but does implement them.
       */
      interface IERC1155SeaDrop is ISeaDropToken {
          /**
           * @notice Update the SeaDrop public drop parameters at a given index.
           *
           * @param publicDrop The new public drop parameters.
           * @param index      The public drop index.
           */
          function updatePublicDrop(
              PublicDrop calldata publicDrop,
              uint256 index
          ) external;
          /**
           * @notice Returns the public drop stage parameters at a given index.
           *
           * @param index The index of the public drop stage.
           */
          function getPublicDrop(
              uint256 index
          ) external view returns (PublicDrop memory);
          /**
           * @notice Returns the public drop indexes.
           */
          function getPublicDropIndexes() external view returns (uint256[] memory);
          /**
           * @notice Returns a set of mint stats for the address.
           *         This assists SeaDrop in enforcing maxSupply,
           *         maxTotalMintableByWallet, maxTotalMintableByWalletPerToken,
           *         and maxTokenSupplyForStage checks.
           *
           * @dev    NOTE: Implementing contracts should always update these numbers
           *         before transferring any tokens with _safeMint() to mitigate
           *         consequences of malicious onERC1155Received() hooks.
           *
           * @param minter  The minter address.
           * @param tokenId The token id to return stats for.
           */
          function getMintStats(
              address minter,
              uint256 tokenId
          )
              external
              view
              returns (
                  uint256 minterNumMinted,
                  uint256 minterNumMintedForTokenId,
                  uint256 totalMintedForTokenId,
                  uint256 maxSupply
              );
          /**
           * @notice This function is only allowed to be called by the configurer
           *         contract as a way to batch mints and configuration in one tx.
           *
           * @param recipient The address to receive the mints.
           * @param tokenIds  The tokenIds to mint.
           * @param amounts   The amounts to mint.
           */
          function multiConfigureMint(
              address recipient,
              uint256[] calldata tokenIds,
              uint256[] calldata amounts
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ISeaDropTokenContractMetadata
      } from "./ISeaDropTokenContractMetadata.sol";
      import { AllowListData, CreatorPayout } from "../lib/SeaDropStructs.sol";
      /**
       * @dev A helper base interface for IERC721SeaDrop and IERC1155SeaDrop.
       *      The token does not expose these methods as part of its external
       *      interface to optimize contract size, but does implement them.
       */
      interface ISeaDropToken is ISeaDropTokenContractMetadata {
          /**
           * @notice Update the SeaDrop allowed Seaport contracts privileged to mint.
           *         Only the owner can use this function.
           *
           * @param allowedSeaport The allowed Seaport addresses.
           */
          function updateAllowedSeaport(address[] calldata allowedSeaport) external;
          /**
           * @notice Update the SeaDrop allowed fee recipient.
           *         Only the owner can use this function.
           *
           * @param feeRecipient The new fee recipient.
           * @param allowed      Whether the fee recipient is allowed.
           */
          function updateAllowedFeeRecipient(
              address feeRecipient,
              bool allowed
          ) external;
          /**
           * @notice Update the SeaDrop creator payout addresses.
           *         The total basis points must add up to exactly 10_000.
           *         Only the owner can use this function.
           *
           * @param creatorPayouts The new creator payouts.
           */
          function updateCreatorPayouts(
              CreatorPayout[] calldata creatorPayouts
          ) external;
          /**
           * @notice Update the SeaDrop drop URI.
           *         Only the owner can use this function.
           *
           * @param dropURI The new drop URI.
           */
          function updateDropURI(string calldata dropURI) external;
          /**
           * @notice Update the SeaDrop allow list data.
           *         Only the owner can use this function.
           *
           * @param allowListData The new allow list data.
           */
          function updateAllowList(AllowListData calldata allowListData) external;
          /**
           * @notice Update the SeaDrop allowed payers.
           *         Only the owner can use this function.
           *
           * @param payer   The payer to update.
           * @param allowed Whether the payer is allowed.
           */
          function updatePayer(address payer, bool allowed) external;
          /**
           * @notice Update the SeaDrop allowed signer.
           *         Only the owner can use this function.
           *         An allowed signer can also disallow themselves.
           *
           * @param signer  The signer to update.
           * @param allowed Whether the signer is allowed.
           */
          function updateSigner(address signer, bool allowed) external;
          /**
           * @notice Get the SeaDrop allowed Seaport contracts privileged to mint.
           */
          function getAllowedSeaport() external view returns (address[] memory);
          /**
           * @notice Returns the SeaDrop creator payouts.
           */
          function getCreatorPayouts() external view returns (CreatorPayout[] memory);
          /**
           * @notice Returns the SeaDrop allow list merkle root.
           */
          function getAllowListMerkleRoot() external view returns (bytes32);
          /**
           * @notice Returns the SeaDrop allowed fee recipients.
           */
          function getAllowedFeeRecipients() external view returns (address[] memory);
          /**
           * @notice Returns the SeaDrop allowed signers.
           */
          function getSigners() external view returns (address[] memory);
          /**
           * @notice Returns if the signed digest has been used.
           *
           * @param digest The digest hash.
           */
          function getDigestIsUsed(bytes32 digest) external view returns (bool);
          /**
           * @notice Returns the SeaDrop allowed payers.
           */
          function getPayers() external view returns (address[] memory);
          /**
           * @notice Returns the configurer contract.
           */
          function configurer() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          IERC1155ContractMetadata
      } from "../interfaces/IERC1155ContractMetadata.sol";
      import {
          ERC1155ConduitPreapproved
      } from "../lib/ERC1155ConduitPreapproved.sol";
      import {
          ICreatorToken,
          ILegacyCreatorToken
      } from "../interfaces/ICreatorToken.sol";
      import { ITransferValidator1155 } from "../interfaces/ITransferValidator.sol";
      import { TokenTransferValidator } from "../lib/TokenTransferValidator.sol";
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      import { ERC2981 } from "solady/src/tokens/ERC2981.sol";
      import { Ownable } from "solady/src/auth/Ownable.sol";
      import {
          Initializable
      } from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
      /**
       * @title  ERC1155ContractMetadataCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @notice A cloneable token contract that extends ERC-1155
       *         with additional metadata and ownership capabilities.
       */
      contract ERC1155ContractMetadataCloneable is
          ERC1155ConduitPreapproved,
          TokenTransferValidator,
          ERC2981,
          Ownable,
          IERC1155ContractMetadata,
          Initializable
      {
          /// @notice A struct containing the token supply info per token id.
          mapping(uint256 => TokenSupply) _tokenSupply;
          /// @notice The total number of tokens minted by address.
          mapping(address => uint256) _totalMintedByUser;
          /// @notice The total number of tokens minted per token id by address.
          mapping(address => mapping(uint256 => uint256)) _totalMintedByUserPerToken;
          /// @notice The name of the token.
          string internal _name;
          /// @notice The symbol of the token.
          string internal _symbol;
          /// @notice The base URI for token metadata.
          string internal _baseURI;
          /// @notice The contract URI for contract metadata.
          string internal _contractURI;
          /// @notice The provenance hash for guaranteeing metadata order
          ///         for random reveals.
          bytes32 internal _provenanceHash;
          /// @notice The allowed contract that can configure SeaDrop parameters.
          address internal _CONFIGURER;
          /**
           * @dev Reverts if the sender is not the owner or the allowed
           *      configurer contract.
           *
           *      This is used as a function instead of a modifier
           *      to save contract space when used multiple times.
           */
          function _onlyOwnerOrConfigurer() internal view {
              if (msg.sender != _CONFIGURER && msg.sender != owner()) {
                  revert Unauthorized();
              }
          }
          /**
           * @notice Deploy the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          configure parameters. Also contains SeaDrop
           *                          implementation code.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function __ERC1155ContractMetadataCloneable_init(
              address allowedConfigurer,
              string memory name_,
              string memory symbol_
          ) internal onlyInitializing {
              // Set the name of the token.
              _name = name_;
              // Set the symbol of the token.
              _symbol = symbol_;
              // Set the allowed configurer contract to interact with this contract.
              _CONFIGURER = allowedConfigurer;
          }
          /**
           * @notice Sets the base URI for the token metadata and emits an event.
           *
           * @param newBaseURI The new base URI to set.
           */
          function setBaseURI(string calldata newBaseURI) external override {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the new base URI.
              _baseURI = newBaseURI;
              // Emit an event with the update.
              emit BatchMetadataUpdate(0, type(uint256).max);
          }
          /**
           * @notice Sets the contract URI for contract metadata.
           *
           * @param newContractURI The new contract URI.
           */
          function setContractURI(string calldata newContractURI) external override {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the new contract URI.
              _contractURI = newContractURI;
              // Emit an event with the update.
              emit ContractURIUpdated(newContractURI);
          }
          /**
           * @notice Emit an event notifying metadata updates for
           *         a range of token ids, according to EIP-4906.
           *
           * @param fromTokenId The start token id.
           * @param toTokenId   The end token id.
           */
          function emitBatchMetadataUpdate(
              uint256 fromTokenId,
              uint256 toTokenId
          ) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Emit an event with the update.
              if (fromTokenId == toTokenId) {
                  // If only one token is being updated, use the event
                  // in the 1155 spec.
                  emit URI(uri(fromTokenId), fromTokenId);
              } else {
                  emit BatchMetadataUpdate(fromTokenId, toTokenId);
              }
          }
          /**
           * @notice Sets the max token supply and emits an event.
           *
           * @param tokenId      The token id to set the max supply for.
           * @param newMaxSupply The new max supply to set.
           */
          function setMaxSupply(uint256 tokenId, uint256 newMaxSupply) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Ensure the max supply does not exceed the maximum value of uint64,
              // a limit due to the storage of bit-packed variables in TokenSupply,
              if (newMaxSupply > 2 ** 64 - 1) {
                  revert CannotExceedMaxSupplyOfUint64(newMaxSupply);
              }
              // Set the new max supply.
              _tokenSupply[tokenId].maxSupply = uint64(newMaxSupply);
              // Emit an event with the update.
              emit MaxSupplyUpdated(tokenId, newMaxSupply);
          }
          /**
           * @notice Sets the provenance hash and emits an event.
           *
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it has not been
           *         modified after mint started.
           *
           *         This function will revert if the provenance hash has already
           *         been set, so be sure to carefully set it only once.
           *
           * @param newProvenanceHash The new provenance hash to set.
           */
          function setProvenanceHash(bytes32 newProvenanceHash) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Keep track of the old provenance hash for emitting with the event.
              bytes32 oldProvenanceHash = _provenanceHash;
              // Revert if the provenance hash has already been set.
              if (oldProvenanceHash != bytes32(0)) {
                  revert ProvenanceHashCannotBeSetAfterAlreadyBeingSet();
              }
              // Set the new provenance hash.
              _provenanceHash = newProvenanceHash;
              // Emit an event with the update.
              emit ProvenanceHashUpdated(oldProvenanceHash, newProvenanceHash);
          }
          /**
           * @notice Sets the default royalty information.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator of 10_000 basis points.
           */
          function setDefaultRoyalty(address receiver, uint96 feeNumerator) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the default royalty.
              // ERC2981 implementation ensures feeNumerator <= feeDenominator
              // and receiver != address(0).
              _setDefaultRoyalty(receiver, feeNumerator);
              // Emit an event with the updated params.
              emit RoyaltyInfoUpdated(receiver, feeNumerator);
          }
          /**
           * @notice Returns the name of the token.
           */
          function name() external view returns (string memory) {
              return _name;
          }
          /**
           * @notice Returns the symbol of the token.
           */
          function symbol() external view returns (string memory) {
              return _symbol;
          }
          /**
           * @notice Returns the base URI for token metadata.
           */
          function baseURI() external view override returns (string memory) {
              return _baseURI;
          }
          /**
           * @notice Returns the contract URI for contract metadata.
           */
          function contractURI() external view override returns (string memory) {
              return _contractURI;
          }
          /**
           * @notice Returns the max token supply for a token id.
           */
          function maxSupply(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].maxSupply;
          }
          /**
           * @notice Returns the total supply for a token id.
           */
          function totalSupply(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].totalSupply;
          }
          /**
           * @notice Returns the total minted for a token id.
           */
          function totalMinted(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].totalMinted;
          }
          /**
           * @notice Returns the provenance hash.
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it is unmodified
           *         after mint has started.
           */
          function provenanceHash() external view override returns (bytes32) {
              return _provenanceHash;
          }
          /**
           * @notice Returns the URI for token metadata.
           *
           *         This implementation returns the same URI for *all* token types.
           *         It relies on the token type ID substitution mechanism defined
           *         in the EIP to replace {id} with the token id.
           *
           * @custom:param tokenId The token id to get the URI for.
           */
          function uri(
              uint256 /* tokenId */
          ) public view virtual override returns (string memory) {
              // Return the base URI.
              return _baseURI;
          }
          /**
           * @notice Returns the transfer validation function used.
           */
          function getTransferValidationFunction()
              external
              pure
              returns (bytes4 functionSignature, bool isViewFunction)
          {
              functionSignature = ITransferValidator1155.validateTransfer.selector;
              isViewFunction = true;
          }
          /**
           * @notice Set the transfer validator. Only callable by the token owner.
           */
          function setTransferValidator(address newValidator) external onlyOwner {
              // Set the new transfer validator.
              _setTransferValidator(newValidator);
          }
          /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
          function _useBeforeTokenTransfer() internal view virtual override returns (bool) {
              return true;
          }
          /**
           * @dev Hook that is called before any token transfer.
           *      This includes minting and burning.
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory /* data */
          ) internal virtual override {
              if (from != address(0) && to != address(0)) {
                  // Call the transfer validator if one is set.
                  address transferValidator = _transferValidator;
                  if (transferValidator != address(0)) {
                      for (uint256 i = 0; i < ids.length; i++) {
                          ITransferValidator1155(transferValidator).validateTransfer(
                              msg.sender,
                              from,
                              to,
                              ids[i],
                              amounts[i]
                          );
                      }
                  }
              }
          }
          /**
           * @notice Returns whether the interface is supported.
           *
           * @param interfaceId The interface id to check against.
           */
          function supportsInterface(
              bytes4 interfaceId
          ) public view virtual override(ERC1155, ERC2981) returns (bool) {
              return
                  interfaceId == type(IERC1155ContractMetadata).interfaceId ||
                  interfaceId == type(ICreatorToken).interfaceId ||
                  interfaceId == type(ILegacyCreatorToken).interfaceId ||
                  interfaceId == 0x49064906 || // ERC-4906 (MetadataUpdate)
                  ERC2981.supportsInterface(interfaceId) ||
                  // ERC1155 returns supportsInterface true for
                  //     ERC165, ERC1155, ERC1155MetadataURI
                  ERC1155.supportsInterface(interfaceId);
          }
          /**
           * @dev Adds to the internal counters for a mint.
           *
           * @param to     The address to mint to.
           * @param id     The token id to mint.
           * @param amount The quantity to mint.
           * @param data   The data to pass if receiver is a contract.
           */
          function _mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual override {
              // Increment mint counts.
              _incrementMintCounts(to, id, amount);
              ERC1155._mint(to, id, amount, data);
          }
          /**
           * @dev Adds to the internal counters for a batch mint.
           *
           * @param to      The address to mint to.
           * @param ids     The token ids to mint.
           * @param amounts The quantities to mint.
           * @param data    The data to pass if receiver is a contract.
           */
          function _batchMint(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              // Put ids length on the stack to save MLOADs.
              uint256 idsLength = ids.length;
              for (uint256 i = 0; i < idsLength; ) {
                  // Increment mint counts.
                  _incrementMintCounts(to, ids[i], amounts[i]);
                  unchecked {
                      ++i;
                  }
              }
              ERC1155._batchMint(to, ids, amounts, data);
          }
          /**
           * @dev Subtracts from the internal counters for a burn.
           *
           * @param by     The address calling the burn.
           * @param from   The address to burn from.
           * @param id     The token id to burn.
           * @param amount The amount to burn.
           */
          function _burn(
              address by,
              address from,
              uint256 id,
              uint256 amount
          ) internal virtual override {
              // Reduce the supply.
              _reduceSupplyOnBurn(id, amount);
              ERC1155._burn(by, from, id, amount);
          }
          /**
           * @dev Subtracts from the internal counters for a batch burn.
           *
           * @param by      The address calling the burn.
           * @param from    The address to burn from.
           * @param ids     The token ids to burn.
           * @param amounts The amounts to burn.
           */
          function _batchBurn(
              address by,
              address from,
              uint256[] memory ids,
              uint256[] memory amounts
          ) internal virtual override {
              // Put ids length on the stack to save MLOADs.
              uint256 idsLength = ids.length;
              for (uint256 i = 0; i < idsLength; ) {
                  // Reduce the supply.
                  _reduceSupplyOnBurn(ids[i], amounts[i]);
                  unchecked {
                      ++i;
                  }
              }
              ERC1155._batchBurn(by, from, ids, amounts);
          }
          function _reduceSupplyOnBurn(uint256 id, uint256 amount) internal {
              // Get the current token supply.
              TokenSupply storage tokenSupply = _tokenSupply[id];
              // Reduce the totalSupply.
              unchecked {
                  tokenSupply.totalSupply -= uint64(amount);
              }
          }
          /**
           * @dev Internal function to increment mint counts.
           *
           *      Note that this function does not check if the mint exceeds
           *      maxSupply, which should be validated before this function is called.
           *
           * @param to     The address to mint to.
           * @param id     The token id to mint.
           * @param amount The quantity to mint.
           */
          function _incrementMintCounts(
              address to,
              uint256 id,
              uint256 amount
          ) internal {
              // Get the current token supply.
              TokenSupply storage tokenSupply = _tokenSupply[id];
              if (tokenSupply.totalMinted + amount > tokenSupply.maxSupply) {
                  revert MintExceedsMaxSupply(
                      tokenSupply.totalMinted + amount,
                      tokenSupply.maxSupply
                  );
              }
              // Increment supply and number minted.
              // Can be unchecked because maxSupply cannot be set to exceed uint64.
              unchecked {
                  tokenSupply.totalSupply += uint64(amount);
                  tokenSupply.totalMinted += uint64(amount);
                  // Increment total minted by user.
                  _totalMintedByUser[to] += amount;
                  // Increment total minted by user per token.
                  _totalMintedByUserPerToken[to][id] += amount;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { PublicDrop } from "./ERC1155SeaDropStructs.sol";
      import { CreatorPayout } from "./SeaDropStructs.sol";
      library ERC1155SeaDropContractOffererStorage {
          struct Layout {
              /// @notice The allowed Seaport addresses that can mint.
              mapping(address => bool) _allowedSeaport;
              /// @notice The enumerated allowed Seaport addresses.
              address[] _enumeratedAllowedSeaport;
              /// @notice The public drop data.
              mapping(uint256 => PublicDrop) _publicDrops;
              /// @notice The enumerated public drop indexes.
              uint256[] _enumeratedPublicDropIndexes;
              /// @notice The creator payout addresses and basis points.
              CreatorPayout[] _creatorPayouts;
              /// @notice The allow list merkle root.
              bytes32 _allowListMerkleRoot;
              /// @notice The allowed fee recipients.
              mapping(address => bool) _allowedFeeRecipients;
              /// @notice The enumerated allowed fee recipients.
              address[] _enumeratedFeeRecipients;
              /// @notice The allowed server-side signers.
              mapping(address => bool) _allowedSigners;
              /// @notice The enumerated allowed signers.
              address[] _enumeratedSigners;
              /// @notice The used signature digests.
              mapping(bytes32 => bool) _usedDigests;
              /// @notice The allowed payers.
              mapping(address => bool) _allowedPayers;
              /// @notice The enumerated allowed payers.
              address[] _enumeratedPayers;
          }
          bytes32 internal constant STORAGE_SLOT =
              bytes32(
                  uint256(
                      keccak256("contracts.storage.ERC1155SeaDropContractOfferer")
                  ) - 1
              );
          function layout() internal pure returns (Layout storage l) {
              bytes32 slot = STORAGE_SLOT;
              assembly {
                  l.slot := slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { PublicDrop } from "./ERC1155SeaDropStructs.sol";
      import { SeaDropErrorsAndEvents } from "./SeaDropErrorsAndEvents.sol";
      interface ERC1155SeaDropErrorsAndEvents is SeaDropErrorsAndEvents {
          /**
           * @dev Revert with an error if an empty PublicDrop is provided
           *      for an already-empty public drop.
           */
          error PublicDropStageNotPresent();
          /**
           * @dev Revert with an error if the mint quantity exceeds the
           *      max minted per wallet for a certain token id.
           */
          error MintQuantityExceedsMaxMintedPerWalletForTokenId(
              uint256 tokenId,
              uint256 total,
              uint256 allowed
          );
          /**
           * @dev Revert with an error if the target token id to mint is not within
           *      the drop stage range.
           */
          error TokenIdNotWithinDropStageRange(
              uint256 tokenId,
              uint256 startTokenId,
              uint256 endTokenId
          );
          /**
           *  @notice Revert with an error if the number of maxSupplyAmounts doesn't
           *          match the number of maxSupplyTokenIds.
           */
          error MaxSupplyMismatch();
          /**
           *  @notice Revert with an error if the number of mint tokenIds doesn't
           *          match the number of mint amounts.
           */
          error MintAmountsMismatch();
          /**
           * @notice Revert with an error if the mint order offer contains
           *         a duplicate tokenId.
           */
          error OfferContainsDuplicateTokenId(uint256 tokenId);
          /**
           * @dev Revert if the fromTokenId is greater than the toTokenId.
           */
          error InvalidFromAndToTokenId(uint256 fromTokenId, uint256 toTokenId);
          /**
           *  @notice Revert with an error if the number of publicDropIndexes doesn't
           *          match the number of publicDrops.
           */
          error PublicDropsMismatch();
          /**
           * @dev An event with updated public drop data.
           */
          event PublicDropUpdated(PublicDrop publicDrop, uint256 index);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { AllowListData, CreatorPayout } from "./SeaDropStructs.sol";
      /**
       * @notice A struct defining public drop data.
       *         Designed to fit efficiently in two storage slots.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       * @param paymentToken             The payment token address. Null for
       *                                 native token.
       * @param fromTokenId              The start token id for the stage.
       * @param toTokenId                The end token id for the stage.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed. (The limit for this field is
       *                                 2^16 - 1)
       * @param maxTotalMintableByWalletPerToken Maximum total number of mints a user
       *                                 is allowed for the token id. (The limit for
       *                                 this field is 2^16 - 1)
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       */
      struct PublicDrop {
          // slot 1
          uint80 startPrice; // 80/512 bits
          uint80 endPrice; // 160/512 bits
          uint40 startTime; // 200/512 bits
          uint40 endTime; // 240/512 bits
          bool restrictFeeRecipients; // 248/512 bits
          // uint8 unused;
          // slot 2
          address paymentToken; // 408/512 bits
          uint24 fromTokenId; // 432/512 bits
          uint24 toTokenId; // 456/512 bits
          uint16 maxTotalMintableByWallet; // 472/512 bits
          uint16 maxTotalMintableByWalletPerToken; // 488/512 bits
          uint16 feeBps; // 504/512 bits
      }
      /**
       * @notice A struct defining mint params for an allow list.
       *         An allow list leaf will be composed of `msg.sender` and
       *         the following params.
       *
       *         Note: Since feeBps is encoded in the leaf, backend should ensure
       *         that feeBps is acceptable before generating a proof.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token for the mint. Null for
       *                                 native token.
       * @param fromTokenId              The start token id for the stage.
       * @param toTokenId                The end token id for the stage.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed.
       * @param maxTotalMintableByWalletPerToken Maximum total number of mints a user
       *                                 is allowed for the token id.
       * @param maxTokenSupplyForStage   The limit of token supply this stage can
       *                                 mint within.
       * @param dropStageIndex           The drop stage index to emit with the event
       *                                 for analytical purposes. This should be
       *                                 non-zero since the public mint emits with
       *                                 index zero.
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct MintParams {
          uint256 startPrice;
          uint256 endPrice;
          uint256 startTime;
          uint256 endTime;
          address paymentToken;
          uint256 fromTokenId;
          uint256 toTokenId;
          uint256 maxTotalMintableByWallet;
          uint256 maxTotalMintableByWalletPerToken;
          uint256 maxTokenSupplyForStage;
          uint256 dropStageIndex; // non-zero
          uint256 feeBps;
          bool restrictFeeRecipients;
      }
      /**
       * @dev Struct containing internal SeaDrop implementation logic
       *      mint details to avoid stack too deep.
       *
       * @param feeRecipient The fee recipient.
       * @param payer        The payer of the mint.
       * @param minter       The mint recipient.
       * @param tokenIds     The tokenIds to mint.
       * @param quantities   The number of tokens to mint per tokenId.
       * @param withEffects  Whether to apply state changes of the mint.
       */
      struct MintDetails {
          address feeRecipient;
          address payer;
          address minter;
          uint256[] tokenIds;
          uint256[] quantities;
          bool withEffects;
      }
      /**
       * @notice A struct to configure multiple contract options in one transaction.
       */
      struct MultiConfigureStruct {
          uint256[] maxSupplyTokenIds;
          uint256[] maxSupplyAmounts;
          string baseURI;
          string contractURI;
          PublicDrop[] publicDrops;
          uint256[] publicDropsIndexes;
          string dropURI;
          AllowListData allowListData;
          CreatorPayout[] creatorPayouts;
          bytes32 provenanceHash;
          address[] allowedFeeRecipients;
          address[] disallowedFeeRecipients;
          address[] allowedPayers;
          address[] disallowedPayers;
          // Server-signed
          address[] allowedSigners;
          address[] disallowedSigners;
          // ERC-2981
          address royaltyReceiver;
          uint96 royaltyBps;
          // Mint
          address mintRecipient;
          uint256[] mintTokenIds;
          uint256[] mintAmounts;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      /**
       * @notice A struct defining a creator payout address and basis points.
       *
       * @param payoutAddress The payout address.
       * @param basisPoints   The basis points to pay out to the creator.
       *                      The total creator payouts must equal 10_000 bps.
       */
      struct CreatorPayout {
          address payoutAddress;
          uint16 basisPoints;
      }
      /**
       * @notice A struct defining allow list data (for minting an allow list).
       *
       * @param merkleRoot    The merkle root for the allow list.
       * @param publicKeyURIs If the allowListURI is encrypted, a list of URIs
       *                      pointing to the public keys. Empty if unencrypted.
       * @param allowListURI  The URI for the allow list.
       */
      struct AllowListData {
          bytes32 merkleRoot;
          string[] publicKeyURIs;
          string allowListURI;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      /**
       * @title  ERC1155ConduitPreapproved
       * @notice Solady's ERC1155 with the OpenSea conduit preapproved.
       */
      abstract contract ERC1155ConduitPreapproved is ERC1155 {
          /// @dev The canonical OpenSea conduit.
          address internal constant _CONDUIT =
              0x1E0049783F008A0085193E00003D00cd54003c71;
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual override {
              _safeTransfer(_by(), from, to, id, amount, data);
          }
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) public virtual override {
              _safeBatchTransfer(_by(), from, to, ids, amounts, data);
          }
          function isApprovedForAll(
              address owner,
              address operator
          ) public view virtual override returns (bool) {
              if (operator == _CONDUIT) return true;
              return ERC1155.isApprovedForAll(owner, operator);
          }
          function _by() internal view virtual returns (address result) {
              assembly {
                  // `msg.sender == _CONDUIT ? address(0) : msg.sender`.
                  result := mul(iszero(eq(caller(), _CONDUIT)), caller())
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple ERC1155 implementation.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC1155.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol)
      ///
      /// @dev Note:
      /// The ERC1155 standard allows for self-approvals.
      /// For performance, this implementation WILL NOT revert for such actions.
      /// Please add any checks with overrides if desired.
      abstract contract ERC1155 {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The lengths of the input arrays are not the same.
          error ArrayLengthsMismatch();
          /// @dev Cannot mint or transfer to the zero address.
          error TransferToZeroAddress();
          /// @dev The recipient's balance has overflowed.
          error AccountBalanceOverflow();
          /// @dev Insufficient balance.
          error InsufficientBalance();
          /// @dev Only the token owner or an approved account can manage the tokens.
          error NotOwnerNorApproved();
          /// @dev Cannot safely transfer to a contract that does not implement
          /// the ERC1155Receiver interface.
          error TransferToNonERC1155ReceiverImplementer();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                           EVENTS                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Emitted when `amount` of token `id` is transferred
          /// from `from` to `to` by `operator`.
          event TransferSingle(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256 id,
              uint256 amount
          );
          /// @dev Emitted when `amounts` of token `ids` are transferred
          /// from `from` to `to` by `operator`.
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] amounts
          );
          /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
          event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
          /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
          /// is updated to `value`. This event is not used in the base contract.
          /// You may need to emit this event depending on your URI logic.
          ///
          /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
          event URI(string value, uint256 indexed id);
          /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
          uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
              0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
          /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
          uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
              0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
          /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
          uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
              0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The `ownerSlotSeed` of a given owner is given by.
          /// ```
          ///     let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))
          /// ```
          ///
          /// The balance slot of `owner` is given by.
          /// ```
          ///     mstore(0x20, ownerSlotSeed)
          ///     mstore(0x00, id)
          ///     let balanceSlot := keccak256(0x00, 0x40)
          /// ```
          ///
          /// The operator approval slot of `owner` is given by.
          /// ```
          ///     mstore(0x20, ownerSlotSeed)
          ///     mstore(0x00, operator)
          ///     let operatorApprovalSlot := keccak256(0x0c, 0x34)
          /// ```
          uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                      ERC1155 METADATA                      */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the URI for token `id`.
          ///
          /// You can either return the same templated URI for all token IDs,
          /// (e.g. "https://example.com/api/{id}.json"),
          /// or return a unique URI for each `id`.
          ///
          /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
          function uri(uint256 id) public view virtual returns (string memory);
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          ERC1155                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the amount of `id` owned by `owner`.
          function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, owner)
                  mstore(0x00, id)
                  result := sload(keccak256(0x00, 0x40))
              }
          }
          /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
          function isApprovedForAll(address owner, address operator)
              public
              view
              virtual
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, owner)
                  mstore(0x00, operator)
                  result := sload(keccak256(0x0c, 0x34))
              }
          }
          /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
          ///
          /// Emits a {ApprovalForAll} event.
          function setApprovalForAll(address operator, bool isApproved) public virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Convert to 0 or 1.
                  isApproved := iszero(iszero(isApproved))
                  // Update the `isApproved` for (`msg.sender`, `operator`).
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, caller())
                  mstore(0x00, operator)
                  sstore(keccak256(0x0c, 0x34), isApproved)
                  // Emit the {ApprovalForAll} event.
                  mstore(0x00, isApproved)
                  // forgefmt: disable-next-line
                  log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
              }
          }
          /// @dev Transfers `amount` of `id` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - If the caller is not `from`,
          ///   it must be approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                  mstore(0x20, fromSlotSeed)
                  // Clear the upper 96 bits.
                  from := shr(96, fromSlotSeed)
                  to := shr(96, toSlotSeed)
                  // Revert if `to` is the zero address.
                  if iszero(to) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // If the caller is not `from`, do the authorization check.
                  if iszero(eq(caller(), from)) {
                      mstore(0x00, caller())
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Subtract and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, toSlotSeed)
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // Do the {onERC1155Received} check if `to` is a smart contract.
                  if extcodesize(to) {
                      // Prepare the calldata.
                      let m := mload(0x40)
                      // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                      mstore(m, 0xf23a6e61)
                      mstore(add(m, 0x20), caller())
                      mstore(add(m, 0x40), from)
                      mstore(add(m, 0x60), id)
                      mstore(add(m, 0x80), amount)
                      mstore(add(m, 0xa0), 0xa0)
                      calldatacopy(add(m, 0xc0), sub(data.offset, 0x20), add(0x20, data.length))
                      // Revert if the call reverts.
                      if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) {
                          if returndatasize() {
                              // Bubble up the revert if the call reverts.
                              returndatacopy(0x00, 0x00, returndatasize())
                              revert(0x00, returndatasize())
                          }
                          mstore(m, 0)
                      }
                      // Load the returndata and compare it with the function selector.
                      if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                          mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
          }
          /// @dev Transfers `amounts` of `ids` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - `ids` and `amounts` must have the same length.
          /// - If the caller is not `from`,
          ///   it must be approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) public virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(ids.length, amounts.length)) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                  mstore(0x20, fromSlotSeed)
                  // Clear the upper 96 bits.
                  from := shr(96, fromSlotSeed)
                  to := shr(96, toSlotSeed)
                  // Revert if `to` is the zero address.
                  if iszero(to) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // If the caller is not `from`, do the authorization check.
                  if iszero(eq(caller(), from)) {
                      mstore(0x00, caller())
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, ids.length)
                      for { let i := 0 } iszero(eq(i, end)) { i := add(i, 0x20) } {
                          let amount := calldataload(add(amounts.offset, i))
                          // Subtract and store the updated balance of `from`.
                          {
                              mstore(0x20, fromSlotSeed)
                              mstore(0x00, calldataload(add(ids.offset, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x20, toSlotSeed)
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, ids.length))
                      let o := add(m, 0x40)
                      calldatacopy(o, sub(ids.offset, 0x20), n)
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, n))
                      o := add(o, n)
                      n := add(0x20, shl(5, amounts.length))
                      calldatacopy(o, sub(amounts.offset, 0x20), n)
                      n := sub(add(o, n), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to)
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransferCalldata(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // Do the {onERC1155BatchReceived} check if `to` is a smart contract.
                  if extcodesize(to) {
                      let m := mload(0x40)
                      // Prepare the calldata.
                      // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                      mstore(m, 0xbc197c81)
                      mstore(add(m, 0x20), caller())
                      mstore(add(m, 0x40), from)
                      // Copy the `ids`.
                      mstore(add(m, 0x60), 0xa0)
                      let n := add(0x20, shl(5, ids.length))
                      let o := add(m, 0xc0)
                      calldatacopy(o, sub(ids.offset, 0x20), n)
                      // Copy the `amounts`.
                      let s := add(0xa0, n)
                      mstore(add(m, 0x80), s)
                      o := add(o, n)
                      n := add(0x20, shl(5, amounts.length))
                      calldatacopy(o, sub(amounts.offset, 0x20), n)
                      // Copy the `data`.
                      mstore(add(m, 0xa0), add(s, n))
                      o := add(o, n)
                      n := add(0x20, data.length)
                      calldatacopy(o, sub(data.offset, 0x20), n)
                      n := sub(add(o, n), add(m, 0x1c))
                      // Revert if the call reverts.
                      if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                          if returndatasize() {
                              // Bubble up the revert if the call reverts.
                              returndatacopy(0x00, 0x00, returndatasize())
                              revert(0x00, returndatasize())
                          }
                          mstore(m, 0)
                      }
                      // Load the returndata and compare it with the function selector.
                      if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                          mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
          }
          /// @dev Returns the amounts of `ids` for `owners.
          ///
          /// Requirements:
          /// - `owners` and `ids` must have the same length.
          function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
              public
              view
              virtual
              returns (uint256[] memory balances)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(ids.length, owners.length)) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  balances := mload(0x40)
                  mstore(balances, ids.length)
                  let o := add(balances, 0x20)
                  let end := shl(5, ids.length)
                  mstore(0x40, add(end, o))
                  // Loop through all the `ids` and load the balances.
                  for { let i := 0 } iszero(eq(i, end)) { i := add(i, 0x20) } {
                      let owner := calldataload(add(owners.offset, i))
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)))
                      mstore(0x00, calldataload(add(ids.offset, i)))
                      mstore(add(o, i), sload(keccak256(0x00, 0x40)))
                  }
              }
          }
          /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
          /// See: https://eips.ethereum.org/EIPS/eip-165
          /// This function call must use less than 30000 gas.
          function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let s := shr(224, interfaceId)
                  // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
                  result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  INTERNAL MINT FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Mints `amount` of `id` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(address(0), to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                      mstore(0x14, to)
                      mstore(0x00, id)
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x00, id)
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(address(0), to, _single(id), _single(amount), data);
              }
              if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data);
          }
          /// @dev Mints `amounts` of `ids` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `ids` and `amounts` must have the same length.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function _batchMint(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(address(0), to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x00, mload(add(ids, i)))
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(address(0), to, ids, amounts, data);
              }
              if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  INTERNAL BURN FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Equivalent to `_burn(address(0), from, id, amount)`.
          function _burn(address from, uint256 id, uint256 amount) internal virtual {
              _burn(address(0), from, id, amount);
          }
          /// @dev Destroys `amount` of `id` from `from`.
          ///
          /// Requirements:
          /// - `from` must have at least `amount` of `id`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          ///
          /// Emits a {Transfer} event.
          function _burn(address by, address from, uint256 id, uint256 amount) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, address(0), _single(id), _single(amount), "");
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let from_ := shl(96, from)
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Decrease and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x00, id)
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, address(0), _single(id), _single(amount), "");
              }
          }
          /// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`.
          function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts)
              internal
              virtual
          {
              _batchBurn(address(0), from, ids, amounts);
          }
          /// @dev Destroys `amounts` of `ids` from `from`.
          ///
          /// Requirements:
          /// - `ids` and `amounts` must have the same length.
          /// - `from` must have at least `amounts` of `ids`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          ///
          /// Emits a {TransferBatch} event.
          function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts)
              internal
              virtual
          {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, address(0), ids, amounts, "");
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let from_ := shl(96, from)
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Decrease and store the updated balance of `to`.
                          {
                              mstore(0x00, mload(add(ids, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, address(0), ids, amounts, "");
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                INTERNAL APPROVAL FUNCTIONS                 */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Approve or remove the `operator` as an operator for `by`,
          /// without authorization checks.
          ///
          /// Emits a {ApprovalForAll} event.
          function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Convert to 0 or 1.
                  isApproved := iszero(iszero(isApproved))
                  // Update the `isApproved` for (`by`, `operator`).
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, by)
                  mstore(0x00, operator)
                  sstore(keccak256(0x0c, 0x34), isApproved)
                  // Emit the {ApprovalForAll} event.
                  mstore(0x00, isApproved)
                  let m := shr(96, not(0))
                  log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator))
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                INTERNAL TRANSFER FUNCTIONS                 */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`.
          function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data)
              internal
              virtual
          {
              _safeTransfer(address(0), from, to, id, amount, data);
          }
          /// @dev Transfers `amount` of `id` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function _safeTransfer(
              address by,
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let from_ := shl(96, from)
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Subtract and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x20, amount)
                  // forgefmt: disable-next-line
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data);
          }
          /// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`.
          function _safeBatchTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              _safeBatchTransfer(address(0), from, to, ids, amounts, data);
          }
          /// @dev Transfers `amounts` of `ids` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `ids` and `amounts` must have the same length.
          /// - `from` must have at least `amounts` of `ids`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function _safeBatchTransfer(
              address by,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let from_ := shl(96, from)
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_)
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_)
                  mstore(0x20, fromSlotSeed)
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Subtract and store the updated balance of `from`.
                          {
                              mstore(0x20, fromSlotSeed)
                              mstore(0x00, mload(add(ids, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x20, toSlotSeed)
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, ids, amounts, data);
              }
              if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                    HOOKS FOR OVERRIDING                    */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
          /// The is to help the compiler avoid producing dead bytecode.
          function _useBeforeTokenTransfer() internal view virtual returns (bool) {
              return false;
          }
          /// @dev Hook that is called before any token transfer.
          /// This includes minting and burning, as well as batched variants.
          ///
          /// The same hook is called on both single and batched variants.
          /// For single transfers, the length of the `id` and `amount` arrays are 1.
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /// @dev Override this function to return true if `_afterTokenTransfer` is used.
          /// The is to help the compiler avoid producing dead bytecode.
          function _useAfterTokenTransfer() internal view virtual returns (bool) {
              return false;
          }
          /// @dev Hook that is called after any token transfer.
          /// This includes minting and burning, as well as batched variants.
          ///
          /// The same hook is called on both single and batched variants.
          /// For single transfers, the length of the `id` and `amount` arrays are 1.
          function _afterTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                      PRIVATE HELPERS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Helper for calling the `_afterTokenTransfer` hook.
          /// The is to help the compiler avoid producing dead bytecode.
          function _afterTokenTransferCalldata(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) private {
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, ids, amounts, data);
              }
          }
          /// @dev Returns if `a` has bytecode of non-zero length.
          function _hasCode(address a) private view returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := extcodesize(a) // Can handle dirty upper bits.
              }
          }
          /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
          /// Reverts if the target does not support the function correctly.
          function _checkOnERC1155Received(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) private {
              /// @solidity memory-safe-assembly
              assembly {
                  // Prepare the calldata.
                  let m := mload(0x40)
                  // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                  mstore(m, 0xf23a6e61)
                  mstore(add(m, 0x20), caller())
                  mstore(add(m, 0x40), shr(96, shl(96, from)))
                  mstore(add(m, 0x60), id)
                  mstore(add(m, 0x80), amount)
                  mstore(add(m, 0xa0), 0xa0)
                  let n := mload(data)
                  mstore(add(m, 0xc0), n)
                  if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) }
                  // Revert if the call reverts.
                  if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
                      if returndatasize() {
                          // Bubble up the revert if the call reverts.
                          returndatacopy(0x00, 0x00, returndatasize())
                          revert(0x00, returndatasize())
                      }
                      mstore(m, 0)
                  }
                  // Load the returndata and compare it with the function selector.
                  if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                      mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
          /// Reverts if the target does not support the function correctly.
          function _checkOnERC1155BatchReceived(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) private {
              /// @solidity memory-safe-assembly
              assembly {
                  // Prepare the calldata.
                  let m := mload(0x40)
                  // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                  mstore(m, 0xbc197c81)
                  mstore(add(m, 0x20), caller())
                  mstore(add(m, 0x40), shr(96, shl(96, from)))
                  // Copy the `ids`.
                  mstore(add(m, 0x60), 0xa0)
                  let n := add(0x20, shl(5, mload(ids)))
                  let o := add(m, 0xc0)
                  pop(staticcall(gas(), 4, ids, n, o, n))
                  // Copy the `amounts`.
                  let s := add(0xa0, returndatasize())
                  mstore(add(m, 0x80), s)
                  o := add(o, returndatasize())
                  n := add(0x20, shl(5, mload(amounts)))
                  pop(staticcall(gas(), 4, amounts, n, o, n))
                  // Copy the `data`.
                  mstore(add(m, 0xa0), add(s, returndatasize()))
                  o := add(o, returndatasize())
                  n := add(0x20, mload(data))
                  pop(staticcall(gas(), 4, data, n, o, n))
                  n := sub(add(o, returndatasize()), add(m, 0x1c))
                  // Revert if the call reverts.
                  if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                      if returndatasize() {
                          // Bubble up the revert if the call reverts.
                          returndatacopy(0x00, 0x00, returndatasize())
                          revert(0x00, returndatasize())
                      }
                      mstore(m, 0)
                  }
                  // Load the returndata and compare it with the function selector.
                  if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                      mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Returns `x` in an array with a single element.
          function _single(uint256 x) private pure returns (uint256[] memory result) {
              assembly {
                  result := mload(0x40)
                  mstore(0x40, add(result, 0x40))
                  mstore(result, 1)
                  mstore(add(result, 0x20), x)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      import {
          BasicOrderType,
          ItemType,
          OrderType,
          Side
      } from "./ConsiderationEnums.sol";
      import {
          CalldataPointer,
          MemoryPointer
      } from "../helpers/PointerLibraries.sol";
      /**
       * @dev An order contains eleven components: an offerer, a zone (or account that
       *      can cancel the order or restrict who can fulfill the order depending on
       *      the type), the order type (specifying partial fill support as well as
       *      restricted order status), the start and end time, a hash that will be
       *      provided to the zone when validating restricted orders, a salt, a key
       *      corresponding to a given conduit, a counter, and an arbitrary number of
       *      offer items that can be spent along with consideration items that must
       *      be received by their respective recipient.
       */
      struct OrderComponents {
          address offerer;
          address zone;
          OfferItem[] offer;
          ConsiderationItem[] consideration;
          OrderType orderType;
          uint256 startTime;
          uint256 endTime;
          bytes32 zoneHash;
          uint256 salt;
          bytes32 conduitKey;
          uint256 counter;
      }
      /**
       * @dev An offer item has five components: an item type (ETH or other native
       *      tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and
       *      ERC1155), a token address, a dual-purpose "identifierOrCriteria"
       *      component that will either represent a tokenId or a merkle root
       *      depending on the item type, and a start and end amount that support
       *      increasing or decreasing amounts over the duration of the respective
       *      order.
       */
      struct OfferItem {
          ItemType itemType;
          address token;
          uint256 identifierOrCriteria;
          uint256 startAmount;
          uint256 endAmount;
      }
      /**
       * @dev A consideration item has the same five components as an offer item and
       *      an additional sixth component designating the required recipient of the
       *      item.
       */
      struct ConsiderationItem {
          ItemType itemType;
          address token;
          uint256 identifierOrCriteria;
          uint256 startAmount;
          uint256 endAmount;
          address payable recipient;
      }
      /**
       * @dev A spent item is translated from a utilized offer item and has four
       *      components: an item type (ETH or other native tokens, ERC20, ERC721, and
       *      ERC1155), a token address, a tokenId, and an amount.
       */
      struct SpentItem {
          ItemType itemType;
          address token;
          uint256 identifier;
          uint256 amount;
      }
      /**
       * @dev A received item is translated from a utilized consideration item and has
       *      the same four components as a spent item, as well as an additional fifth
       *      component designating the required recipient of the item.
       */
      struct ReceivedItem {
          ItemType itemType;
          address token;
          uint256 identifier;
          uint256 amount;
          address payable recipient;
      }
      /**
       * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155
       *      matching, a group of six functions may be called that only requires a
       *      subset of the usual order arguments. Note the use of a "basicOrderType"
       *      enum; this represents both the usual order type as well as the "route"
       *      of the basic order (a simple derivation function for the basic order
       *      type is `basicOrderType = orderType + (4 * basicOrderRoute)`.)
       */
      struct BasicOrderParameters {
          // calldata offset
          address considerationToken; // 0x24
          uint256 considerationIdentifier; // 0x44
          uint256 considerationAmount; // 0x64
          address payable offerer; // 0x84
          address zone; // 0xa4
          address offerToken; // 0xc4
          uint256 offerIdentifier; // 0xe4
          uint256 offerAmount; // 0x104
          BasicOrderType basicOrderType; // 0x124
          uint256 startTime; // 0x144
          uint256 endTime; // 0x164
          bytes32 zoneHash; // 0x184
          uint256 salt; // 0x1a4
          bytes32 offererConduitKey; // 0x1c4
          bytes32 fulfillerConduitKey; // 0x1e4
          uint256 totalOriginalAdditionalRecipients; // 0x204
          AdditionalRecipient[] additionalRecipients; // 0x224
          bytes signature; // 0x244
          // Total length, excluding dynamic array data: 0x264 (580)
      }
      /**
       * @dev Basic orders can supply any number of additional recipients, with the
       *      implied assumption that they are supplied from the offered ETH (or other
       *      native token) or ERC20 token for the order.
       */
      struct AdditionalRecipient {
          uint256 amount;
          address payable recipient;
      }
      /**
       * @dev The full set of order components, with the exception of the counter,
       *      must be supplied when fulfilling more sophisticated orders or groups of
       *      orders. The total number of original consideration items must also be
       *      supplied, as the caller may specify additional consideration items.
       */
      struct OrderParameters {
          address offerer; // 0x00
          address zone; // 0x20
          OfferItem[] offer; // 0x40
          ConsiderationItem[] consideration; // 0x60
          OrderType orderType; // 0x80
          uint256 startTime; // 0xa0
          uint256 endTime; // 0xc0
          bytes32 zoneHash; // 0xe0
          uint256 salt; // 0x100
          bytes32 conduitKey; // 0x120
          uint256 totalOriginalConsiderationItems; // 0x140
          // offer.length                          // 0x160
      }
      /**
       * @dev Orders require a signature in addition to the other order parameters.
       */
      struct Order {
          OrderParameters parameters;
          bytes signature;
      }
      /**
       * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
       *      and a denominator (the total size of the order) in addition to the
       *      signature and other order parameters. It also supports an optional field
       *      for supplying extra data; this data will be provided to the zone if the
       *      order type is restricted and the zone is not the caller, or will be
       *      provided to the offerer as context for contract order types.
       */
      struct AdvancedOrder {
          OrderParameters parameters;
          uint120 numerator;
          uint120 denominator;
          bytes signature;
          bytes extraData;
      }
      /**
       * @dev Orders can be validated (either explicitly via `validate`, or as a
       *      consequence of a full or partial fill), specifically cancelled (they can
       *      also be cancelled in bulk via incrementing a per-zone counter), and
       *      partially or fully filled (with the fraction filled represented by a
       *      numerator and denominator).
       */
      struct OrderStatus {
          bool isValidated;
          bool isCancelled;
          uint120 numerator;
          uint120 denominator;
      }
      /**
       * @dev A criteria resolver specifies an order, side (offer vs. consideration),
       *      and item index. It then provides a chosen identifier (i.e. tokenId)
       *      alongside a merkle proof demonstrating the identifier meets the required
       *      criteria.
       */
      struct CriteriaResolver {
          uint256 orderIndex;
          Side side;
          uint256 index;
          uint256 identifier;
          bytes32[] criteriaProof;
      }
      /**
       * @dev A fulfillment is applied to a group of orders. It decrements a series of
       *      offer and consideration items, then generates a single execution
       *      element. A given fulfillment can be applied to as many offer and
       *      consideration items as desired, but must contain at least one offer and
       *      at least one consideration that match. The fulfillment must also remain
       *      consistent on all key parameters across all offer items (same offerer,
       *      token, type, tokenId, and conduit preference) as well as across all
       *      consideration items (token, type, tokenId, and recipient).
       */
      struct Fulfillment {
          FulfillmentComponent[] offerComponents;
          FulfillmentComponent[] considerationComponents;
      }
      /**
       * @dev Each fulfillment component contains one index referencing a specific
       *      order and another referencing a specific offer or consideration item.
       */
      struct FulfillmentComponent {
          uint256 orderIndex;
          uint256 itemIndex;
      }
      /**
       * @dev An execution is triggered once all consideration items have been zeroed
       *      out. It sends the item in question from the offerer to the item's
       *      recipient, optionally sourcing approvals from either this contract
       *      directly or from the offerer's chosen conduit if one is specified. An
       *      execution is not provided as an argument, but rather is derived via
       *      orders, criteria resolvers, and fulfillments (where the total number of
       *      executions will be less than or equal to the total number of indicated
       *      fulfillments) and returned as part of `matchOrders`.
       */
      struct Execution {
          ReceivedItem item;
          address offerer;
          bytes32 conduitKey;
      }
      /**
       * @dev Restricted orders are validated post-execution by calling validateOrder
       *      on the zone. This struct provides context about the order fulfillment
       *      and any supplied extraData, as well as all order hashes fulfilled in a
       *      call to a match or fulfillAvailable method.
       */
      struct ZoneParameters {
          bytes32 orderHash;
          address fulfiller;
          address offerer;
          SpentItem[] offer;
          ReceivedItem[] consideration;
          bytes extraData;
          bytes32[] orderHashes;
          uint256 startTime;
          uint256 endTime;
          bytes32 zoneHash;
      }
      /**
       * @dev Zones and contract offerers can communicate which schemas they implement
       *      along with any associated metadata related to each schema.
       */
      struct Schema {
          uint256 id;
          bytes metadata;
      }
      using StructPointers for OrderComponents global;
      using StructPointers for OfferItem global;
      using StructPointers for ConsiderationItem global;
      using StructPointers for SpentItem global;
      using StructPointers for ReceivedItem global;
      using StructPointers for BasicOrderParameters global;
      using StructPointers for AdditionalRecipient global;
      using StructPointers for OrderParameters global;
      using StructPointers for Order global;
      using StructPointers for AdvancedOrder global;
      using StructPointers for OrderStatus global;
      using StructPointers for CriteriaResolver global;
      using StructPointers for Fulfillment global;
      using StructPointers for FulfillmentComponent global;
      using StructPointers for Execution global;
      using StructPointers for ZoneParameters global;
      /**
       * @dev This library provides a set of functions for converting structs to
       *      pointers.
       */
      library StructPointers {
          /**
           * @dev Get a MemoryPointer from OrderComponents.
           *
           * @param obj The OrderComponents object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderComponents memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderComponents.
           *
           * @param obj The OrderComponents object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderComponents calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OfferItem.
           *
           * @param obj The OfferItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OfferItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OfferItem.
           *
           * @param obj The OfferItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OfferItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ConsiderationItem.
           *
           * @param obj The ConsiderationItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ConsiderationItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ConsiderationItem.
           *
           * @param obj The ConsiderationItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ConsiderationItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from SpentItem.
           *
           * @param obj The SpentItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              SpentItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from SpentItem.
           *
           * @param obj The SpentItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              SpentItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ReceivedItem.
           *
           * @param obj The ReceivedItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ReceivedItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ReceivedItem.
           *
           * @param obj The ReceivedItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ReceivedItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from BasicOrderParameters.
           *
           * @param obj The BasicOrderParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              BasicOrderParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from BasicOrderParameters.
           *
           * @param obj The BasicOrderParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              BasicOrderParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from AdditionalRecipient.
           *
           * @param obj The AdditionalRecipient object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              AdditionalRecipient memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from AdditionalRecipient.
           *
           * @param obj The AdditionalRecipient object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              AdditionalRecipient calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OrderParameters.
           *
           * @param obj The OrderParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderParameters.
           *
           * @param obj The OrderParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Order.
           *
           * @param obj The Order object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Order memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Order.
           *
           * @param obj The Order object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Order calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from AdvancedOrder.
           *
           * @param obj The AdvancedOrder object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              AdvancedOrder memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from AdvancedOrder.
           *
           * @param obj The AdvancedOrder object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              AdvancedOrder calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OrderStatus.
           *
           * @param obj The OrderStatus object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderStatus memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderStatus.
           *
           * @param obj The OrderStatus object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderStatus calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from CriteriaResolver.
           *
           * @param obj The CriteriaResolver object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              CriteriaResolver memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from CriteriaResolver.
           *
           * @param obj The CriteriaResolver object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              CriteriaResolver calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Fulfillment.
           *
           * @param obj The Fulfillment object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Fulfillment memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Fulfillment.
           *
           * @param obj The Fulfillment object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Fulfillment calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from FulfillmentComponent.
           *
           * @param obj The FulfillmentComponent object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              FulfillmentComponent memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from FulfillmentComponent.
           *
           * @param obj The FulfillmentComponent object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              FulfillmentComponent calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Execution.
           *
           * @param obj The Execution object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Execution memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Execution.
           *
           * @param obj The Execution object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Execution calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ZoneParameters.
           *
           * @param obj The ZoneParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ZoneParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ZoneParameters.
           *
           * @param obj The ZoneParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ZoneParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      import {ReceivedItem, Schema, SpentItem} from "../lib/ConsiderationStructs.sol";
      import {IERC165} from "../interfaces/IERC165.sol";
      /**
       * @title ContractOffererInterface
       * @notice Contains the minimum interfaces needed to interact with a contract
       *         offerer.
       */
      interface ContractOffererInterface is IERC165 {
          /**
           * @dev Generates an order with the specified minimum and maximum spent
           *      items, and optional context (supplied as extraData).
           *
           * @param fulfiller       The address of the fulfiller.
           * @param minimumReceived The minimum items that the caller is willing to
           *                        receive.
           * @param maximumSpent    The maximum items the caller is willing to spend.
           * @param context         Additional context of the order.
           *
           * @return offer         A tuple containing the offer items.
           * @return consideration A tuple containing the consideration items.
           */
          function generateOrder(
              address fulfiller,
              SpentItem[] calldata minimumReceived,
              SpentItem[] calldata maximumSpent,
              bytes calldata context // encoded based on the schemaID
          ) external returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
          /**
           * @dev Ratifies an order with the specified offer, consideration, and
           *      optional context (supplied as extraData).
           *
           * @param offer         The offer items.
           * @param consideration The consideration items.
           * @param context       Additional context of the order.
           * @param orderHashes   The hashes to ratify.
           * @param contractNonce The nonce of the contract.
           *
           * @return ratifyOrderMagicValue The magic value returned by the contract
           *                               offerer.
           */
          function ratifyOrder(
              SpentItem[] calldata offer,
              ReceivedItem[] calldata consideration,
              bytes calldata context, // encoded based on the schemaID
              bytes32[] calldata orderHashes,
              uint256 contractNonce
          ) external returns (bytes4 ratifyOrderMagicValue);
          /**
           * @dev View function to preview an order generated in response to a minimum
           *      set of received items, maximum set of spent items, and context
           *      (supplied as extraData).
           *
           * @param caller          The address of the caller (e.g. Seaport).
           * @param fulfiller       The address of the fulfiller (e.g. the account
           *                        calling Seaport).
           * @param minimumReceived The minimum items that the caller is willing to
           *                        receive.
           * @param maximumSpent    The maximum items the caller is willing to spend.
           * @param context         Additional context of the order.
           *
           * @return offer         A tuple containing the offer items.
           * @return consideration A tuple containing the consideration items.
           */
          function previewOrder(
              address caller,
              address fulfiller,
              SpentItem[] calldata minimumReceived,
              SpentItem[] calldata maximumSpent,
              bytes calldata context // encoded based on the schemaID
          ) external view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
          /**
           * @dev Gets the metadata for this contract offerer.
           *
           * @return name    The name of the contract offerer.
           * @return schemas The schemas supported by the contract offerer.
           */
          function getSeaportMetadata() external view returns (string memory name, Schema[] memory schemas); // map to Seaport Improvement Proposal IDs
          function supportsInterface(bytes4 interfaceId) external view override returns (bool);
          // Additional functions and/or events based on implemented schemaIDs
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.19;
      /**
       * @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.19;
      interface ISeaDropTokenContractMetadata {
          /**
           * @dev Emit an event for token metadata reveals/updates,
           *      according to EIP-4906.
           *
           * @param _fromTokenId The start token id.
           * @param _toTokenId   The end token id.
           */
          event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
          /**
           * @dev Emit an event when the URI for the collection-level metadata
           *      is updated.
           */
          event ContractURIUpdated(string newContractURI);
          /**
           * @dev Emit an event with the previous and new provenance hash after
           *      being updated.
           */
          event ProvenanceHashUpdated(bytes32 previousHash, bytes32 newHash);
          /**
           * @dev Emit an event when the EIP-2981 royalty info is updated.
           */
          event RoyaltyInfoUpdated(address receiver, uint256 basisPoints);
          /**
           * @notice Throw if the max supply exceeds uint64, a limit
           *         due to the storage of bit-packed variables.
           */
          error CannotExceedMaxSupplyOfUint64(uint256 got);
          /**
           * @dev Revert with an error when attempting to set the provenance
           *      hash after the mint has started.
           */
          error ProvenanceHashCannotBeSetAfterMintStarted();
          /**
           * @dev Revert with an error when attempting to set the provenance
           *      hash after it has already been set.
           */
          error ProvenanceHashCannotBeSetAfterAlreadyBeingSet();
          /**
           * @notice Sets the base URI for the token metadata and emits an event.
           *
           * @param tokenURI The new base URI to set.
           */
          function setBaseURI(string calldata tokenURI) external;
          /**
           * @notice Sets the contract URI for contract metadata.
           *
           * @param newContractURI The new contract URI.
           */
          function setContractURI(string calldata newContractURI) external;
          /**
           * @notice Sets the provenance hash and emits an event.
           *
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it has not been
           *         modified after mint started.
           *
           *         This function will revert after the first item has been minted.
           *
           * @param newProvenanceHash The new provenance hash to set.
           */
          function setProvenanceHash(bytes32 newProvenanceHash) external;
          /**
           * @notice Sets the default royalty information.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator of
           *   10_000 basis points.
           */
          function setDefaultRoyalty(address receiver, uint96 feeNumerator) external;
          /**
           * @notice Returns the base URI for token metadata.
           */
          function baseURI() external view returns (string memory);
          /**
           * @notice Returns the contract URI.
           */
          function contractURI() external view returns (string memory);
          /**
           * @notice Returns the provenance hash.
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it is unmodified
           *         after mint has started.
           */
          function provenanceHash() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ISeaDropTokenContractMetadata
      } from "./ISeaDropTokenContractMetadata.sol";
      interface IERC1155ContractMetadata is ISeaDropTokenContractMetadata {
          /**
           * @dev A struct representing the supply info for a token id,
           *      packed into one storage slot.
           *
           * @param maxSupply   The max supply for the token id.
           * @param totalSupply The total token supply for the token id.
           *                    Subtracted when an item is burned.
           * @param totalMinted The total number of tokens minted for the token id.
           */
          struct TokenSupply {
              uint64 maxSupply; // 64/256 bits
              uint64 totalSupply; // 128/256 bits
              uint64 totalMinted; // 192/256 bits
          }
          /**
           * @dev Emit an event when the max token supply for a token id is updated.
           */
          event MaxSupplyUpdated(uint256 tokenId, uint256 newMaxSupply);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply.
           */
          error MintExceedsMaxSupply(uint256 total, uint256 maxSupply);
          /**
           * @notice Sets the max supply for a token id and emits an event.
           *
           * @param tokenId      The token id to set the max supply for.
           * @param newMaxSupply The new max supply to set.
           */
          function setMaxSupply(uint256 tokenId, uint256 newMaxSupply) external;
          /**
           * @notice Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @notice Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @notice Returns the max token supply for a token id.
           */
          function maxSupply(uint256 tokenId) external view returns (uint256);
          /**
           * @notice Returns the total supply for a token id.
           */
          function totalSupply(uint256 tokenId) external view returns (uint256);
          /**
           * @notice Returns the total minted for a token id.
           */
          function totalMinted(uint256 tokenId) external view returns (uint256);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      interface ICreatorToken {
          event TransferValidatorUpdated(address oldValidator, address newValidator);
          function getTransferValidator() external view returns (address validator);
          function getTransferValidationFunction()
              external
              view
              returns (bytes4 functionSignature, bool isViewFunction);
          function setTransferValidator(address validator) external;
      }
      interface ILegacyCreatorToken {
          event TransferValidatorUpdated(address oldValidator, address newValidator);
          function getTransferValidator() external view returns (address validator);
          function setTransferValidator(address validator) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      interface ITransferValidator721 {
          /// @notice Ensure that a transfer has been authorized for a specific tokenId
          function validateTransfer(
              address caller,
              address from,
              address to,
              uint256 tokenId
          ) external view;
      }
      interface ITransferValidator1155 {
          /// @notice Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and reduce the transferable amount remaining
          function validateTransfer(
              address caller,
              address from,
              address to,
              uint256 tokenId,
              uint256 amount
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      import { ICreatorToken } from "../interfaces/ICreatorToken.sol";
      /**
       * @title  TokenTransferValidator
       * @notice Functionality to use a transfer validator.
       */
      abstract contract TokenTransferValidator is ICreatorToken {
          /// @dev Store the transfer validator. The null address means no transfer validator is set.
          address internal _transferValidator;
          /// @notice Revert with an error if the transfer validator is being set to the same address.
          error SameTransferValidator();
          /// @notice Returns the currently active transfer validator.
          ///         The null address means no transfer validator is set.
          function getTransferValidator() external view returns (address) {
              return _transferValidator;
          }
          /// @notice Set the transfer validator.
          ///         The external method that uses this must include access control.
          function _setTransferValidator(address newValidator) internal {
              address oldValidator = _transferValidator;
              if (oldValidator == newValidator) {
                  revert SameTransferValidator();
              }
              _transferValidator = newValidator;
              emit TransferValidatorUpdated(oldValidator, newValidator);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple ERC2981 NFT Royalty Standard implementation.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol)
      abstract contract ERC2981 {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The royalty fee numerator exceeds the fee denominator.
          error RoyaltyOverflow();
          /// @dev The royalty receiver cannot be the zero address.
          error RoyaltyReceiverIsZeroAddress();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The default royalty info is given by:
          /// ```
          ///     let packed := sload(_ERC2981_MASTER_SLOT_SEED)
          ///     let receiver := shr(96, packed)
          ///     let royaltyFraction := xor(packed, shl(96, receiver))
          /// ```
          ///
          /// The per token royalty info is given by.
          /// ```
          ///     mstore(0x00, tokenId)
          ///     mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
          ///     let packed := sload(keccak256(0x00, 0x40))
          ///     let receiver := shr(96, packed)
          ///     let royaltyFraction := xor(packed, shl(96, receiver))
          /// ```
          uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          ERC2981                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Checks that `_feeDenominator` is non-zero.
          constructor() {
              require(_feeDenominator() != 0, "Fee denominator cannot be zero.");
          }
          /// @dev Returns the denominator for the royalty amount.
          /// Defaults to 10000, which represents fees in basis points.
          /// Override this function to return a custom amount if needed.
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
          /// See: https://eips.ethereum.org/EIPS/eip-165
          /// This function call must use less than 30000 gas.
          function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let s := shr(224, interfaceId)
                  // ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a.
                  result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a))
              }
          }
          /// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`.
          function royaltyInfo(uint256 tokenId, uint256 salePrice)
              public
              view
              virtual
              returns (address receiver, uint256 royaltyAmount)
          {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  let packed := sload(keccak256(0x00, 0x40))
                  receiver := shr(96, packed)
                  if iszero(receiver) {
                      packed := sload(mload(0x20))
                      receiver := shr(96, packed)
                  }
                  let x := salePrice
                  let y := xor(packed, shl(96, receiver)) // `feeNumerator`.
                  // Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                  // Out-of-gas revert. Should not be triggered in practice, but included for safety.
                  returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y))))
                  royaltyAmount := div(mul(x, y), feeDenominator)
              }
          }
          /// @dev Sets the default royalty `receiver` and `feeNumerator`.
          ///
          /// Requirements:
          /// - `receiver` must not be the zero address.
          /// - `feeNumerator` must not be greater than the fee denominator.
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  feeNumerator := shr(160, shl(160, feeNumerator))
                  if gt(feeNumerator, feeDenominator) {
                      mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
                      revert(0x1c, 0x04)
                  }
                  let packed := shl(96, receiver)
                  if iszero(packed) {
                      mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator))
              }
          }
          /// @dev Sets the default royalty `receiver` and `feeNumerator` to zero.
          function _deleteDefaultRoyalty() internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  sstore(_ERC2981_MASTER_SLOT_SEED, 0)
              }
          }
          /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`.
          ///
          /// Requirements:
          /// - `receiver` must not be the zero address.
          /// - `feeNumerator` must not be greater than the fee denominator.
          function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
              internal
              virtual
          {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  feeNumerator := shr(160, shl(160, feeNumerator))
                  if gt(feeNumerator, feeDenominator) {
                      mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
                      revert(0x1c, 0x04)
                  }
                  let packed := shl(96, receiver)
                  if iszero(packed) {
                      mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  sstore(keccak256(0x00, 0x40), or(packed, feeNumerator))
              }
          }
          /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero.
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  sstore(keccak256(0x00, 0x40), 0)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple single owner authorization mixin.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
      /// @dev While the ownable portion follows
      /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
      /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
      abstract contract Ownable {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The caller is not authorized to call the function.
          error Unauthorized();
          /// @dev The `newOwner` cannot be the zero address.
          error NewOwnerIsZeroAddress();
          /// @dev The `pendingOwner` does not have a valid handover request.
          error NoHandoverRequest();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                           EVENTS                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
          /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
          /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
          /// despite it not being as lightweight as a single argument event.
          event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
          /// @dev An ownership handover to `pendingOwner` has been requested.
          event OwnershipHandoverRequested(address indexed pendingOwner);
          /// @dev The ownership handover to `pendingOwner` has been canceled.
          event OwnershipHandoverCanceled(address indexed pendingOwner);
          /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
          uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
              0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
          /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
          uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
              0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
          /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
          uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
              0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
          /// It is intentionally chosen to be a high value
          /// to avoid collision with lower slots.
          /// The choice of manual storage layout is to enable compatibility
          /// with both regular and upgradeable contracts.
          uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
          /// The ownership handover slot of `newOwner` is given by:
          /// ```
          ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
          ///     let handoverSlot := keccak256(0x00, 0x20)
          /// ```
          /// It stores the expiry timestamp of the two-step ownership handover.
          uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     INTERNAL FUNCTIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Initializes the owner directly without authorization guard.
          /// This function must be called upon initialization,
          /// regardless of whether the contract is upgradeable or not.
          /// This is to enable generalization to both regular and upgradeable contracts,
          /// and to save gas in case the initial owner is not the caller.
          /// For performance reasons, this function will not check if there
          /// is an existing owner.
          function _initializeOwner(address newOwner) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Clean the upper 96 bits.
                  newOwner := shr(96, shl(96, newOwner))
                  // Store the new value.
                  sstore(not(_OWNER_SLOT_NOT), newOwner)
                  // Emit the {OwnershipTransferred} event.
                  log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
              }
          }
          /// @dev Sets the owner directly without authorization guard.
          function _setOwner(address newOwner) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  let ownerSlot := not(_OWNER_SLOT_NOT)
                  // Clean the upper 96 bits.
                  newOwner := shr(96, shl(96, newOwner))
                  // Emit the {OwnershipTransferred} event.
                  log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                  // Store the new value.
                  sstore(ownerSlot, newOwner)
              }
          }
          /// @dev Throws if the sender is not the owner.
          function _checkOwner() internal view virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // If the caller is not the stored owner, revert.
                  if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                      mstore(0x00, 0x82b42900) // `Unauthorized()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  PUBLIC UPDATE FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Allows the owner to transfer the ownership to `newOwner`.
          function transferOwnership(address newOwner) public payable virtual onlyOwner {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(shl(96, newOwner)) {
                      mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
              }
              _setOwner(newOwner);
          }
          /// @dev Allows the owner to renounce their ownership.
          function renounceOwnership() public payable virtual onlyOwner {
              _setOwner(address(0));
          }
          /// @dev Request a two-step ownership handover to the caller.
          /// The request will automatically expire in 48 hours (172800 seconds) by default.
          function requestOwnershipHandover() public payable virtual {
              unchecked {
                  uint256 expires = block.timestamp + ownershipHandoverValidFor();
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute and set the handover slot to `expires`.
                      mstore(0x0c, _HANDOVER_SLOT_SEED)
                      mstore(0x00, caller())
                      sstore(keccak256(0x0c, 0x20), expires)
                      // Emit the {OwnershipHandoverRequested} event.
                      log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                  }
              }
          }
          /// @dev Cancels the two-step ownership handover to the caller, if any.
          function cancelOwnershipHandover() public payable virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute and set the handover slot to 0.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, caller())
                  sstore(keccak256(0x0c, 0x20), 0)
                  // Emit the {OwnershipHandoverCanceled} event.
                  log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
              }
          }
          /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
          /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
          function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute and set the handover slot to 0.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, pendingOwner)
                  let handoverSlot := keccak256(0x0c, 0x20)
                  // If the handover does not exist, or has expired.
                  if gt(timestamp(), sload(handoverSlot)) {
                      mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                      revert(0x1c, 0x04)
                  }
                  // Set the handover slot to 0.
                  sstore(handoverSlot, 0)
              }
              _setOwner(pendingOwner);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   PUBLIC READ FUNCTIONS                    */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the owner of the contract.
          function owner() public view virtual returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := sload(not(_OWNER_SLOT_NOT))
              }
          }
          /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
          function ownershipHandoverExpiresAt(address pendingOwner)
              public
              view
              virtual
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute the handover slot.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, pendingOwner)
                  // Load the handover slot.
                  result := sload(keccak256(0x0c, 0x20))
              }
          }
          /// @dev Returns how long a two-step ownership handover is valid for in seconds.
          function ownershipHandoverValidFor() public view virtual returns (uint64) {
              return 48 * 3600;
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         MODIFIERS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Marks a function as only callable by the owner.
          modifier onlyOwner() virtual {
              _checkOwner();
              _;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.19;
      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) || (address(this).code.length == 0 && _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
      pragma solidity ^0.8.19;
      import { CreatorPayout, PublicDrop } from "./ERC721SeaDropStructs.sol";
      interface SeaDropErrorsAndEvents {
          /**
           * @notice The SeaDrop token types, emitted as part of
           *         `event SeaDropTokenDeployed`.
           */
          enum SEADROP_TOKEN_TYPE {
              ERC721_STANDARD,
              ERC721_CLONE,
              ERC721_UPGRADEABLE,
              ERC1155_STANDARD,
              ERC1155_CLONE,
              ERC1155_UPGRADEABLE
          }
          /**
           * @notice An event to signify that a SeaDrop token contract was deployed.
           */
          event SeaDropTokenDeployed(SEADROP_TOKEN_TYPE tokenType);
          /**
           * @notice Revert with an error if the function selector is not supported.
           */
          error UnsupportedFunctionSelector(bytes4 selector);
          /**
           * @dev Revert with an error if the drop stage is not active.
           */
          error NotActive(
              uint256 currentTimestamp,
              uint256 startTimestamp,
              uint256 endTimestamp
          );
          /**
           * @dev Revert with an error if the mint quantity exceeds the max allowed
           *      to be minted per wallet.
           */
          error MintQuantityExceedsMaxMintedPerWallet(uint256 total, uint256 allowed);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply.
           */
          error MintQuantityExceedsMaxSupply(uint256 total, uint256 maxSupply);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply for the stage.
           *      Note: The `maxTokenSupplyForStage` for public mint is
           *      always `type(uint).max`.
           */
          error MintQuantityExceedsMaxTokenSupplyForStage(
              uint256 total,
              uint256 maxTokenSupplyForStage
          );
          /**
           * @dev Revert if the fee recipient is the zero address.
           */
          error FeeRecipientCannotBeZeroAddress();
          /**
           * @dev Revert if the fee recipient is not already included.
           */
          error FeeRecipientNotPresent();
          /**
           * @dev Revert if the fee basis points is greater than 10_000.
           */
          error InvalidFeeBps(uint256 feeBps);
          /**
           * @dev Revert if the fee recipient is already included.
           */
          error DuplicateFeeRecipient();
          /**
           * @dev Revert if the fee recipient is restricted and not allowed.
           */
          error FeeRecipientNotAllowed(address got);
          /**
           * @dev Revert if the creator payout address is the zero address.
           */
          error CreatorPayoutAddressCannotBeZeroAddress();
          /**
           * @dev Revert if the creator payouts are not set.
           */
          error CreatorPayoutsNotSet();
          /**
           * @dev Revert if the creator payout basis points are zero.
           */
          error CreatorPayoutBasisPointsCannotBeZero();
          /**
           * @dev Revert if the total basis points for the creator payouts
           *      don't equal exactly 10_000.
           */
          error InvalidCreatorPayoutTotalBasisPoints(
              uint256 totalReceivedBasisPoints
          );
          /**
           * @dev Revert if the creator payout basis points don't add up to 10_000.
           */
          error InvalidCreatorPayoutBasisPoints(uint256 totalReceivedBasisPoints);
          /**
           * @dev Revert with an error if the allow list proof is invalid.
           */
          error InvalidProof();
          /**
           * @dev Revert if a supplied signer address is the zero address.
           */
          error SignerCannotBeZeroAddress();
          /**
           * @dev Revert with an error if a signer is not included in
           *      the enumeration when removing.
           */
          error SignerNotPresent();
          /**
           * @dev Revert with an error if a payer is not included in
           *      the enumeration when removing.
           */
          error PayerNotPresent();
          /**
           * @dev Revert with an error if a payer is already included in mapping
           *      when adding.
           */
          error DuplicatePayer();
          /**
           * @dev Revert with an error if a signer is already included in mapping
           *      when adding.
           */
          error DuplicateSigner();
          /**
           * @dev Revert with an error if the payer is not allowed. The minter must
           *      pay for their own mint.
           */
          error PayerNotAllowed(address got);
          /**
           * @dev Revert if a supplied payer address is the zero address.
           */
          error PayerCannotBeZeroAddress();
          /**
           * @dev Revert if the start time is greater than the end time.
           */
          error InvalidStartAndEndTime(uint256 startTime, uint256 endTime);
          /**
           * @dev Revert with an error if the signer payment token is not the same.
           */
          error InvalidSignedPaymentToken(address got, address want);
          /**
           * @dev Revert with an error if supplied signed mint price is less than
           *      the minimum specified.
           */
          error InvalidSignedMintPrice(
              address paymentToken,
              uint256 got,
              uint256 minimum
          );
          /**
           * @dev Revert with an error if supplied signed maxTotalMintableByWallet
           *      is greater than the maximum specified.
           */
          error InvalidSignedMaxTotalMintableByWallet(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed
           *      maxTotalMintableByWalletPerToken is greater than the maximum
           *      specified.
           */
          error InvalidSignedMaxTotalMintableByWalletPerToken(
              uint256 got,
              uint256 maximum
          );
          /**
           * @dev Revert with an error if the fromTokenId is not within range.
           */
          error InvalidSignedFromTokenId(uint256 got, uint256 minimum);
          /**
           * @dev Revert with an error if the toTokenId is not within range.
           */
          error InvalidSignedToTokenId(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed start time is less than
           *      the minimum specified.
           */
          error InvalidSignedStartTime(uint256 got, uint256 minimum);
          /**
           * @dev Revert with an error if supplied signed end time is greater than
           *      the maximum specified.
           */
          error InvalidSignedEndTime(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed maxTokenSupplyForStage
           *      is greater than the maximum specified.
           */
          error InvalidSignedMaxTokenSupplyForStage(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed feeBps is greater than
           *      the maximum specified, or less than the minimum.
           */
          error InvalidSignedFeeBps(uint256 got, uint256 minimumOrMaximum);
          /**
           * @dev Revert with an error if signed mint did not specify to restrict
           *      fee recipients.
           */
          error SignedMintsMustRestrictFeeRecipients();
          /**
           * @dev Revert with an error if a signature for a signed mint has already
           *      been used.
           */
          error SignatureAlreadyUsed();
          /**
           * @dev Revert with an error if the contract has no balance to withdraw.
           */
          error NoBalanceToWithdraw();
          /**
           * @dev Revert with an error if the caller is not an allowed Seaport.
           */
          error InvalidCallerOnlyAllowedSeaport(address caller);
          /**
           * @dev Revert with an error if the order does not have the ERC1155 magic
           *      consideration item to signify a consecutive mint.
           */
          error MustSpecifyERC1155ConsiderationItemForSeaDropMint();
          /**
           * @dev Revert with an error if the extra data version is not supported.
           */
          error UnsupportedExtraDataVersion(uint8 version);
          /**
           * @dev Revert with an error if the extra data encoding is not supported.
           */
          error InvalidExtraDataEncoding(uint8 version);
          /**
           * @dev Revert with an error if the provided substandard is not supported.
           */
          error InvalidSubstandard(uint8 substandard);
          /**
           * @dev Revert with an error if the implementation contract is called without
           *      delegatecall.
           */
          error OnlyDelegateCalled();
          /**
           * @dev Revert with an error if the provided allowed Seaport is the
           *      zero address.
           */
          error AllowedSeaportCannotBeZeroAddress();
          /**
           * @dev Emit an event when allowed Seaport contracts are updated.
           */
          event AllowedSeaportUpdated(address[] allowedSeaport);
          /**
           * @dev An event with details of a SeaDrop mint, for analytical purposes.
           *
           * @param payer          The address who payed for the tx.
           * @param dropStageIndex The drop stage index. Items minted through
           *                       public mint have dropStageIndex of 0
           */
          event SeaDropMint(address payer, uint256 dropStageIndex);
          /**
           * @dev An event with updated allow list data.
           *
           * @param previousMerkleRoot The previous allow list merkle root.
           * @param newMerkleRoot      The new allow list merkle root.
           * @param publicKeyURI       If the allow list is encrypted, the public key
           *                           URIs that can decrypt the list.
           *                           Empty if unencrypted.
           * @param allowListURI       The URI for the allow list.
           */
          event AllowListUpdated(
              bytes32 indexed previousMerkleRoot,
              bytes32 indexed newMerkleRoot,
              string[] publicKeyURI,
              string allowListURI
          );
          /**
           * @dev An event with updated drop URI.
           */
          event DropURIUpdated(string newDropURI);
          /**
           * @dev An event with the updated creator payout address.
           */
          event CreatorPayoutsUpdated(CreatorPayout[] creatorPayouts);
          /**
           * @dev An event with the updated allowed fee recipient.
           */
          event AllowedFeeRecipientUpdated(
              address indexed feeRecipient,
              bool indexed allowed
          );
          /**
           * @dev An event with the updated signer.
           */
          event SignerUpdated(address indexed signer, bool indexed allowed);
          /**
           * @dev An event with the updated payer.
           */
          event PayerUpdated(address indexed payer, bool indexed allowed);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      enum OrderType {
          // 0: no partial fills, anyone can execute
          FULL_OPEN,
          // 1: partial fills supported, anyone can execute
          PARTIAL_OPEN,
          // 2: no partial fills, only offerer or zone can execute
          FULL_RESTRICTED,
          // 3: partial fills supported, only offerer or zone can execute
          PARTIAL_RESTRICTED,
          // 4: contract order type
          CONTRACT
      }
      enum BasicOrderType {
          // 0: no partial fills, anyone can execute
          ETH_TO_ERC721_FULL_OPEN,
          // 1: partial fills supported, anyone can execute
          ETH_TO_ERC721_PARTIAL_OPEN,
          // 2: no partial fills, only offerer or zone can execute
          ETH_TO_ERC721_FULL_RESTRICTED,
          // 3: partial fills supported, only offerer or zone can execute
          ETH_TO_ERC721_PARTIAL_RESTRICTED,
          // 4: no partial fills, anyone can execute
          ETH_TO_ERC1155_FULL_OPEN,
          // 5: partial fills supported, anyone can execute
          ETH_TO_ERC1155_PARTIAL_OPEN,
          // 6: no partial fills, only offerer or zone can execute
          ETH_TO_ERC1155_FULL_RESTRICTED,
          // 7: partial fills supported, only offerer or zone can execute
          ETH_TO_ERC1155_PARTIAL_RESTRICTED,
          // 8: no partial fills, anyone can execute
          ERC20_TO_ERC721_FULL_OPEN,
          // 9: partial fills supported, anyone can execute
          ERC20_TO_ERC721_PARTIAL_OPEN,
          // 10: no partial fills, only offerer or zone can execute
          ERC20_TO_ERC721_FULL_RESTRICTED,
          // 11: partial fills supported, only offerer or zone can execute
          ERC20_TO_ERC721_PARTIAL_RESTRICTED,
          // 12: no partial fills, anyone can execute
          ERC20_TO_ERC1155_FULL_OPEN,
          // 13: partial fills supported, anyone can execute
          ERC20_TO_ERC1155_PARTIAL_OPEN,
          // 14: no partial fills, only offerer or zone can execute
          ERC20_TO_ERC1155_FULL_RESTRICTED,
          // 15: partial fills supported, only offerer or zone can execute
          ERC20_TO_ERC1155_PARTIAL_RESTRICTED,
          // 16: no partial fills, anyone can execute
          ERC721_TO_ERC20_FULL_OPEN,
          // 17: partial fills supported, anyone can execute
          ERC721_TO_ERC20_PARTIAL_OPEN,
          // 18: no partial fills, only offerer or zone can execute
          ERC721_TO_ERC20_FULL_RESTRICTED,
          // 19: partial fills supported, only offerer or zone can execute
          ERC721_TO_ERC20_PARTIAL_RESTRICTED,
          // 20: no partial fills, anyone can execute
          ERC1155_TO_ERC20_FULL_OPEN,
          // 21: partial fills supported, anyone can execute
          ERC1155_TO_ERC20_PARTIAL_OPEN,
          // 22: no partial fills, only offerer or zone can execute
          ERC1155_TO_ERC20_FULL_RESTRICTED,
          // 23: partial fills supported, only offerer or zone can execute
          ERC1155_TO_ERC20_PARTIAL_RESTRICTED
      }
      enum BasicOrderRouteType {
          // 0: provide Ether (or other native token) to receive offered ERC721 item.
          ETH_TO_ERC721,
          // 1: provide Ether (or other native token) to receive offered ERC1155 item.
          ETH_TO_ERC1155,
          // 2: provide ERC20 item to receive offered ERC721 item.
          ERC20_TO_ERC721,
          // 3: provide ERC20 item to receive offered ERC1155 item.
          ERC20_TO_ERC1155,
          // 4: provide ERC721 item to receive offered ERC20 item.
          ERC721_TO_ERC20,
          // 5: provide ERC1155 item to receive offered ERC20 item.
          ERC1155_TO_ERC20
      }
      enum ItemType {
          // 0: ETH on mainnet, MATIC on polygon, etc.
          NATIVE,
          // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work)
          ERC20,
          // 2: ERC721 items
          ERC721,
          // 3: ERC1155 items
          ERC1155,
          // 4: ERC721 items where a number of tokenIds are supported
          ERC721_WITH_CRITERIA,
          // 5: ERC1155 items where a number of ids are supported
          ERC1155_WITH_CRITERIA
      }
      enum Side {
          // 0: Items that can be spent
          OFFER,
          // 1: Items that must be received
          CONSIDERATION
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      type CalldataPointer is uint256;
      type ReturndataPointer is uint256;
      type MemoryPointer is uint256;
      using CalldataPointerLib for CalldataPointer global;
      using MemoryPointerLib for MemoryPointer global;
      using ReturndataPointerLib for ReturndataPointer global;
      using CalldataReaders for CalldataPointer global;
      using ReturndataReaders for ReturndataPointer global;
      using MemoryReaders for MemoryPointer global;
      using MemoryWriters for MemoryPointer global;
      CalldataPointer constant CalldataStart = CalldataPointer.wrap(0x04);
      MemoryPointer constant FreeMemoryPPtr = MemoryPointer.wrap(0x40);
      uint256 constant IdentityPrecompileAddress = 0x4;
      uint256 constant OffsetOrLengthMask = 0xffffffff;
      uint256 constant _OneWord = 0x20;
      uint256 constant _FreeMemoryPointerSlot = 0x40;
      /// @dev Allocates `size` bytes in memory by increasing the free memory pointer
      ///    and returns the memory pointer to the first byte of the allocated region.
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function malloc(uint256 size) pure returns (MemoryPointer mPtr) {
          assembly {
              mPtr := mload(_FreeMemoryPointerSlot)
              mstore(_FreeMemoryPointerSlot, add(mPtr, size))
          }
      }
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function getFreeMemoryPointer() pure returns (MemoryPointer mPtr) {
          mPtr = FreeMemoryPPtr.readMemoryPointer();
      }
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function setFreeMemoryPointer(MemoryPointer mPtr) pure {
          FreeMemoryPPtr.write(mPtr);
      }
      library CalldataPointerLib {
          function lt(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(CalldataPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          /// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata.
          ///      pointer `cdPtr` must point to some parent object with a dynamic
          ///      type's head stored at `cdPtr + headOffset`.
          function pptr(
              CalldataPointer cdPtr,
              uint256 headOffset
          ) internal pure returns (CalldataPointer cdPtrChild) {
              cdPtrChild = cdPtr.offset(
                  cdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
              );
          }
          /// @dev Resolves an offset stored at `cdPtr` to a calldata pointer.
          ///      `cdPtr` must point to some parent object with a dynamic type as its
          ///      first member, e.g. `struct { bytes data; }`
          function pptr(
              CalldataPointer cdPtr
          ) internal pure returns (CalldataPointer cdPtrChild) {
              cdPtrChild = cdPtr.offset(cdPtr.readUint256() & OffsetOrLengthMask);
          }
          /// @dev Returns the calldata pointer one word after `cdPtr`.
          function next(
              CalldataPointer cdPtr
          ) internal pure returns (CalldataPointer cdPtrNext) {
              assembly {
                  cdPtrNext := add(cdPtr, _OneWord)
              }
          }
          /// @dev Returns the calldata pointer `_offset` bytes after `cdPtr`.
          function offset(
              CalldataPointer cdPtr,
              uint256 _offset
          ) internal pure returns (CalldataPointer cdPtrNext) {
              assembly {
                  cdPtrNext := add(cdPtr, _offset)
              }
          }
          /// @dev Copies `size` bytes from calldata starting at `src` to memory at
          ///      `dst`.
          function copy(
              CalldataPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal pure {
              assembly {
                  calldatacopy(dst, src, size)
              }
          }
      }
      library ReturndataPointerLib {
          function lt(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(ReturndataPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          /// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata
          ///      pointer. `rdPtr` must point to some parent object with a dynamic
          ///      type's head stored at `rdPtr + headOffset`.
          function pptr(
              ReturndataPointer rdPtr,
              uint256 headOffset
          ) internal pure returns (ReturndataPointer rdPtrChild) {
              rdPtrChild = rdPtr.offset(
                  rdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
              );
          }
          /// @dev Resolves an offset stored at `rdPtr` to a returndata pointer.
          ///    `rdPtr` must point to some parent object with a dynamic type as its
          ///    first member, e.g. `struct { bytes data; }`
          function pptr(
              ReturndataPointer rdPtr
          ) internal pure returns (ReturndataPointer rdPtrChild) {
              rdPtrChild = rdPtr.offset(rdPtr.readUint256() & OffsetOrLengthMask);
          }
          /// @dev Returns the returndata pointer one word after `cdPtr`.
          function next(
              ReturndataPointer rdPtr
          ) internal pure returns (ReturndataPointer rdPtrNext) {
              assembly {
                  rdPtrNext := add(rdPtr, _OneWord)
              }
          }
          /// @dev Returns the returndata pointer `_offset` bytes after `cdPtr`.
          function offset(
              ReturndataPointer rdPtr,
              uint256 _offset
          ) internal pure returns (ReturndataPointer rdPtrNext) {
              assembly {
                  rdPtrNext := add(rdPtr, _offset)
              }
          }
          /// @dev Copies `size` bytes from returndata starting at `src` to memory at
          /// `dst`.
          function copy(
              ReturndataPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal pure {
              assembly {
                  returndatacopy(dst, src, size)
              }
          }
      }
      library MemoryPointerLib {
          function copy(
              MemoryPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal view {
              assembly {
                  let success := staticcall(
                      gas(),
                      IdentityPrecompileAddress,
                      src,
                      size,
                      dst,
                      size
                  )
                  if or(iszero(returndatasize()), iszero(success)) {
                      revert(0, 0)
                  }
              }
          }
          function lt(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(MemoryPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          function hash(
              MemoryPointer ptr,
              uint256 length
          ) internal pure returns (bytes32 _hash) {
              assembly {
                  _hash := keccak256(ptr, length)
              }
          }
          /// @dev Returns the memory pointer one word after `mPtr`.
          function next(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer mPtrNext) {
              assembly {
                  mPtrNext := add(mPtr, _OneWord)
              }
          }
          /// @dev Returns the memory pointer `_offset` bytes after `mPtr`.
          function offset(
              MemoryPointer mPtr,
              uint256 _offset
          ) internal pure returns (MemoryPointer mPtrNext) {
              assembly {
                  mPtrNext := add(mPtr, _offset)
              }
          }
          /// @dev Resolves a pointer at `mPtr + headOffset` to a memory
          ///    pointer. `mPtr` must point to some parent object with a dynamic
          ///    type's pointer stored at `mPtr + headOffset`.
          function pptr(
              MemoryPointer mPtr,
              uint256 headOffset
          ) internal pure returns (MemoryPointer mPtrChild) {
              mPtrChild = mPtr.offset(headOffset).readMemoryPointer();
          }
          /// @dev Resolves a pointer stored at `mPtr` to a memory pointer.
          ///    `mPtr` must point to some parent object with a dynamic type as its
          ///    first member, e.g. `struct { bytes data; }`
          function pptr(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer mPtrChild) {
              mPtrChild = mPtr.readMemoryPointer();
          }
      }
      library CalldataReaders {
          /// @dev Reads the value at `cdPtr` and applies a mask to return only the
          ///    last 4 bytes.
          function readMaskedUint256(
              CalldataPointer cdPtr
          ) internal pure returns (uint256 value) {
              value = cdPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `cdPtr` in calldata.
          function readBool(
              CalldataPointer cdPtr
          ) internal pure returns (bool value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the address at `cdPtr` in calldata.
          function readAddress(
              CalldataPointer cdPtr
          ) internal pure returns (address value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes1 at `cdPtr` in calldata.
          function readBytes1(
              CalldataPointer cdPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes2 at `cdPtr` in calldata.
          function readBytes2(
              CalldataPointer cdPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes3 at `cdPtr` in calldata.
          function readBytes3(
              CalldataPointer cdPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes4 at `cdPtr` in calldata.
          function readBytes4(
              CalldataPointer cdPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes5 at `cdPtr` in calldata.
          function readBytes5(
              CalldataPointer cdPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes6 at `cdPtr` in calldata.
          function readBytes6(
              CalldataPointer cdPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes7 at `cdPtr` in calldata.
          function readBytes7(
              CalldataPointer cdPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes8 at `cdPtr` in calldata.
          function readBytes8(
              CalldataPointer cdPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes9 at `cdPtr` in calldata.
          function readBytes9(
              CalldataPointer cdPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes10 at `cdPtr` in calldata.
          function readBytes10(
              CalldataPointer cdPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes11 at `cdPtr` in calldata.
          function readBytes11(
              CalldataPointer cdPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes12 at `cdPtr` in calldata.
          function readBytes12(
              CalldataPointer cdPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes13 at `cdPtr` in calldata.
          function readBytes13(
              CalldataPointer cdPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes14 at `cdPtr` in calldata.
          function readBytes14(
              CalldataPointer cdPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes15 at `cdPtr` in calldata.
          function readBytes15(
              CalldataPointer cdPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes16 at `cdPtr` in calldata.
          function readBytes16(
              CalldataPointer cdPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes17 at `cdPtr` in calldata.
          function readBytes17(
              CalldataPointer cdPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes18 at `cdPtr` in calldata.
          function readBytes18(
              CalldataPointer cdPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes19 at `cdPtr` in calldata.
          function readBytes19(
              CalldataPointer cdPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes20 at `cdPtr` in calldata.
          function readBytes20(
              CalldataPointer cdPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes21 at `cdPtr` in calldata.
          function readBytes21(
              CalldataPointer cdPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes22 at `cdPtr` in calldata.
          function readBytes22(
              CalldataPointer cdPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes23 at `cdPtr` in calldata.
          function readBytes23(
              CalldataPointer cdPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes24 at `cdPtr` in calldata.
          function readBytes24(
              CalldataPointer cdPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes25 at `cdPtr` in calldata.
          function readBytes25(
              CalldataPointer cdPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes26 at `cdPtr` in calldata.
          function readBytes26(
              CalldataPointer cdPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes27 at `cdPtr` in calldata.
          function readBytes27(
              CalldataPointer cdPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes28 at `cdPtr` in calldata.
          function readBytes28(
              CalldataPointer cdPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes29 at `cdPtr` in calldata.
          function readBytes29(
              CalldataPointer cdPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes30 at `cdPtr` in calldata.
          function readBytes30(
              CalldataPointer cdPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes31 at `cdPtr` in calldata.
          function readBytes31(
              CalldataPointer cdPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes32 at `cdPtr` in calldata.
          function readBytes32(
              CalldataPointer cdPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint8 at `cdPtr` in calldata.
          function readUint8(
              CalldataPointer cdPtr
          ) internal pure returns (uint8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint16 at `cdPtr` in calldata.
          function readUint16(
              CalldataPointer cdPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint24 at `cdPtr` in calldata.
          function readUint24(
              CalldataPointer cdPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint32 at `cdPtr` in calldata.
          function readUint32(
              CalldataPointer cdPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint40 at `cdPtr` in calldata.
          function readUint40(
              CalldataPointer cdPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint48 at `cdPtr` in calldata.
          function readUint48(
              CalldataPointer cdPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint56 at `cdPtr` in calldata.
          function readUint56(
              CalldataPointer cdPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint64 at `cdPtr` in calldata.
          function readUint64(
              CalldataPointer cdPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint72 at `cdPtr` in calldata.
          function readUint72(
              CalldataPointer cdPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint80 at `cdPtr` in calldata.
          function readUint80(
              CalldataPointer cdPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint88 at `cdPtr` in calldata.
          function readUint88(
              CalldataPointer cdPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint96 at `cdPtr` in calldata.
          function readUint96(
              CalldataPointer cdPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint104 at `cdPtr` in calldata.
          function readUint104(
              CalldataPointer cdPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint112 at `cdPtr` in calldata.
          function readUint112(
              CalldataPointer cdPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint120 at `cdPtr` in calldata.
          function readUint120(
              CalldataPointer cdPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint128 at `cdPtr` in calldata.
          function readUint128(
              CalldataPointer cdPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint136 at `cdPtr` in calldata.
          function readUint136(
              CalldataPointer cdPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint144 at `cdPtr` in calldata.
          function readUint144(
              CalldataPointer cdPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint152 at `cdPtr` in calldata.
          function readUint152(
              CalldataPointer cdPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint160 at `cdPtr` in calldata.
          function readUint160(
              CalldataPointer cdPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint168 at `cdPtr` in calldata.
          function readUint168(
              CalldataPointer cdPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint176 at `cdPtr` in calldata.
          function readUint176(
              CalldataPointer cdPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint184 at `cdPtr` in calldata.
          function readUint184(
              CalldataPointer cdPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint192 at `cdPtr` in calldata.
          function readUint192(
              CalldataPointer cdPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint200 at `cdPtr` in calldata.
          function readUint200(
              CalldataPointer cdPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint208 at `cdPtr` in calldata.
          function readUint208(
              CalldataPointer cdPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint216 at `cdPtr` in calldata.
          function readUint216(
              CalldataPointer cdPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint224 at `cdPtr` in calldata.
          function readUint224(
              CalldataPointer cdPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint232 at `cdPtr` in calldata.
          function readUint232(
              CalldataPointer cdPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint240 at `cdPtr` in calldata.
          function readUint240(
              CalldataPointer cdPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint248 at `cdPtr` in calldata.
          function readUint248(
              CalldataPointer cdPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint256 at `cdPtr` in calldata.
          function readUint256(
              CalldataPointer cdPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int8 at `cdPtr` in calldata.
          function readInt8(
              CalldataPointer cdPtr
          ) internal pure returns (int8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int16 at `cdPtr` in calldata.
          function readInt16(
              CalldataPointer cdPtr
          ) internal pure returns (int16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int24 at `cdPtr` in calldata.
          function readInt24(
              CalldataPointer cdPtr
          ) internal pure returns (int24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int32 at `cdPtr` in calldata.
          function readInt32(
              CalldataPointer cdPtr
          ) internal pure returns (int32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int40 at `cdPtr` in calldata.
          function readInt40(
              CalldataPointer cdPtr
          ) internal pure returns (int40 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int48 at `cdPtr` in calldata.
          function readInt48(
              CalldataPointer cdPtr
          ) internal pure returns (int48 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int56 at `cdPtr` in calldata.
          function readInt56(
              CalldataPointer cdPtr
          ) internal pure returns (int56 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int64 at `cdPtr` in calldata.
          function readInt64(
              CalldataPointer cdPtr
          ) internal pure returns (int64 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int72 at `cdPtr` in calldata.
          function readInt72(
              CalldataPointer cdPtr
          ) internal pure returns (int72 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int80 at `cdPtr` in calldata.
          function readInt80(
              CalldataPointer cdPtr
          ) internal pure returns (int80 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int88 at `cdPtr` in calldata.
          function readInt88(
              CalldataPointer cdPtr
          ) internal pure returns (int88 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int96 at `cdPtr` in calldata.
          function readInt96(
              CalldataPointer cdPtr
          ) internal pure returns (int96 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int104 at `cdPtr` in calldata.
          function readInt104(
              CalldataPointer cdPtr
          ) internal pure returns (int104 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int112 at `cdPtr` in calldata.
          function readInt112(
              CalldataPointer cdPtr
          ) internal pure returns (int112 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int120 at `cdPtr` in calldata.
          function readInt120(
              CalldataPointer cdPtr
          ) internal pure returns (int120 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int128 at `cdPtr` in calldata.
          function readInt128(
              CalldataPointer cdPtr
          ) internal pure returns (int128 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int136 at `cdPtr` in calldata.
          function readInt136(
              CalldataPointer cdPtr
          ) internal pure returns (int136 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int144 at `cdPtr` in calldata.
          function readInt144(
              CalldataPointer cdPtr
          ) internal pure returns (int144 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int152 at `cdPtr` in calldata.
          function readInt152(
              CalldataPointer cdPtr
          ) internal pure returns (int152 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int160 at `cdPtr` in calldata.
          function readInt160(
              CalldataPointer cdPtr
          ) internal pure returns (int160 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int168 at `cdPtr` in calldata.
          function readInt168(
              CalldataPointer cdPtr
          ) internal pure returns (int168 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int176 at `cdPtr` in calldata.
          function readInt176(
              CalldataPointer cdPtr
          ) internal pure returns (int176 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int184 at `cdPtr` in calldata.
          function readInt184(
              CalldataPointer cdPtr
          ) internal pure returns (int184 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int192 at `cdPtr` in calldata.
          function readInt192(
              CalldataPointer cdPtr
          ) internal pure returns (int192 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int200 at `cdPtr` in calldata.
          function readInt200(
              CalldataPointer cdPtr
          ) internal pure returns (int200 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int208 at `cdPtr` in calldata.
          function readInt208(
              CalldataPointer cdPtr
          ) internal pure returns (int208 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int216 at `cdPtr` in calldata.
          function readInt216(
              CalldataPointer cdPtr
          ) internal pure returns (int216 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int224 at `cdPtr` in calldata.
          function readInt224(
              CalldataPointer cdPtr
          ) internal pure returns (int224 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int232 at `cdPtr` in calldata.
          function readInt232(
              CalldataPointer cdPtr
          ) internal pure returns (int232 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int240 at `cdPtr` in calldata.
          function readInt240(
              CalldataPointer cdPtr
          ) internal pure returns (int240 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int248 at `cdPtr` in calldata.
          function readInt248(
              CalldataPointer cdPtr
          ) internal pure returns (int248 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int256 at `cdPtr` in calldata.
          function readInt256(
              CalldataPointer cdPtr
          ) internal pure returns (int256 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
      }
      library ReturndataReaders {
          /// @dev Reads value at `rdPtr` & applies a mask to return only last 4 bytes
          function readMaskedUint256(
              ReturndataPointer rdPtr
          ) internal pure returns (uint256 value) {
              value = rdPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `rdPtr` in returndata.
          function readBool(
              ReturndataPointer rdPtr
          ) internal pure returns (bool value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the address at `rdPtr` in returndata.
          function readAddress(
              ReturndataPointer rdPtr
          ) internal pure returns (address value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes1 at `rdPtr` in returndata.
          function readBytes1(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes2 at `rdPtr` in returndata.
          function readBytes2(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes3 at `rdPtr` in returndata.
          function readBytes3(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes4 at `rdPtr` in returndata.
          function readBytes4(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes5 at `rdPtr` in returndata.
          function readBytes5(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes6 at `rdPtr` in returndata.
          function readBytes6(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes7 at `rdPtr` in returndata.
          function readBytes7(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes8 at `rdPtr` in returndata.
          function readBytes8(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes9 at `rdPtr` in returndata.
          function readBytes9(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes10 at `rdPtr` in returndata.
          function readBytes10(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes11 at `rdPtr` in returndata.
          function readBytes11(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes12 at `rdPtr` in returndata.
          function readBytes12(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes13 at `rdPtr` in returndata.
          function readBytes13(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes14 at `rdPtr` in returndata.
          function readBytes14(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes15 at `rdPtr` in returndata.
          function readBytes15(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes16 at `rdPtr` in returndata.
          function readBytes16(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes17 at `rdPtr` in returndata.
          function readBytes17(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes18 at `rdPtr` in returndata.
          function readBytes18(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes19 at `rdPtr` in returndata.
          function readBytes19(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes20 at `rdPtr` in returndata.
          function readBytes20(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes21 at `rdPtr` in returndata.
          function readBytes21(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes22 at `rdPtr` in returndata.
          function readBytes22(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes23 at `rdPtr` in returndata.
          function readBytes23(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes24 at `rdPtr` in returndata.
          function readBytes24(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes25 at `rdPtr` in returndata.
          function readBytes25(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes26 at `rdPtr` in returndata.
          function readBytes26(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes27 at `rdPtr` in returndata.
          function readBytes27(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes28 at `rdPtr` in returndata.
          function readBytes28(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes29 at `rdPtr` in returndata.
          function readBytes29(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes30 at `rdPtr` in returndata.
          function readBytes30(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes31 at `rdPtr` in returndata.
          function readBytes31(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes32 at `rdPtr` in returndata.
          function readBytes32(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint8 at `rdPtr` in returndata.
          function readUint8(
              ReturndataPointer rdPtr
          ) internal pure returns (uint8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint16 at `rdPtr` in returndata.
          function readUint16(
              ReturndataPointer rdPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint24 at `rdPtr` in returndata.
          function readUint24(
              ReturndataPointer rdPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint32 at `rdPtr` in returndata.
          function readUint32(
              ReturndataPointer rdPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint40 at `rdPtr` in returndata.
          function readUint40(
              ReturndataPointer rdPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint48 at `rdPtr` in returndata.
          function readUint48(
              ReturndataPointer rdPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint56 at `rdPtr` in returndata.
          function readUint56(
              ReturndataPointer rdPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint64 at `rdPtr` in returndata.
          function readUint64(
              ReturndataPointer rdPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint72 at `rdPtr` in returndata.
          function readUint72(
              ReturndataPointer rdPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint80 at `rdPtr` in returndata.
          function readUint80(
              ReturndataPointer rdPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint88 at `rdPtr` in returndata.
          function readUint88(
              ReturndataPointer rdPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint96 at `rdPtr` in returndata.
          function readUint96(
              ReturndataPointer rdPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint104 at `rdPtr` in returndata.
          function readUint104(
              ReturndataPointer rdPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint112 at `rdPtr` in returndata.
          function readUint112(
              ReturndataPointer rdPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint120 at `rdPtr` in returndata.
          function readUint120(
              ReturndataPointer rdPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint128 at `rdPtr` in returndata.
          function readUint128(
              ReturndataPointer rdPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint136 at `rdPtr` in returndata.
          function readUint136(
              ReturndataPointer rdPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint144 at `rdPtr` in returndata.
          function readUint144(
              ReturndataPointer rdPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint152 at `rdPtr` in returndata.
          function readUint152(
              ReturndataPointer rdPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint160 at `rdPtr` in returndata.
          function readUint160(
              ReturndataPointer rdPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint168 at `rdPtr` in returndata.
          function readUint168(
              ReturndataPointer rdPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint176 at `rdPtr` in returndata.
          function readUint176(
              ReturndataPointer rdPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint184 at `rdPtr` in returndata.
          function readUint184(
              ReturndataPointer rdPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint192 at `rdPtr` in returndata.
          function readUint192(
              ReturndataPointer rdPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint200 at `rdPtr` in returndata.
          function readUint200(
              ReturndataPointer rdPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint208 at `rdPtr` in returndata.
          function readUint208(
              ReturndataPointer rdPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint216 at `rdPtr` in returndata.
          function readUint216(
              ReturndataPointer rdPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint224 at `rdPtr` in returndata.
          function readUint224(
              ReturndataPointer rdPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint232 at `rdPtr` in returndata.
          function readUint232(
              ReturndataPointer rdPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint240 at `rdPtr` in returndata.
          function readUint240(
              ReturndataPointer rdPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint248 at `rdPtr` in returndata.
          function readUint248(
              ReturndataPointer rdPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint256 at `rdPtr` in returndata.
          function readUint256(
              ReturndataPointer rdPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int8 at `rdPtr` in returndata.
          function readInt8(
              ReturndataPointer rdPtr
          ) internal pure returns (int8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int16 at `rdPtr` in returndata.
          function readInt16(
              ReturndataPointer rdPtr
          ) internal pure returns (int16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int24 at `rdPtr` in returndata.
          function readInt24(
              ReturndataPointer rdPtr
          ) internal pure returns (int24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int32 at `rdPtr` in returndata.
          function readInt32(
              ReturndataPointer rdPtr
          ) internal pure returns (int32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int40 at `rdPtr` in returndata.
          function readInt40(
              ReturndataPointer rdPtr
          ) internal pure returns (int40 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int48 at `rdPtr` in returndata.
          function readInt48(
              ReturndataPointer rdPtr
          ) internal pure returns (int48 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int56 at `rdPtr` in returndata.
          function readInt56(
              ReturndataPointer rdPtr
          ) internal pure returns (int56 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int64 at `rdPtr` in returndata.
          function readInt64(
              ReturndataPointer rdPtr
          ) internal pure returns (int64 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int72 at `rdPtr` in returndata.
          function readInt72(
              ReturndataPointer rdPtr
          ) internal pure returns (int72 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int80 at `rdPtr` in returndata.
          function readInt80(
              ReturndataPointer rdPtr
          ) internal pure returns (int80 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int88 at `rdPtr` in returndata.
          function readInt88(
              ReturndataPointer rdPtr
          ) internal pure returns (int88 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int96 at `rdPtr` in returndata.
          function readInt96(
              ReturndataPointer rdPtr
          ) internal pure returns (int96 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int104 at `rdPtr` in returndata.
          function readInt104(
              ReturndataPointer rdPtr
          ) internal pure returns (int104 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int112 at `rdPtr` in returndata.
          function readInt112(
              ReturndataPointer rdPtr
          ) internal pure returns (int112 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int120 at `rdPtr` in returndata.
          function readInt120(
              ReturndataPointer rdPtr
          ) internal pure returns (int120 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int128 at `rdPtr` in returndata.
          function readInt128(
              ReturndataPointer rdPtr
          ) internal pure returns (int128 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int136 at `rdPtr` in returndata.
          function readInt136(
              ReturndataPointer rdPtr
          ) internal pure returns (int136 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int144 at `rdPtr` in returndata.
          function readInt144(
              ReturndataPointer rdPtr
          ) internal pure returns (int144 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int152 at `rdPtr` in returndata.
          function readInt152(
              ReturndataPointer rdPtr
          ) internal pure returns (int152 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int160 at `rdPtr` in returndata.
          function readInt160(
              ReturndataPointer rdPtr
          ) internal pure returns (int160 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int168 at `rdPtr` in returndata.
          function readInt168(
              ReturndataPointer rdPtr
          ) internal pure returns (int168 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int176 at `rdPtr` in returndata.
          function readInt176(
              ReturndataPointer rdPtr
          ) internal pure returns (int176 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int184 at `rdPtr` in returndata.
          function readInt184(
              ReturndataPointer rdPtr
          ) internal pure returns (int184 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int192 at `rdPtr` in returndata.
          function readInt192(
              ReturndataPointer rdPtr
          ) internal pure returns (int192 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int200 at `rdPtr` in returndata.
          function readInt200(
              ReturndataPointer rdPtr
          ) internal pure returns (int200 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int208 at `rdPtr` in returndata.
          function readInt208(
              ReturndataPointer rdPtr
          ) internal pure returns (int208 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int216 at `rdPtr` in returndata.
          function readInt216(
              ReturndataPointer rdPtr
          ) internal pure returns (int216 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int224 at `rdPtr` in returndata.
          function readInt224(
              ReturndataPointer rdPtr
          ) internal pure returns (int224 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int232 at `rdPtr` in returndata.
          function readInt232(
              ReturndataPointer rdPtr
          ) internal pure returns (int232 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int240 at `rdPtr` in returndata.
          function readInt240(
              ReturndataPointer rdPtr
          ) internal pure returns (int240 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int248 at `rdPtr` in returndata.
          function readInt248(
              ReturndataPointer rdPtr
          ) internal pure returns (int248 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int256 at `rdPtr` in returndata.
          function readInt256(
              ReturndataPointer rdPtr
          ) internal pure returns (int256 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
      }
      library MemoryReaders {
          /// @dev Reads the memory pointer at `mPtr` in memory.
          function readMemoryPointer(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads value at `mPtr` & applies a mask to return only last 4 bytes
          function readMaskedUint256(
              MemoryPointer mPtr
          ) internal pure returns (uint256 value) {
              value = mPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `mPtr` in memory.
          function readBool(MemoryPointer mPtr) internal pure returns (bool value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the address at `mPtr` in memory.
          function readAddress(
              MemoryPointer mPtr
          ) internal pure returns (address value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes1 at `mPtr` in memory.
          function readBytes1(
              MemoryPointer mPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes2 at `mPtr` in memory.
          function readBytes2(
              MemoryPointer mPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes3 at `mPtr` in memory.
          function readBytes3(
              MemoryPointer mPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes4 at `mPtr` in memory.
          function readBytes4(
              MemoryPointer mPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes5 at `mPtr` in memory.
          function readBytes5(
              MemoryPointer mPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes6 at `mPtr` in memory.
          function readBytes6(
              MemoryPointer mPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes7 at `mPtr` in memory.
          function readBytes7(
              MemoryPointer mPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes8 at `mPtr` in memory.
          function readBytes8(
              MemoryPointer mPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes9 at `mPtr` in memory.
          function readBytes9(
              MemoryPointer mPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes10 at `mPtr` in memory.
          function readBytes10(
              MemoryPointer mPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes11 at `mPtr` in memory.
          function readBytes11(
              MemoryPointer mPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes12 at `mPtr` in memory.
          function readBytes12(
              MemoryPointer mPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes13 at `mPtr` in memory.
          function readBytes13(
              MemoryPointer mPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes14 at `mPtr` in memory.
          function readBytes14(
              MemoryPointer mPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes15 at `mPtr` in memory.
          function readBytes15(
              MemoryPointer mPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes16 at `mPtr` in memory.
          function readBytes16(
              MemoryPointer mPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes17 at `mPtr` in memory.
          function readBytes17(
              MemoryPointer mPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes18 at `mPtr` in memory.
          function readBytes18(
              MemoryPointer mPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes19 at `mPtr` in memory.
          function readBytes19(
              MemoryPointer mPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes20 at `mPtr` in memory.
          function readBytes20(
              MemoryPointer mPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes21 at `mPtr` in memory.
          function readBytes21(
              MemoryPointer mPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes22 at `mPtr` in memory.
          function readBytes22(
              MemoryPointer mPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes23 at `mPtr` in memory.
          function readBytes23(
              MemoryPointer mPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes24 at `mPtr` in memory.
          function readBytes24(
              MemoryPointer mPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes25 at `mPtr` in memory.
          function readBytes25(
              MemoryPointer mPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes26 at `mPtr` in memory.
          function readBytes26(
              MemoryPointer mPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes27 at `mPtr` in memory.
          function readBytes27(
              MemoryPointer mPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes28 at `mPtr` in memory.
          function readBytes28(
              MemoryPointer mPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes29 at `mPtr` in memory.
          function readBytes29(
              MemoryPointer mPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes30 at `mPtr` in memory.
          function readBytes30(
              MemoryPointer mPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes31 at `mPtr` in memory.
          function readBytes31(
              MemoryPointer mPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes32 at `mPtr` in memory.
          function readBytes32(
              MemoryPointer mPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint8 at `mPtr` in memory.
          function readUint8(MemoryPointer mPtr) internal pure returns (uint8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint16 at `mPtr` in memory.
          function readUint16(
              MemoryPointer mPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint24 at `mPtr` in memory.
          function readUint24(
              MemoryPointer mPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint32 at `mPtr` in memory.
          function readUint32(
              MemoryPointer mPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint40 at `mPtr` in memory.
          function readUint40(
              MemoryPointer mPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint48 at `mPtr` in memory.
          function readUint48(
              MemoryPointer mPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint56 at `mPtr` in memory.
          function readUint56(
              MemoryPointer mPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint64 at `mPtr` in memory.
          function readUint64(
              MemoryPointer mPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint72 at `mPtr` in memory.
          function readUint72(
              MemoryPointer mPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint80 at `mPtr` in memory.
          function readUint80(
              MemoryPointer mPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint88 at `mPtr` in memory.
          function readUint88(
              MemoryPointer mPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint96 at `mPtr` in memory.
          function readUint96(
              MemoryPointer mPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint104 at `mPtr` in memory.
          function readUint104(
              MemoryPointer mPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint112 at `mPtr` in memory.
          function readUint112(
              MemoryPointer mPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint120 at `mPtr` in memory.
          function readUint120(
              MemoryPointer mPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint128 at `mPtr` in memory.
          function readUint128(
              MemoryPointer mPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint136 at `mPtr` in memory.
          function readUint136(
              MemoryPointer mPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint144 at `mPtr` in memory.
          function readUint144(
              MemoryPointer mPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint152 at `mPtr` in memory.
          function readUint152(
              MemoryPointer mPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint160 at `mPtr` in memory.
          function readUint160(
              MemoryPointer mPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint168 at `mPtr` in memory.
          function readUint168(
              MemoryPointer mPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint176 at `mPtr` in memory.
          function readUint176(
              MemoryPointer mPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint184 at `mPtr` in memory.
          function readUint184(
              MemoryPointer mPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint192 at `mPtr` in memory.
          function readUint192(
              MemoryPointer mPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint200 at `mPtr` in memory.
          function readUint200(
              MemoryPointer mPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint208 at `mPtr` in memory.
          function readUint208(
              MemoryPointer mPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint216 at `mPtr` in memory.
          function readUint216(
              MemoryPointer mPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint224 at `mPtr` in memory.
          function readUint224(
              MemoryPointer mPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint232 at `mPtr` in memory.
          function readUint232(
              MemoryPointer mPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint240 at `mPtr` in memory.
          function readUint240(
              MemoryPointer mPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint248 at `mPtr` in memory.
          function readUint248(
              MemoryPointer mPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint256 at `mPtr` in memory.
          function readUint256(
              MemoryPointer mPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int8 at `mPtr` in memory.
          function readInt8(MemoryPointer mPtr) internal pure returns (int8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int16 at `mPtr` in memory.
          function readInt16(MemoryPointer mPtr) internal pure returns (int16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int24 at `mPtr` in memory.
          function readInt24(MemoryPointer mPtr) internal pure returns (int24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int32 at `mPtr` in memory.
          function readInt32(MemoryPointer mPtr) internal pure returns (int32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int40 at `mPtr` in memory.
          function readInt40(MemoryPointer mPtr) internal pure returns (int40 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int48 at `mPtr` in memory.
          function readInt48(MemoryPointer mPtr) internal pure returns (int48 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int56 at `mPtr` in memory.
          function readInt56(MemoryPointer mPtr) internal pure returns (int56 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int64 at `mPtr` in memory.
          function readInt64(MemoryPointer mPtr) internal pure returns (int64 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int72 at `mPtr` in memory.
          function readInt72(MemoryPointer mPtr) internal pure returns (int72 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int80 at `mPtr` in memory.
          function readInt80(MemoryPointer mPtr) internal pure returns (int80 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int88 at `mPtr` in memory.
          function readInt88(MemoryPointer mPtr) internal pure returns (int88 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int96 at `mPtr` in memory.
          function readInt96(MemoryPointer mPtr) internal pure returns (int96 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int104 at `mPtr` in memory.
          function readInt104(
              MemoryPointer mPtr
          ) internal pure returns (int104 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int112 at `mPtr` in memory.
          function readInt112(
              MemoryPointer mPtr
          ) internal pure returns (int112 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int120 at `mPtr` in memory.
          function readInt120(
              MemoryPointer mPtr
          ) internal pure returns (int120 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int128 at `mPtr` in memory.
          function readInt128(
              MemoryPointer mPtr
          ) internal pure returns (int128 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int136 at `mPtr` in memory.
          function readInt136(
              MemoryPointer mPtr
          ) internal pure returns (int136 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int144 at `mPtr` in memory.
          function readInt144(
              MemoryPointer mPtr
          ) internal pure returns (int144 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int152 at `mPtr` in memory.
          function readInt152(
              MemoryPointer mPtr
          ) internal pure returns (int152 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int160 at `mPtr` in memory.
          function readInt160(
              MemoryPointer mPtr
          ) internal pure returns (int160 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int168 at `mPtr` in memory.
          function readInt168(
              MemoryPointer mPtr
          ) internal pure returns (int168 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int176 at `mPtr` in memory.
          function readInt176(
              MemoryPointer mPtr
          ) internal pure returns (int176 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int184 at `mPtr` in memory.
          function readInt184(
              MemoryPointer mPtr
          ) internal pure returns (int184 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int192 at `mPtr` in memory.
          function readInt192(
              MemoryPointer mPtr
          ) internal pure returns (int192 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int200 at `mPtr` in memory.
          function readInt200(
              MemoryPointer mPtr
          ) internal pure returns (int200 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int208 at `mPtr` in memory.
          function readInt208(
              MemoryPointer mPtr
          ) internal pure returns (int208 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int216 at `mPtr` in memory.
          function readInt216(
              MemoryPointer mPtr
          ) internal pure returns (int216 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int224 at `mPtr` in memory.
          function readInt224(
              MemoryPointer mPtr
          ) internal pure returns (int224 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int232 at `mPtr` in memory.
          function readInt232(
              MemoryPointer mPtr
          ) internal pure returns (int232 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int240 at `mPtr` in memory.
          function readInt240(
              MemoryPointer mPtr
          ) internal pure returns (int240 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int248 at `mPtr` in memory.
          function readInt248(
              MemoryPointer mPtr
          ) internal pure returns (int248 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int256 at `mPtr` in memory.
          function readInt256(
              MemoryPointer mPtr
          ) internal pure returns (int256 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
      }
      library MemoryWriters {
          /// @dev Writes `valuePtr` to memory at `mPtr`.
          function write(MemoryPointer mPtr, MemoryPointer valuePtr) internal pure {
              assembly {
                  mstore(mPtr, valuePtr)
              }
          }
          /// @dev Writes a boolean `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, bool value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes an address `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, address value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes a bytes32 `value` to `mPtr` in memory.
          /// Separate name to disambiguate literal write parameters.
          function writeBytes32(MemoryPointer mPtr, bytes32 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes a uint256 `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, uint256 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes an int256 `value` to `mPtr` in memory.
          /// Separate name to disambiguate literal write parameters.
          function writeInt(MemoryPointer mPtr, int256 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.7;
      /**
       * @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`.
           *
           * 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/Address.sol)
      pragma solidity ^0.8.19;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @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 if target is a contract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(target.code.length > 0, "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
      pragma solidity ^0.8.19;
      import { AllowListData, CreatorPayout } from "./SeaDropStructs.sol";
      /**
       * @notice A struct defining public drop data.
       *         Designed to fit efficiently in two storage slots.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token address. Null for
       *                                 native token.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed. (The limit for this field is
       *                                 2^16 - 1)
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct PublicDrop {
          uint80 startPrice; // 80/512 bits
          uint80 endPrice; // 160/512 bits
          uint40 startTime; // 200/512 bits
          uint40 endTime; // 240/512 bits
          address paymentToken; // 400/512 bits
          uint16 maxTotalMintableByWallet; // 416/512 bits
          uint16 feeBps; // 432/512 bits
          bool restrictFeeRecipients; // 440/512 bits
      }
      /**
       * @notice A struct defining mint params for an allow list.
       *         An allow list leaf will be composed of `msg.sender` and
       *         the following params.
       *
       *         Note: Since feeBps is encoded in the leaf, backend should ensure
       *         that feeBps is acceptable before generating a proof.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token for the mint. Null for
       *                                 native token.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed.
       * @param maxTokenSupplyForStage   The limit of token supply this stage can
       *                                 mint within.
       * @param dropStageIndex           The drop stage index to emit with the event
       *                                 for analytical purposes. This should be
       *                                 non-zero since the public mint emits with
       *                                 index zero.
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct MintParams {
          uint256 startPrice;
          uint256 endPrice;
          uint256 startTime;
          uint256 endTime;
          address paymentToken;
          uint256 maxTotalMintableByWallet;
          uint256 maxTokenSupplyForStage;
          uint256 dropStageIndex; // non-zero
          uint256 feeBps;
          bool restrictFeeRecipients;
      }
      /**
       * @dev Struct containing internal SeaDrop implementation logic
       *      mint details to avoid stack too deep.
       *
       * @param feeRecipient The fee recipient.
       * @param payer        The payer of the mint.
       * @param minter       The mint recipient.
       * @param quantity     The number of tokens to mint.
       * @param withEffects  Whether to apply state changes of the mint.
       */
      struct MintDetails {
          address feeRecipient;
          address payer;
          address minter;
          uint256 quantity;
          bool withEffects;
      }
      /**
       * @notice A struct to configure multiple contract options in one transaction.
       */
      struct MultiConfigureStruct {
          uint256 maxSupply;
          string baseURI;
          string contractURI;
          PublicDrop publicDrop;
          string dropURI;
          AllowListData allowListData;
          CreatorPayout[] creatorPayouts;
          bytes32 provenanceHash;
          address[] allowedFeeRecipients;
          address[] disallowedFeeRecipients;
          address[] allowedPayers;
          address[] disallowedPayers;
          // Server-signed
          address[] allowedSigners;
          address[] disallowedSigners;
          // ERC-2981
          address royaltyReceiver;
          uint96 royaltyBps;
          // Mint
          address mintRecipient;
          uint256 mintQuantity;
      }
      

      File 2 of 3: ERC1155SeaDropCloneable
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ERC1155SeaDropContractOffererCloneable
      } from "./ERC1155SeaDropContractOffererCloneable.sol";
      /**
       * @title  ERC1155SeaDropCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @custom:contributor Limit Break (@limitbreak)
       * @notice A cloneable ERC1155 token contract that can mint as a
       *         Seaport contract offerer.
       *         Implements Limit Break's Creator Token Standards transfer
       *         validation for royalty enforcement.
       */
      contract ERC1155SeaDropCloneable is ERC1155SeaDropContractOffererCloneable {
          /**
           * @notice Initialize the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          implementation code. Also contains SeaDrop
           *                          implementation code.
           * @param allowedSeaport    The address of the Seaport contract allowed to
           *                          interact.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function initialize(
              address allowedConfigurer,
              address allowedSeaport,
              string memory name_,
              string memory symbol_,
              address initialOwner
          ) public initializer {
              // Initialize ownership.
              _initializeOwner(initialOwner);
              // Initialize ERC1155SeaDropContractOffererCloneable.
              __ERC1155SeaDropContractOffererCloneable_init(
                  allowedConfigurer,
                  allowedSeaport,
                  name_,
                  symbol_
              );
          }
          /**
           * @notice Burns a token, restricted to the owner or approved operator,
           *         and must have sufficient balance.
           *
           * @param from   The address to burn from.
           * @param id     The token id to burn.
           * @param amount The amount to burn.
           */
          function burn(address from, uint256 id, uint256 amount) external {
              // Burn the token.
              _burn(msg.sender, from, id, amount);
          }
          /**
           * @notice Burns a batch of tokens, restricted to the owner or
           *         approved operator, and must have sufficient balance.
           *
           * @param from    The address to burn from.
           * @param ids     The token ids to burn.
           * @param amounts The amounts to burn per token id.
           */
          function batchBurn(
              address from,
              uint256[] calldata ids,
              uint256[] calldata amounts
          ) external {
              // Burn the tokens.
              _batchBurn(msg.sender, from, ids, amounts);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { IERC1155SeaDrop } from "../interfaces/IERC1155SeaDrop.sol";
      import { ISeaDropToken } from "../interfaces/ISeaDropToken.sol";
      import {
          ERC1155ContractMetadataCloneable
      } from "./ERC1155ContractMetadataCloneable.sol";
      import {
          ERC1155SeaDropContractOffererStorage
      } from "../lib/ERC1155SeaDropContractOffererStorage.sol";
      import {
          ERC1155SeaDropErrorsAndEvents
      } from "../lib/ERC1155SeaDropErrorsAndEvents.sol";
      import { PublicDrop } from "../lib//ERC1155SeaDropStructs.sol";
      import { AllowListData } from "../lib/SeaDropStructs.sol";
      import {
          ERC1155ConduitPreapproved
      } from "../lib/ERC1155ConduitPreapproved.sol";
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      import { SpentItem } from "seaport-types/src/lib/ConsiderationStructs.sol";
      import {
          ContractOffererInterface
      } from "seaport-types/src/interfaces/ContractOffererInterface.sol";
      import {
          IERC165
      } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
      /**
       * @title  ERC1155SeaDropContractOffererCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @notice A cloneable ERC1155 token contract that can mint as a
       *         Seaport contract offerer.
       */
      contract ERC1155SeaDropContractOffererCloneable is
          ERC1155ContractMetadataCloneable,
          ERC1155SeaDropErrorsAndEvents
      {
          using ERC1155SeaDropContractOffererStorage for ERC1155SeaDropContractOffererStorage.Layout;
          /**
           * @notice Initialize the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          configure parameters. Also contains SeaDrop
           *                          implementation code.
           * @param allowedSeaport    The address of the Seaport contract allowed to
           *                          interact.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function __ERC1155SeaDropContractOffererCloneable_init(
              address allowedConfigurer,
              address allowedSeaport,
              string memory name_,
              string memory symbol_
          ) internal onlyInitializing {
              // Set the allowed Seaport to interact with this contract.
              if (allowedSeaport == address(0)) {
                  revert AllowedSeaportCannotBeZeroAddress();
              }
              ERC1155SeaDropContractOffererStorage.layout()._allowedSeaport[
                  allowedSeaport
              ] = true;
              // Set the allowed Seaport enumeration.
              address[] memory enumeratedAllowedSeaport = new address[](1);
              enumeratedAllowedSeaport[0] = allowedSeaport;
              ERC1155SeaDropContractOffererStorage
                  .layout()
                  ._enumeratedAllowedSeaport = enumeratedAllowedSeaport;
              // Emit an event noting the contract deployment.
              emit SeaDropTokenDeployed(SEADROP_TOKEN_TYPE.ERC1155_CLONE);
              // Initialize ERC1155ContractMetadataCloneable.
              __ERC1155ContractMetadataCloneable_init(
                  allowedConfigurer,
                  name_,
                  symbol_
              );
          }
          /**
           * @notice The fallback function is used as a dispatcher for SeaDrop
           *         methods.
           */
          fallback(bytes calldata) external returns (bytes memory output) {
              // Get the function selector.
              bytes4 selector = msg.sig;
              // Get the rest of the msg data after the selector.
              bytes calldata data = msg.data[4:];
              // Determine if we should forward the call to the implementation
              // contract with SeaDrop logic.
              bool callSeaDropImplementation = selector ==
                  ISeaDropToken.updateAllowedSeaport.selector ||
                  selector == ISeaDropToken.updateDropURI.selector ||
                  selector == ISeaDropToken.updateAllowList.selector ||
                  selector == ISeaDropToken.updateCreatorPayouts.selector ||
                  selector == ISeaDropToken.updatePayer.selector ||
                  selector == ISeaDropToken.updateAllowedFeeRecipient.selector ||
                  selector == ISeaDropToken.updateSigner.selector ||
                  selector == IERC1155SeaDrop.updatePublicDrop.selector ||
                  selector == ContractOffererInterface.previewOrder.selector ||
                  selector == ContractOffererInterface.generateOrder.selector ||
                  selector == ContractOffererInterface.getSeaportMetadata.selector ||
                  selector == IERC1155SeaDrop.getPublicDrop.selector ||
                  selector == IERC1155SeaDrop.getPublicDropIndexes.selector ||
                  selector == ISeaDropToken.getAllowedSeaport.selector ||
                  selector == ISeaDropToken.getCreatorPayouts.selector ||
                  selector == ISeaDropToken.getAllowListMerkleRoot.selector ||
                  selector == ISeaDropToken.getAllowedFeeRecipients.selector ||
                  selector == ISeaDropToken.getSigners.selector ||
                  selector == ISeaDropToken.getDigestIsUsed.selector ||
                  selector == ISeaDropToken.getPayers.selector;
              // Determine if we should require only the owner or configurer calling.
              bool requireOnlyOwnerOrConfigurer = selector ==
                  ISeaDropToken.updateAllowedSeaport.selector ||
                  selector == ISeaDropToken.updateDropURI.selector ||
                  selector == ISeaDropToken.updateAllowList.selector ||
                  selector == ISeaDropToken.updateCreatorPayouts.selector ||
                  selector == ISeaDropToken.updatePayer.selector ||
                  selector == ISeaDropToken.updateAllowedFeeRecipient.selector ||
                  selector == IERC1155SeaDrop.updatePublicDrop.selector;
              if (callSeaDropImplementation) {
                  // For update calls, ensure the sender is only the owner
                  // or configurer contract.
                  if (requireOnlyOwnerOrConfigurer) {
                      _onlyOwnerOrConfigurer();
                  } else if (selector == ISeaDropToken.updateSigner.selector) {
                      // For updateSigner, a signer can disallow themselves.
                      // Get the signer parameter.
                      address signer = address(bytes20(data[12:32]));
                      // If the signer is not allowed, ensure sender is only owner
                      // or configurer.
                      if (
                          msg.sender != signer ||
                          (msg.sender == signer &&
                              !ERC1155SeaDropContractOffererStorage
                                  .layout()
                                  ._allowedSigners[signer])
                      ) {
                          _onlyOwnerOrConfigurer();
                      }
                  }
                  // Forward the call to the implementation contract.
                  (bool success, bytes memory returnedData) = _CONFIGURER
                      .delegatecall(msg.data);
                  // Require that the call was successful.
                  if (!success) {
                      // Bubble up the revert reason.
                      assembly {
                          revert(add(32, returnedData), mload(returnedData))
                      }
                  }
                  // If the call was to generateOrder, mint the tokens.
                  if (selector == ContractOffererInterface.generateOrder.selector) {
                      _mintOrder(data);
                  }
                  // Return the data from the delegate call.
                  return returnedData;
              } else if (selector == IERC1155SeaDrop.getMintStats.selector) {
                  // Get the minter and token id.
                  (address minter, uint256 tokenId) = abi.decode(
                      data,
                      (address, uint256)
                  );
                  // Get the mint stats.
                  (
                      uint256 minterNumMinted,
                      uint256 minterNumMintedForTokenId,
                      uint256 totalMintedForTokenId,
                      uint256 maxSupply
                  ) = _getMintStats(minter, tokenId);
                  // Encode the return data.
                  return
                      abi.encode(
                          minterNumMinted,
                          minterNumMintedForTokenId,
                          totalMintedForTokenId,
                          maxSupply
                      );
              } else if (selector == ContractOffererInterface.ratifyOrder.selector) {
                  // This function is a no-op, nothing additional needs to happen here.
                  // Utilize assembly to efficiently return the ratifyOrder magic value.
                  assembly {
                      mstore(0, 0xf4dd92ce)
                      return(0x1c, 32)
                  }
              } else if (selector == ISeaDropToken.configurer.selector) {
                  // Return the configurer contract.
                  return abi.encode(_CONFIGURER);
              } else if (selector == IERC1155SeaDrop.multiConfigureMint.selector) {
                  // Ensure only the owner or configurer can call this function.
                  _onlyOwnerOrConfigurer();
                  // Mint the tokens.
                  _multiConfigureMint(data);
              } else {
                  // Revert if the function selector is not supported.
                  revert UnsupportedFunctionSelector(selector);
              }
          }
          /**
           * @notice Returns a set of mint stats for the address.
           *         This assists in enforcing maxSupply, maxTotalMintableByWallet,
           *         and maxTokenSupplyForStage checks.
           *
           * @dev    NOTE: Implementing contracts should always update these numbers
           *         before transferring any tokens with _safeMint() to mitigate
           *         consequences of malicious onERC1155Received() hooks.
           *
           * @param minter  The minter address.
           * @param tokenId The token id to return the stats for.
           */
          function _getMintStats(
              address minter,
              uint256 tokenId
          )
              internal
              view
              returns (
                  uint256 minterNumMinted,
                  uint256 minterNumMintedForTokenId,
                  uint256 totalMintedForTokenId,
                  uint256 maxSupply
              )
          {
              // Put the token supply on the stack.
              TokenSupply storage tokenSupply = _tokenSupply[tokenId];
              // Assign the return values.
              totalMintedForTokenId = tokenSupply.totalMinted;
              maxSupply = tokenSupply.maxSupply;
              minterNumMinted = _totalMintedByUser[minter];
              minterNumMintedForTokenId = _totalMintedByUserPerToken[minter][tokenId];
          }
          /**
           * @dev Handle ERC-1155 safeTransferFrom. If "from" is this contract,
           *      the sender can only be Seaport or the conduit.
           *
           * @param from   The address to transfer from.
           * @param to     The address to transfer to.
           * @param id     The token id to transfer.
           * @param amount The amount of tokens to transfer.
           * @param data   The data to pass to the onERC1155Received hook.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual override {
              if (from == address(this)) {
                  // Only Seaport or the conduit can use this function
                  // when "from" is this contract.
                  if (
                      msg.sender != _CONDUIT &&
                      !ERC1155SeaDropContractOffererStorage.layout()._allowedSeaport[
                          msg.sender
                      ]
                  ) {
                      revert InvalidCallerOnlyAllowedSeaport(msg.sender);
                  }
                  return;
              }
              ERC1155._safeTransfer(_by(), from, to, id, amount, data);
          }
          /**
           * @notice Returns whether the interface is supported.
           *
           * @param interfaceId The interface id to check against.
           */
          function supportsInterface(
              bytes4 interfaceId
          )
              public
              view
              virtual
              override(ERC1155ContractMetadataCloneable)
              returns (bool)
          {
              return
                  interfaceId == type(IERC1155SeaDrop).interfaceId ||
                  interfaceId == type(ContractOffererInterface).interfaceId ||
                  interfaceId == 0x2e778efc || // SIP-5 (getSeaportMetadata)
                  // ERC1155ContractMetadata returns supportsInterface true for
                  //     IERC1155ContractMetadata, ERC-4906, ERC-2981
                  // ERC1155A returns supportsInterface true for
                  //     ERC165, ERC1155, ERC1155MetadataURI
                  ERC1155ContractMetadataCloneable.supportsInterface(interfaceId);
          }
          /**
           * @dev Internal function to mint tokens during a generateOrder call
           *      from Seaport.
           *
           * @param data The original transaction calldata, without the selector.
           */
          function _mintOrder(bytes calldata data) internal {
              // Decode fulfiller, minimumReceived, and context from calldata.
              (
                  address fulfiller,
                  SpentItem[] memory minimumReceived,
                  ,
                  bytes memory context
              ) = abi.decode(data, (address, SpentItem[], SpentItem[], bytes));
              // Assign the minter from context[22:42]. We validate context has the
              // correct minimum length in the implementation's `_decodeOrder`.
              address minter;
              assembly {
                  minter := shr(96, mload(add(add(context, 0x20), 22)))
              }
              // If the minter is the zero address, set it to the fulfiller.
              if (minter == address(0)) {
                  minter = fulfiller;
              }
              // Set the token ids and quantities.
              uint256 minimumReceivedLength = minimumReceived.length;
              uint256[] memory tokenIds = new uint256[](minimumReceivedLength);
              uint256[] memory quantities = new uint256[](minimumReceivedLength);
              for (uint256 i = 0; i < minimumReceivedLength; ) {
                  tokenIds[i] = minimumReceived[i].identifier;
                  quantities[i] = minimumReceived[i].amount;
                  unchecked {
                      ++i;
                  }
              }
              // Mint the tokens.
              _batchMint(minter, tokenIds, quantities, "");
          }
          /**
           * @dev Internal function to mint tokens during a multiConfigureMint call
           *      from the configurer contract.
           *
           * @param data The original transaction calldata, without the selector.
           */
          function _multiConfigureMint(bytes calldata data) internal {
              // Decode the calldata.
              (
                  address recipient,
                  uint256[] memory tokenIds,
                  uint256[] memory amounts
              ) = abi.decode(data, (address, uint256[], uint256[]));
              _batchMint(recipient, tokenIds, amounts, "");
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { ISeaDropToken } from "./ISeaDropToken.sol";
      import { PublicDrop } from "../lib/ERC1155SeaDropStructs.sol";
      /**
       * @dev A helper interface to get and set parameters for ERC1155SeaDrop.
       *      The token does not expose these methods as part of its external
       *      interface to optimize contract size, but does implement them.
       */
      interface IERC1155SeaDrop is ISeaDropToken {
          /**
           * @notice Update the SeaDrop public drop parameters at a given index.
           *
           * @param publicDrop The new public drop parameters.
           * @param index      The public drop index.
           */
          function updatePublicDrop(
              PublicDrop calldata publicDrop,
              uint256 index
          ) external;
          /**
           * @notice Returns the public drop stage parameters at a given index.
           *
           * @param index The index of the public drop stage.
           */
          function getPublicDrop(
              uint256 index
          ) external view returns (PublicDrop memory);
          /**
           * @notice Returns the public drop indexes.
           */
          function getPublicDropIndexes() external view returns (uint256[] memory);
          /**
           * @notice Returns a set of mint stats for the address.
           *         This assists SeaDrop in enforcing maxSupply,
           *         maxTotalMintableByWallet, maxTotalMintableByWalletPerToken,
           *         and maxTokenSupplyForStage checks.
           *
           * @dev    NOTE: Implementing contracts should always update these numbers
           *         before transferring any tokens with _safeMint() to mitigate
           *         consequences of malicious onERC1155Received() hooks.
           *
           * @param minter  The minter address.
           * @param tokenId The token id to return stats for.
           */
          function getMintStats(
              address minter,
              uint256 tokenId
          )
              external
              view
              returns (
                  uint256 minterNumMinted,
                  uint256 minterNumMintedForTokenId,
                  uint256 totalMintedForTokenId,
                  uint256 maxSupply
              );
          /**
           * @notice This function is only allowed to be called by the configurer
           *         contract as a way to batch mints and configuration in one tx.
           *
           * @param recipient The address to receive the mints.
           * @param tokenIds  The tokenIds to mint.
           * @param amounts   The amounts to mint.
           */
          function multiConfigureMint(
              address recipient,
              uint256[] calldata tokenIds,
              uint256[] calldata amounts
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ISeaDropTokenContractMetadata
      } from "./ISeaDropTokenContractMetadata.sol";
      import { AllowListData, CreatorPayout } from "../lib/SeaDropStructs.sol";
      /**
       * @dev A helper base interface for IERC721SeaDrop and IERC1155SeaDrop.
       *      The token does not expose these methods as part of its external
       *      interface to optimize contract size, but does implement them.
       */
      interface ISeaDropToken is ISeaDropTokenContractMetadata {
          /**
           * @notice Update the SeaDrop allowed Seaport contracts privileged to mint.
           *         Only the owner can use this function.
           *
           * @param allowedSeaport The allowed Seaport addresses.
           */
          function updateAllowedSeaport(address[] calldata allowedSeaport) external;
          /**
           * @notice Update the SeaDrop allowed fee recipient.
           *         Only the owner can use this function.
           *
           * @param feeRecipient The new fee recipient.
           * @param allowed      Whether the fee recipient is allowed.
           */
          function updateAllowedFeeRecipient(
              address feeRecipient,
              bool allowed
          ) external;
          /**
           * @notice Update the SeaDrop creator payout addresses.
           *         The total basis points must add up to exactly 10_000.
           *         Only the owner can use this function.
           *
           * @param creatorPayouts The new creator payouts.
           */
          function updateCreatorPayouts(
              CreatorPayout[] calldata creatorPayouts
          ) external;
          /**
           * @notice Update the SeaDrop drop URI.
           *         Only the owner can use this function.
           *
           * @param dropURI The new drop URI.
           */
          function updateDropURI(string calldata dropURI) external;
          /**
           * @notice Update the SeaDrop allow list data.
           *         Only the owner can use this function.
           *
           * @param allowListData The new allow list data.
           */
          function updateAllowList(AllowListData calldata allowListData) external;
          /**
           * @notice Update the SeaDrop allowed payers.
           *         Only the owner can use this function.
           *
           * @param payer   The payer to update.
           * @param allowed Whether the payer is allowed.
           */
          function updatePayer(address payer, bool allowed) external;
          /**
           * @notice Update the SeaDrop allowed signer.
           *         Only the owner can use this function.
           *         An allowed signer can also disallow themselves.
           *
           * @param signer  The signer to update.
           * @param allowed Whether the signer is allowed.
           */
          function updateSigner(address signer, bool allowed) external;
          /**
           * @notice Get the SeaDrop allowed Seaport contracts privileged to mint.
           */
          function getAllowedSeaport() external view returns (address[] memory);
          /**
           * @notice Returns the SeaDrop creator payouts.
           */
          function getCreatorPayouts() external view returns (CreatorPayout[] memory);
          /**
           * @notice Returns the SeaDrop allow list merkle root.
           */
          function getAllowListMerkleRoot() external view returns (bytes32);
          /**
           * @notice Returns the SeaDrop allowed fee recipients.
           */
          function getAllowedFeeRecipients() external view returns (address[] memory);
          /**
           * @notice Returns the SeaDrop allowed signers.
           */
          function getSigners() external view returns (address[] memory);
          /**
           * @notice Returns if the signed digest has been used.
           *
           * @param digest The digest hash.
           */
          function getDigestIsUsed(bytes32 digest) external view returns (bool);
          /**
           * @notice Returns the SeaDrop allowed payers.
           */
          function getPayers() external view returns (address[] memory);
          /**
           * @notice Returns the configurer contract.
           */
          function configurer() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          IERC1155ContractMetadata
      } from "../interfaces/IERC1155ContractMetadata.sol";
      import {
          ERC1155ConduitPreapproved
      } from "../lib/ERC1155ConduitPreapproved.sol";
      import {
          ICreatorToken,
          ILegacyCreatorToken
      } from "../interfaces/ICreatorToken.sol";
      import { ITransferValidator1155 } from "../interfaces/ITransferValidator.sol";
      import { TokenTransferValidator } from "../lib/TokenTransferValidator.sol";
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      import { ERC2981 } from "solady/src/tokens/ERC2981.sol";
      import { Ownable } from "solady/src/auth/Ownable.sol";
      import {
          Initializable
      } from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
      /**
       * @title  ERC1155ContractMetadataCloneable
       * @author James Wenzel (emo.eth)
       * @author Ryan Ghods (ralxz.eth)
       * @author Stephan Min (stephanm.eth)
       * @author Michael Cohen (notmichael.eth)
       * @notice A cloneable token contract that extends ERC-1155
       *         with additional metadata and ownership capabilities.
       */
      contract ERC1155ContractMetadataCloneable is
          ERC1155ConduitPreapproved,
          TokenTransferValidator,
          ERC2981,
          Ownable,
          IERC1155ContractMetadata,
          Initializable
      {
          /// @notice A struct containing the token supply info per token id.
          mapping(uint256 => TokenSupply) _tokenSupply;
          /// @notice The total number of tokens minted by address.
          mapping(address => uint256) _totalMintedByUser;
          /// @notice The total number of tokens minted per token id by address.
          mapping(address => mapping(uint256 => uint256)) _totalMintedByUserPerToken;
          /// @notice The name of the token.
          string internal _name;
          /// @notice The symbol of the token.
          string internal _symbol;
          /// @notice The base URI for token metadata.
          string internal _baseURI;
          /// @notice The contract URI for contract metadata.
          string internal _contractURI;
          /// @notice The provenance hash for guaranteeing metadata order
          ///         for random reveals.
          bytes32 internal _provenanceHash;
          /// @notice The allowed contract that can configure SeaDrop parameters.
          address internal _CONFIGURER;
          /**
           * @dev Reverts if the sender is not the owner or the allowed
           *      configurer contract.
           *
           *      This is used as a function instead of a modifier
           *      to save contract space when used multiple times.
           */
          function _onlyOwnerOrConfigurer() internal view {
              if (msg.sender != _CONFIGURER && msg.sender != owner()) {
                  revert Unauthorized();
              }
          }
          /**
           * @notice Deploy the token contract.
           *
           * @param allowedConfigurer The address of the contract allowed to
           *                          configure parameters. Also contains SeaDrop
           *                          implementation code.
           * @param name_             The name of the token.
           * @param symbol_           The symbol of the token.
           */
          function __ERC1155ContractMetadataCloneable_init(
              address allowedConfigurer,
              string memory name_,
              string memory symbol_
          ) internal onlyInitializing {
              // Set the name of the token.
              _name = name_;
              // Set the symbol of the token.
              _symbol = symbol_;
              // Set the allowed configurer contract to interact with this contract.
              _CONFIGURER = allowedConfigurer;
          }
          /**
           * @notice Sets the base URI for the token metadata and emits an event.
           *
           * @param newBaseURI The new base URI to set.
           */
          function setBaseURI(string calldata newBaseURI) external override {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the new base URI.
              _baseURI = newBaseURI;
              // Emit an event with the update.
              emit BatchMetadataUpdate(0, type(uint256).max);
          }
          /**
           * @notice Sets the contract URI for contract metadata.
           *
           * @param newContractURI The new contract URI.
           */
          function setContractURI(string calldata newContractURI) external override {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the new contract URI.
              _contractURI = newContractURI;
              // Emit an event with the update.
              emit ContractURIUpdated(newContractURI);
          }
          /**
           * @notice Emit an event notifying metadata updates for
           *         a range of token ids, according to EIP-4906.
           *
           * @param fromTokenId The start token id.
           * @param toTokenId   The end token id.
           */
          function emitBatchMetadataUpdate(
              uint256 fromTokenId,
              uint256 toTokenId
          ) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Emit an event with the update.
              if (fromTokenId == toTokenId) {
                  // If only one token is being updated, use the event
                  // in the 1155 spec.
                  emit URI(uri(fromTokenId), fromTokenId);
              } else {
                  emit BatchMetadataUpdate(fromTokenId, toTokenId);
              }
          }
          /**
           * @notice Sets the max token supply and emits an event.
           *
           * @param tokenId      The token id to set the max supply for.
           * @param newMaxSupply The new max supply to set.
           */
          function setMaxSupply(uint256 tokenId, uint256 newMaxSupply) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Ensure the max supply does not exceed the maximum value of uint64,
              // a limit due to the storage of bit-packed variables in TokenSupply,
              if (newMaxSupply > 2 ** 64 - 1) {
                  revert CannotExceedMaxSupplyOfUint64(newMaxSupply);
              }
              // Set the new max supply.
              _tokenSupply[tokenId].maxSupply = uint64(newMaxSupply);
              // Emit an event with the update.
              emit MaxSupplyUpdated(tokenId, newMaxSupply);
          }
          /**
           * @notice Sets the provenance hash and emits an event.
           *
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it has not been
           *         modified after mint started.
           *
           *         This function will revert if the provenance hash has already
           *         been set, so be sure to carefully set it only once.
           *
           * @param newProvenanceHash The new provenance hash to set.
           */
          function setProvenanceHash(bytes32 newProvenanceHash) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Keep track of the old provenance hash for emitting with the event.
              bytes32 oldProvenanceHash = _provenanceHash;
              // Revert if the provenance hash has already been set.
              if (oldProvenanceHash != bytes32(0)) {
                  revert ProvenanceHashCannotBeSetAfterAlreadyBeingSet();
              }
              // Set the new provenance hash.
              _provenanceHash = newProvenanceHash;
              // Emit an event with the update.
              emit ProvenanceHashUpdated(oldProvenanceHash, newProvenanceHash);
          }
          /**
           * @notice Sets the default royalty information.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator of 10_000 basis points.
           */
          function setDefaultRoyalty(address receiver, uint96 feeNumerator) external {
              // Ensure the sender is only the owner or configurer contract.
              _onlyOwnerOrConfigurer();
              // Set the default royalty.
              // ERC2981 implementation ensures feeNumerator <= feeDenominator
              // and receiver != address(0).
              _setDefaultRoyalty(receiver, feeNumerator);
              // Emit an event with the updated params.
              emit RoyaltyInfoUpdated(receiver, feeNumerator);
          }
          /**
           * @notice Returns the name of the token.
           */
          function name() external view returns (string memory) {
              return _name;
          }
          /**
           * @notice Returns the symbol of the token.
           */
          function symbol() external view returns (string memory) {
              return _symbol;
          }
          /**
           * @notice Returns the base URI for token metadata.
           */
          function baseURI() external view override returns (string memory) {
              return _baseURI;
          }
          /**
           * @notice Returns the contract URI for contract metadata.
           */
          function contractURI() external view override returns (string memory) {
              return _contractURI;
          }
          /**
           * @notice Returns the max token supply for a token id.
           */
          function maxSupply(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].maxSupply;
          }
          /**
           * @notice Returns the total supply for a token id.
           */
          function totalSupply(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].totalSupply;
          }
          /**
           * @notice Returns the total minted for a token id.
           */
          function totalMinted(uint256 tokenId) external view returns (uint256) {
              return _tokenSupply[tokenId].totalMinted;
          }
          /**
           * @notice Returns the provenance hash.
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it is unmodified
           *         after mint has started.
           */
          function provenanceHash() external view override returns (bytes32) {
              return _provenanceHash;
          }
          /**
           * @notice Returns the URI for token metadata.
           *
           *         This implementation returns the same URI for *all* token types.
           *         It relies on the token type ID substitution mechanism defined
           *         in the EIP to replace {id} with the token id.
           *
           * @custom:param tokenId The token id to get the URI for.
           */
          function uri(
              uint256 /* tokenId */
          ) public view virtual override returns (string memory) {
              // Return the base URI.
              return _baseURI;
          }
          /**
           * @notice Returns the transfer validation function used.
           */
          function getTransferValidationFunction()
              external
              pure
              returns (bytes4 functionSignature, bool isViewFunction)
          {
              functionSignature = ITransferValidator1155.validateTransfer.selector;
              isViewFunction = true;
          }
          /**
           * @notice Set the transfer validator. Only callable by the token owner.
           */
          function setTransferValidator(address newValidator) external onlyOwner {
              // Set the new transfer validator.
              _setTransferValidator(newValidator);
          }
          /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
          function _useBeforeTokenTransfer() internal view virtual override returns (bool) {
              return true;
          }
          /**
           * @dev Hook that is called before any token transfer.
           *      This includes minting and burning.
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory /* data */
          ) internal virtual override {
              if (from != address(0) && to != address(0)) {
                  // Call the transfer validator if one is set.
                  address transferValidator = _transferValidator;
                  if (transferValidator != address(0)) {
                      for (uint256 i = 0; i < ids.length; i++) {
                          ITransferValidator1155(transferValidator).validateTransfer(
                              msg.sender,
                              from,
                              to,
                              ids[i],
                              amounts[i]
                          );
                      }
                  }
              }
          }
          /**
           * @notice Returns whether the interface is supported.
           *
           * @param interfaceId The interface id to check against.
           */
          function supportsInterface(
              bytes4 interfaceId
          ) public view virtual override(ERC1155, ERC2981) returns (bool) {
              return
                  interfaceId == type(IERC1155ContractMetadata).interfaceId ||
                  interfaceId == type(ICreatorToken).interfaceId ||
                  interfaceId == type(ILegacyCreatorToken).interfaceId ||
                  interfaceId == 0x49064906 || // ERC-4906 (MetadataUpdate)
                  ERC2981.supportsInterface(interfaceId) ||
                  // ERC1155 returns supportsInterface true for
                  //     ERC165, ERC1155, ERC1155MetadataURI
                  ERC1155.supportsInterface(interfaceId);
          }
          /**
           * @dev Adds to the internal counters for a mint.
           *
           * @param to     The address to mint to.
           * @param id     The token id to mint.
           * @param amount The quantity to mint.
           * @param data   The data to pass if receiver is a contract.
           */
          function _mint(
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual override {
              // Increment mint counts.
              _incrementMintCounts(to, id, amount);
              ERC1155._mint(to, id, amount, data);
          }
          /**
           * @dev Adds to the internal counters for a batch mint.
           *
           * @param to      The address to mint to.
           * @param ids     The token ids to mint.
           * @param amounts The quantities to mint.
           * @param data    The data to pass if receiver is a contract.
           */
          function _batchMint(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual override {
              // Put ids length on the stack to save MLOADs.
              uint256 idsLength = ids.length;
              for (uint256 i = 0; i < idsLength; ) {
                  // Increment mint counts.
                  _incrementMintCounts(to, ids[i], amounts[i]);
                  unchecked {
                      ++i;
                  }
              }
              ERC1155._batchMint(to, ids, amounts, data);
          }
          /**
           * @dev Subtracts from the internal counters for a burn.
           *
           * @param by     The address calling the burn.
           * @param from   The address to burn from.
           * @param id     The token id to burn.
           * @param amount The amount to burn.
           */
          function _burn(
              address by,
              address from,
              uint256 id,
              uint256 amount
          ) internal virtual override {
              // Reduce the supply.
              _reduceSupplyOnBurn(id, amount);
              ERC1155._burn(by, from, id, amount);
          }
          /**
           * @dev Subtracts from the internal counters for a batch burn.
           *
           * @param by      The address calling the burn.
           * @param from    The address to burn from.
           * @param ids     The token ids to burn.
           * @param amounts The amounts to burn.
           */
          function _batchBurn(
              address by,
              address from,
              uint256[] memory ids,
              uint256[] memory amounts
          ) internal virtual override {
              // Put ids length on the stack to save MLOADs.
              uint256 idsLength = ids.length;
              for (uint256 i = 0; i < idsLength; ) {
                  // Reduce the supply.
                  _reduceSupplyOnBurn(ids[i], amounts[i]);
                  unchecked {
                      ++i;
                  }
              }
              ERC1155._batchBurn(by, from, ids, amounts);
          }
          function _reduceSupplyOnBurn(uint256 id, uint256 amount) internal {
              // Get the current token supply.
              TokenSupply storage tokenSupply = _tokenSupply[id];
              // Reduce the totalSupply.
              unchecked {
                  tokenSupply.totalSupply -= uint64(amount);
              }
          }
          /**
           * @dev Internal function to increment mint counts.
           *
           *      Note that this function does not check if the mint exceeds
           *      maxSupply, which should be validated before this function is called.
           *
           * @param to     The address to mint to.
           * @param id     The token id to mint.
           * @param amount The quantity to mint.
           */
          function _incrementMintCounts(
              address to,
              uint256 id,
              uint256 amount
          ) internal {
              // Get the current token supply.
              TokenSupply storage tokenSupply = _tokenSupply[id];
              if (tokenSupply.totalMinted + amount > tokenSupply.maxSupply) {
                  revert MintExceedsMaxSupply(
                      tokenSupply.totalMinted + amount,
                      tokenSupply.maxSupply
                  );
              }
              // Increment supply and number minted.
              // Can be unchecked because maxSupply cannot be set to exceed uint64.
              unchecked {
                  tokenSupply.totalSupply += uint64(amount);
                  tokenSupply.totalMinted += uint64(amount);
                  // Increment total minted by user.
                  _totalMintedByUser[to] += amount;
                  // Increment total minted by user per token.
                  _totalMintedByUserPerToken[to][id] += amount;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { PublicDrop } from "./ERC1155SeaDropStructs.sol";
      import { CreatorPayout } from "./SeaDropStructs.sol";
      library ERC1155SeaDropContractOffererStorage {
          struct Layout {
              /// @notice The allowed Seaport addresses that can mint.
              mapping(address => bool) _allowedSeaport;
              /// @notice The enumerated allowed Seaport addresses.
              address[] _enumeratedAllowedSeaport;
              /// @notice The public drop data.
              mapping(uint256 => PublicDrop) _publicDrops;
              /// @notice The enumerated public drop indexes.
              uint256[] _enumeratedPublicDropIndexes;
              /// @notice The creator payout addresses and basis points.
              CreatorPayout[] _creatorPayouts;
              /// @notice The allow list merkle root.
              bytes32 _allowListMerkleRoot;
              /// @notice The allowed fee recipients.
              mapping(address => bool) _allowedFeeRecipients;
              /// @notice The enumerated allowed fee recipients.
              address[] _enumeratedFeeRecipients;
              /// @notice The allowed server-side signers.
              mapping(address => bool) _allowedSigners;
              /// @notice The enumerated allowed signers.
              address[] _enumeratedSigners;
              /// @notice The used signature digests.
              mapping(bytes32 => bool) _usedDigests;
              /// @notice The allowed payers.
              mapping(address => bool) _allowedPayers;
              /// @notice The enumerated allowed payers.
              address[] _enumeratedPayers;
          }
          bytes32 internal constant STORAGE_SLOT =
              bytes32(
                  uint256(
                      keccak256("contracts.storage.ERC1155SeaDropContractOfferer")
                  ) - 1
              );
          function layout() internal pure returns (Layout storage l) {
              bytes32 slot = STORAGE_SLOT;
              assembly {
                  l.slot := slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { PublicDrop } from "./ERC1155SeaDropStructs.sol";
      import { SeaDropErrorsAndEvents } from "./SeaDropErrorsAndEvents.sol";
      interface ERC1155SeaDropErrorsAndEvents is SeaDropErrorsAndEvents {
          /**
           * @dev Revert with an error if an empty PublicDrop is provided
           *      for an already-empty public drop.
           */
          error PublicDropStageNotPresent();
          /**
           * @dev Revert with an error if the mint quantity exceeds the
           *      max minted per wallet for a certain token id.
           */
          error MintQuantityExceedsMaxMintedPerWalletForTokenId(
              uint256 tokenId,
              uint256 total,
              uint256 allowed
          );
          /**
           * @dev Revert with an error if the target token id to mint is not within
           *      the drop stage range.
           */
          error TokenIdNotWithinDropStageRange(
              uint256 tokenId,
              uint256 startTokenId,
              uint256 endTokenId
          );
          /**
           *  @notice Revert with an error if the number of maxSupplyAmounts doesn't
           *          match the number of maxSupplyTokenIds.
           */
          error MaxSupplyMismatch();
          /**
           *  @notice Revert with an error if the number of mint tokenIds doesn't
           *          match the number of mint amounts.
           */
          error MintAmountsMismatch();
          /**
           * @notice Revert with an error if the mint order offer contains
           *         a duplicate tokenId.
           */
          error OfferContainsDuplicateTokenId(uint256 tokenId);
          /**
           * @dev Revert if the fromTokenId is greater than the toTokenId.
           */
          error InvalidFromAndToTokenId(uint256 fromTokenId, uint256 toTokenId);
          /**
           *  @notice Revert with an error if the number of publicDropIndexes doesn't
           *          match the number of publicDrops.
           */
          error PublicDropsMismatch();
          /**
           * @dev An event with updated public drop data.
           */
          event PublicDropUpdated(PublicDrop publicDrop, uint256 index);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { AllowListData, CreatorPayout } from "./SeaDropStructs.sol";
      /**
       * @notice A struct defining public drop data.
       *         Designed to fit efficiently in two storage slots.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       * @param paymentToken             The payment token address. Null for
       *                                 native token.
       * @param fromTokenId              The start token id for the stage.
       * @param toTokenId                The end token id for the stage.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed. (The limit for this field is
       *                                 2^16 - 1)
       * @param maxTotalMintableByWalletPerToken Maximum total number of mints a user
       *                                 is allowed for the token id. (The limit for
       *                                 this field is 2^16 - 1)
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       */
      struct PublicDrop {
          // slot 1
          uint80 startPrice; // 80/512 bits
          uint80 endPrice; // 160/512 bits
          uint40 startTime; // 200/512 bits
          uint40 endTime; // 240/512 bits
          bool restrictFeeRecipients; // 248/512 bits
          // uint8 unused;
          // slot 2
          address paymentToken; // 408/512 bits
          uint24 fromTokenId; // 432/512 bits
          uint24 toTokenId; // 456/512 bits
          uint16 maxTotalMintableByWallet; // 472/512 bits
          uint16 maxTotalMintableByWalletPerToken; // 488/512 bits
          uint16 feeBps; // 504/512 bits
      }
      /**
       * @notice A struct defining mint params for an allow list.
       *         An allow list leaf will be composed of `msg.sender` and
       *         the following params.
       *
       *         Note: Since feeBps is encoded in the leaf, backend should ensure
       *         that feeBps is acceptable before generating a proof.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token for the mint. Null for
       *                                 native token.
       * @param fromTokenId              The start token id for the stage.
       * @param toTokenId                The end token id for the stage.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed.
       * @param maxTotalMintableByWalletPerToken Maximum total number of mints a user
       *                                 is allowed for the token id.
       * @param maxTokenSupplyForStage   The limit of token supply this stage can
       *                                 mint within.
       * @param dropStageIndex           The drop stage index to emit with the event
       *                                 for analytical purposes. This should be
       *                                 non-zero since the public mint emits with
       *                                 index zero.
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct MintParams {
          uint256 startPrice;
          uint256 endPrice;
          uint256 startTime;
          uint256 endTime;
          address paymentToken;
          uint256 fromTokenId;
          uint256 toTokenId;
          uint256 maxTotalMintableByWallet;
          uint256 maxTotalMintableByWalletPerToken;
          uint256 maxTokenSupplyForStage;
          uint256 dropStageIndex; // non-zero
          uint256 feeBps;
          bool restrictFeeRecipients;
      }
      /**
       * @dev Struct containing internal SeaDrop implementation logic
       *      mint details to avoid stack too deep.
       *
       * @param feeRecipient The fee recipient.
       * @param payer        The payer of the mint.
       * @param minter       The mint recipient.
       * @param tokenIds     The tokenIds to mint.
       * @param quantities   The number of tokens to mint per tokenId.
       * @param withEffects  Whether to apply state changes of the mint.
       */
      struct MintDetails {
          address feeRecipient;
          address payer;
          address minter;
          uint256[] tokenIds;
          uint256[] quantities;
          bool withEffects;
      }
      /**
       * @notice A struct to configure multiple contract options in one transaction.
       */
      struct MultiConfigureStruct {
          uint256[] maxSupplyTokenIds;
          uint256[] maxSupplyAmounts;
          string baseURI;
          string contractURI;
          PublicDrop[] publicDrops;
          uint256[] publicDropsIndexes;
          string dropURI;
          AllowListData allowListData;
          CreatorPayout[] creatorPayouts;
          bytes32 provenanceHash;
          address[] allowedFeeRecipients;
          address[] disallowedFeeRecipients;
          address[] allowedPayers;
          address[] disallowedPayers;
          // Server-signed
          address[] allowedSigners;
          address[] disallowedSigners;
          // ERC-2981
          address royaltyReceiver;
          uint96 royaltyBps;
          // Mint
          address mintRecipient;
          uint256[] mintTokenIds;
          uint256[] mintAmounts;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      /**
       * @notice A struct defining a creator payout address and basis points.
       *
       * @param payoutAddress The payout address.
       * @param basisPoints   The basis points to pay out to the creator.
       *                      The total creator payouts must equal 10_000 bps.
       */
      struct CreatorPayout {
          address payoutAddress;
          uint16 basisPoints;
      }
      /**
       * @notice A struct defining allow list data (for minting an allow list).
       *
       * @param merkleRoot    The merkle root for the allow list.
       * @param publicKeyURIs If the allowListURI is encrypted, a list of URIs
       *                      pointing to the public keys. Empty if unencrypted.
       * @param allowListURI  The URI for the allow list.
       */
      struct AllowListData {
          bytes32 merkleRoot;
          string[] publicKeyURIs;
          string allowListURI;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import { ERC1155 } from "solady/src/tokens/ERC1155.sol";
      /**
       * @title  ERC1155ConduitPreapproved
       * @notice Solady's ERC1155 with the OpenSea conduit preapproved.
       */
      abstract contract ERC1155ConduitPreapproved is ERC1155 {
          /// @dev The canonical OpenSea conduit.
          address internal constant _CONDUIT =
              0x1E0049783F008A0085193E00003D00cd54003c71;
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual override {
              _safeTransfer(_by(), from, to, id, amount, data);
          }
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) public virtual override {
              _safeBatchTransfer(_by(), from, to, ids, amounts, data);
          }
          function isApprovedForAll(
              address owner,
              address operator
          ) public view virtual override returns (bool) {
              if (operator == _CONDUIT) return true;
              return ERC1155.isApprovedForAll(owner, operator);
          }
          function _by() internal view virtual returns (address result) {
              assembly {
                  // `msg.sender == _CONDUIT ? address(0) : msg.sender`.
                  result := mul(iszero(eq(caller(), _CONDUIT)), caller())
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple ERC1155 implementation.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC1155.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol)
      ///
      /// @dev Note:
      /// The ERC1155 standard allows for self-approvals.
      /// For performance, this implementation WILL NOT revert for such actions.
      /// Please add any checks with overrides if desired.
      abstract contract ERC1155 {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The lengths of the input arrays are not the same.
          error ArrayLengthsMismatch();
          /// @dev Cannot mint or transfer to the zero address.
          error TransferToZeroAddress();
          /// @dev The recipient's balance has overflowed.
          error AccountBalanceOverflow();
          /// @dev Insufficient balance.
          error InsufficientBalance();
          /// @dev Only the token owner or an approved account can manage the tokens.
          error NotOwnerNorApproved();
          /// @dev Cannot safely transfer to a contract that does not implement
          /// the ERC1155Receiver interface.
          error TransferToNonERC1155ReceiverImplementer();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                           EVENTS                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Emitted when `amount` of token `id` is transferred
          /// from `from` to `to` by `operator`.
          event TransferSingle(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256 id,
              uint256 amount
          );
          /// @dev Emitted when `amounts` of token `ids` are transferred
          /// from `from` to `to` by `operator`.
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] amounts
          );
          /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
          event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);
          /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id`
          /// is updated to `value`. This event is not used in the base contract.
          /// You may need to emit this event depending on your URI logic.
          ///
          /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
          event URI(string value, uint256 indexed id);
          /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`.
          uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE =
              0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62;
          /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`.
          uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE =
              0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb;
          /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
          uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
              0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The `ownerSlotSeed` of a given owner is given by.
          /// ```
          ///     let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))
          /// ```
          ///
          /// The balance slot of `owner` is given by.
          /// ```
          ///     mstore(0x20, ownerSlotSeed)
          ///     mstore(0x00, id)
          ///     let balanceSlot := keccak256(0x00, 0x40)
          /// ```
          ///
          /// The operator approval slot of `owner` is given by.
          /// ```
          ///     mstore(0x20, ownerSlotSeed)
          ///     mstore(0x00, operator)
          ///     let operatorApprovalSlot := keccak256(0x0c, 0x34)
          /// ```
          uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                      ERC1155 METADATA                      */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the URI for token `id`.
          ///
          /// You can either return the same templated URI for all token IDs,
          /// (e.g. "https://example.com/api/{id}.json"),
          /// or return a unique URI for each `id`.
          ///
          /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata
          function uri(uint256 id) public view virtual returns (string memory);
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          ERC1155                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the amount of `id` owned by `owner`.
          function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, owner)
                  mstore(0x00, id)
                  result := sload(keccak256(0x00, 0x40))
              }
          }
          /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
          function isApprovedForAll(address owner, address operator)
              public
              view
              virtual
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, owner)
                  mstore(0x00, operator)
                  result := sload(keccak256(0x0c, 0x34))
              }
          }
          /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
          ///
          /// Emits a {ApprovalForAll} event.
          function setApprovalForAll(address operator, bool isApproved) public virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Convert to 0 or 1.
                  isApproved := iszero(iszero(isApproved))
                  // Update the `isApproved` for (`msg.sender`, `operator`).
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, caller())
                  mstore(0x00, operator)
                  sstore(keccak256(0x0c, 0x34), isApproved)
                  // Emit the {ApprovalForAll} event.
                  mstore(0x00, isApproved)
                  // forgefmt: disable-next-line
                  log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator)))
              }
          }
          /// @dev Transfers `amount` of `id` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - If the caller is not `from`,
          ///   it must be approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) public virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                  mstore(0x20, fromSlotSeed)
                  // Clear the upper 96 bits.
                  from := shr(96, fromSlotSeed)
                  to := shr(96, toSlotSeed)
                  // Revert if `to` is the zero address.
                  if iszero(to) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // If the caller is not `from`, do the authorization check.
                  if iszero(eq(caller(), from)) {
                      mstore(0x00, caller())
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Subtract and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, toSlotSeed)
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to)
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // Do the {onERC1155Received} check if `to` is a smart contract.
                  if extcodesize(to) {
                      // Prepare the calldata.
                      let m := mload(0x40)
                      // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                      mstore(m, 0xf23a6e61)
                      mstore(add(m, 0x20), caller())
                      mstore(add(m, 0x40), from)
                      mstore(add(m, 0x60), id)
                      mstore(add(m, 0x80), amount)
                      mstore(add(m, 0xa0), 0xa0)
                      calldatacopy(add(m, 0xc0), sub(data.offset, 0x20), add(0x20, data.length))
                      // Revert if the call reverts.
                      if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) {
                          if returndatasize() {
                              // Bubble up the revert if the call reverts.
                              returndatacopy(0x00, 0x00, returndatasize())
                              revert(0x00, returndatasize())
                          }
                          mstore(m, 0)
                      }
                      // Load the returndata and compare it with the function selector.
                      if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                          mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
          }
          /// @dev Transfers `amounts` of `ids` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - `ids` and `amounts` must have the same length.
          /// - If the caller is not `from`,
          ///   it must be approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) public virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(ids.length, amounts.length)) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from))
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to))
                  mstore(0x20, fromSlotSeed)
                  // Clear the upper 96 bits.
                  from := shr(96, fromSlotSeed)
                  to := shr(96, toSlotSeed)
                  // Revert if `to` is the zero address.
                  if iszero(to) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // If the caller is not `from`, do the authorization check.
                  if iszero(eq(caller(), from)) {
                      mstore(0x00, caller())
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, ids.length)
                      for { let i := 0 } iszero(eq(i, end)) { i := add(i, 0x20) } {
                          let amount := calldataload(add(amounts.offset, i))
                          // Subtract and store the updated balance of `from`.
                          {
                              mstore(0x20, fromSlotSeed)
                              mstore(0x00, calldataload(add(ids.offset, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x20, toSlotSeed)
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, ids.length))
                      let o := add(m, 0x40)
                      calldatacopy(o, sub(ids.offset, 0x20), n)
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, n))
                      o := add(o, n)
                      n := add(0x20, shl(5, amounts.length))
                      calldatacopy(o, sub(amounts.offset, 0x20), n)
                      n := sub(add(o, n), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to)
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransferCalldata(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // Do the {onERC1155BatchReceived} check if `to` is a smart contract.
                  if extcodesize(to) {
                      let m := mload(0x40)
                      // Prepare the calldata.
                      // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                      mstore(m, 0xbc197c81)
                      mstore(add(m, 0x20), caller())
                      mstore(add(m, 0x40), from)
                      // Copy the `ids`.
                      mstore(add(m, 0x60), 0xa0)
                      let n := add(0x20, shl(5, ids.length))
                      let o := add(m, 0xc0)
                      calldatacopy(o, sub(ids.offset, 0x20), n)
                      // Copy the `amounts`.
                      let s := add(0xa0, n)
                      mstore(add(m, 0x80), s)
                      o := add(o, n)
                      n := add(0x20, shl(5, amounts.length))
                      calldatacopy(o, sub(amounts.offset, 0x20), n)
                      // Copy the `data`.
                      mstore(add(m, 0xa0), add(s, n))
                      o := add(o, n)
                      n := add(0x20, data.length)
                      calldatacopy(o, sub(data.offset, 0x20), n)
                      n := sub(add(o, n), add(m, 0x1c))
                      // Revert if the call reverts.
                      if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                          if returndatasize() {
                              // Bubble up the revert if the call reverts.
                              returndatacopy(0x00, 0x00, returndatasize())
                              revert(0x00, returndatasize())
                          }
                          mstore(m, 0)
                      }
                      // Load the returndata and compare it with the function selector.
                      if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                          mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
          }
          /// @dev Returns the amounts of `ids` for `owners.
          ///
          /// Requirements:
          /// - `owners` and `ids` must have the same length.
          function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
              public
              view
              virtual
              returns (uint256[] memory balances)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(ids.length, owners.length)) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  balances := mload(0x40)
                  mstore(balances, ids.length)
                  let o := add(balances, 0x20)
                  let end := shl(5, ids.length)
                  mstore(0x40, add(end, o))
                  // Loop through all the `ids` and load the balances.
                  for { let i := 0 } iszero(eq(i, end)) { i := add(i, 0x20) } {
                      let owner := calldataload(add(owners.offset, i))
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)))
                      mstore(0x00, calldataload(add(ids.offset, i)))
                      mstore(add(o, i), sload(keccak256(0x00, 0x40)))
                  }
              }
          }
          /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
          /// See: https://eips.ethereum.org/EIPS/eip-165
          /// This function call must use less than 30000 gas.
          function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let s := shr(224, interfaceId)
                  // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c.
                  result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c))
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  INTERNAL MINT FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Mints `amount` of `id` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(address(0), to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                      mstore(0x14, to)
                      mstore(0x00, id)
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x00, id)
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(address(0), to, _single(id), _single(amount), data);
              }
              if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data);
          }
          /// @dev Mints `amounts` of `ids` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `ids` and `amounts` must have the same length.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function _batchMint(
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(address(0), to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x00, mload(add(ids, i)))
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_))
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(address(0), to, ids, amounts, data);
              }
              if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  INTERNAL BURN FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Equivalent to `_burn(address(0), from, id, amount)`.
          function _burn(address from, uint256 id, uint256 amount) internal virtual {
              _burn(address(0), from, id, amount);
          }
          /// @dev Destroys `amount` of `id` from `from`.
          ///
          /// Requirements:
          /// - `from` must have at least `amount` of `id`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          ///
          /// Emits a {Transfer} event.
          function _burn(address by, address from, uint256 id, uint256 amount) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, address(0), _single(id), _single(amount), "");
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let from_ := shl(96, from)
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Decrease and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x00, id)
                  mstore(0x20, amount)
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, address(0), _single(id), _single(amount), "");
              }
          }
          /// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`.
          function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts)
              internal
              virtual
          {
              _batchBurn(address(0), from, ids, amounts);
          }
          /// @dev Destroys `amounts` of `ids` from `from`.
          ///
          /// Requirements:
          /// - `ids` and `amounts` must have the same length.
          /// - `from` must have at least `amounts` of `ids`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          ///
          /// Emits a {TransferBatch} event.
          function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts)
              internal
              virtual
          {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, address(0), ids, amounts, "");
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let from_ := shl(96, from)
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Decrease and store the updated balance of `to`.
                          {
                              mstore(0x00, mload(add(ids, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0)
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, address(0), ids, amounts, "");
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                INTERNAL APPROVAL FUNCTIONS                 */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Approve or remove the `operator` as an operator for `by`,
          /// without authorization checks.
          ///
          /// Emits a {ApprovalForAll} event.
          function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Convert to 0 or 1.
                  isApproved := iszero(iszero(isApproved))
                  // Update the `isApproved` for (`by`, `operator`).
                  mstore(0x20, _ERC1155_MASTER_SLOT_SEED)
                  mstore(0x14, by)
                  mstore(0x00, operator)
                  sstore(keccak256(0x0c, 0x34), isApproved)
                  // Emit the {ApprovalForAll} event.
                  mstore(0x00, isApproved)
                  let m := shr(96, not(0))
                  log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator))
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                INTERNAL TRANSFER FUNCTIONS                 */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`.
          function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data)
              internal
              virtual
          {
              _safeTransfer(address(0), from, to, id, amount, data);
          }
          /// @dev Transfers `amount` of `id` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `from` must have at least `amount` of `id`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155Reveived}, which is called upon a batch transfer.
          ///
          /// Emits a {Transfer} event.
          function _safeTransfer(
              address by,
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  let from_ := shl(96, from)
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_))
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Subtract and store the updated balance of `from`.
                  {
                      mstore(0x00, id)
                      let fromBalanceSlot := keccak256(0x00, 0x40)
                      let fromBalance := sload(fromBalanceSlot)
                      if gt(amount, fromBalance) {
                          mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(fromBalanceSlot, sub(fromBalance, amount))
                  }
                  // Increase and store the updated balance of `to`.
                  {
                      mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_))
                      let toBalanceSlot := keccak256(0x00, 0x40)
                      let toBalanceBefore := sload(toBalanceSlot)
                      let toBalanceAfter := add(toBalanceBefore, amount)
                      if lt(toBalanceAfter, toBalanceBefore) {
                          mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                          revert(0x1c, 0x04)
                      }
                      sstore(toBalanceSlot, toBalanceAfter)
                  }
                  // Emit a {TransferSingle} event.
                  mstore(0x20, amount)
                  // forgefmt: disable-next-line
                  log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, _single(id), _single(amount), data);
              }
              if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data);
          }
          /// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`.
          function _safeBatchTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              _safeBatchTransfer(address(0), from, to, ids, amounts, data);
          }
          /// @dev Transfers `amounts` of `ids` from `from` to `to`.
          ///
          /// Requirements:
          /// - `to` cannot be the zero address.
          /// - `ids` and `amounts` must have the same length.
          /// - `from` must have at least `amounts` of `ids`.
          /// - If `by` is not the zero address, it must be either `from`,
          ///   or approved to manage the tokens of `from`.
          /// - If `to` refers to a smart contract, it must implement
          ///   {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer.
          ///
          /// Emits a {TransferBatch} event.
          function _safeBatchTransfer(
              address by,
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {
              if (_useBeforeTokenTransfer()) {
                  _beforeTokenTransfer(from, to, ids, amounts, data);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(eq(mload(ids), mload(amounts))) {
                      mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`.
                      revert(0x1c, 0x04)
                  }
                  let from_ := shl(96, from)
                  let to_ := shl(96, to)
                  // Revert if `to` is the zero address.
                  if iszero(to_) {
                      mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_)
                  let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_)
                  mstore(0x20, fromSlotSeed)
                  // If `by` is not the zero address, and not equal to `from`,
                  // check if it is approved to manage all the tokens of `from`.
                  let by_ := shl(96, by)
                  if iszero(or(iszero(by_), eq(by_, from_))) {
                      mstore(0x00, by)
                      if iszero(sload(keccak256(0x0c, 0x34))) {
                          mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                          revert(0x1c, 0x04)
                      }
                  }
                  // Loop through all the `ids` and update the balances.
                  {
                      let end := shl(5, mload(ids))
                      for { let i := 0 } iszero(eq(i, end)) {} {
                          i := add(i, 0x20)
                          let amount := mload(add(amounts, i))
                          // Subtract and store the updated balance of `from`.
                          {
                              mstore(0x20, fromSlotSeed)
                              mstore(0x00, mload(add(ids, i)))
                              let fromBalanceSlot := keccak256(0x00, 0x40)
                              let fromBalance := sload(fromBalanceSlot)
                              if gt(amount, fromBalance) {
                                  mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(fromBalanceSlot, sub(fromBalance, amount))
                          }
                          // Increase and store the updated balance of `to`.
                          {
                              mstore(0x20, toSlotSeed)
                              let toBalanceSlot := keccak256(0x00, 0x40)
                              let toBalanceBefore := sload(toBalanceSlot)
                              let toBalanceAfter := add(toBalanceBefore, amount)
                              if lt(toBalanceAfter, toBalanceBefore) {
                                  mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                                  revert(0x1c, 0x04)
                              }
                              sstore(toBalanceSlot, toBalanceAfter)
                          }
                      }
                  }
                  // Emit a {TransferBatch} event.
                  {
                      let m := mload(0x40)
                      // Copy the `ids`.
                      mstore(m, 0x40)
                      let n := add(0x20, shl(5, mload(ids)))
                      let o := add(m, 0x40)
                      pop(staticcall(gas(), 4, ids, n, o, n))
                      // Copy the `amounts`.
                      mstore(add(m, 0x20), add(0x40, returndatasize()))
                      o := add(o, returndatasize())
                      n := add(0x20, shl(5, mload(amounts)))
                      pop(staticcall(gas(), 4, amounts, n, o, n))
                      n := sub(add(o, returndatasize()), m)
                      // Do the emit.
                      log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_))
                  }
              }
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, ids, amounts, data);
              }
              if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                    HOOKS FOR OVERRIDING                    */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Override this function to return true if `_beforeTokenTransfer` is used.
          /// The is to help the compiler avoid producing dead bytecode.
          function _useBeforeTokenTransfer() internal view virtual returns (bool) {
              return false;
          }
          /// @dev Hook that is called before any token transfer.
          /// This includes minting and burning, as well as batched variants.
          ///
          /// The same hook is called on both single and batched variants.
          /// For single transfers, the length of the `id` and `amount` arrays are 1.
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /// @dev Override this function to return true if `_afterTokenTransfer` is used.
          /// The is to help the compiler avoid producing dead bytecode.
          function _useAfterTokenTransfer() internal view virtual returns (bool) {
              return false;
          }
          /// @dev Hook that is called after any token transfer.
          /// This includes minting and burning, as well as batched variants.
          ///
          /// The same hook is called on both single and batched variants.
          /// For single transfers, the length of the `id` and `amount` arrays are 1.
          function _afterTokenTransfer(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) internal virtual {}
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                      PRIVATE HELPERS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Helper for calling the `_afterTokenTransfer` hook.
          /// The is to help the compiler avoid producing dead bytecode.
          function _afterTokenTransferCalldata(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) private {
              if (_useAfterTokenTransfer()) {
                  _afterTokenTransfer(from, to, ids, amounts, data);
              }
          }
          /// @dev Returns if `a` has bytecode of non-zero length.
          function _hasCode(address a) private view returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := extcodesize(a) // Can handle dirty upper bits.
              }
          }
          /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`.
          /// Reverts if the target does not support the function correctly.
          function _checkOnERC1155Received(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes memory data
          ) private {
              /// @solidity memory-safe-assembly
              assembly {
                  // Prepare the calldata.
                  let m := mload(0x40)
                  // `onERC1155Received(address,address,uint256,uint256,bytes)`.
                  mstore(m, 0xf23a6e61)
                  mstore(add(m, 0x20), caller())
                  mstore(add(m, 0x40), shr(96, shl(96, from)))
                  mstore(add(m, 0x60), id)
                  mstore(add(m, 0x80), amount)
                  mstore(add(m, 0xa0), 0xa0)
                  let n := mload(data)
                  mstore(add(m, 0xc0), n)
                  if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) }
                  // Revert if the call reverts.
                  if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) {
                      if returndatasize() {
                          // Bubble up the revert if the call reverts.
                          returndatacopy(0x00, 0x00, returndatasize())
                          revert(0x00, returndatasize())
                      }
                      mstore(m, 0)
                  }
                  // Load the returndata and compare it with the function selector.
                  if iszero(eq(mload(m), shl(224, 0xf23a6e61))) {
                      mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`.
          /// Reverts if the target does not support the function correctly.
          function _checkOnERC1155BatchReceived(
              address from,
              address to,
              uint256[] memory ids,
              uint256[] memory amounts,
              bytes memory data
          ) private {
              /// @solidity memory-safe-assembly
              assembly {
                  // Prepare the calldata.
                  let m := mload(0x40)
                  // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`.
                  mstore(m, 0xbc197c81)
                  mstore(add(m, 0x20), caller())
                  mstore(add(m, 0x40), shr(96, shl(96, from)))
                  // Copy the `ids`.
                  mstore(add(m, 0x60), 0xa0)
                  let n := add(0x20, shl(5, mload(ids)))
                  let o := add(m, 0xc0)
                  pop(staticcall(gas(), 4, ids, n, o, n))
                  // Copy the `amounts`.
                  let s := add(0xa0, returndatasize())
                  mstore(add(m, 0x80), s)
                  o := add(o, returndatasize())
                  n := add(0x20, shl(5, mload(amounts)))
                  pop(staticcall(gas(), 4, amounts, n, o, n))
                  // Copy the `data`.
                  mstore(add(m, 0xa0), add(s, returndatasize()))
                  o := add(o, returndatasize())
                  n := add(0x20, mload(data))
                  pop(staticcall(gas(), 4, data, n, o, n))
                  n := sub(add(o, returndatasize()), add(m, 0x1c))
                  // Revert if the call reverts.
                  if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) {
                      if returndatasize() {
                          // Bubble up the revert if the call reverts.
                          returndatacopy(0x00, 0x00, returndatasize())
                          revert(0x00, returndatasize())
                      }
                      mstore(m, 0)
                  }
                  // Load the returndata and compare it with the function selector.
                  if iszero(eq(mload(m), shl(224, 0xbc197c81))) {
                      mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /// @dev Returns `x` in an array with a single element.
          function _single(uint256 x) private pure returns (uint256[] memory result) {
              assembly {
                  result := mload(0x40)
                  mstore(0x40, add(result, 0x40))
                  mstore(result, 1)
                  mstore(add(result, 0x20), x)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      import {
          BasicOrderType,
          ItemType,
          OrderType,
          Side
      } from "./ConsiderationEnums.sol";
      import {
          CalldataPointer,
          MemoryPointer
      } from "../helpers/PointerLibraries.sol";
      /**
       * @dev An order contains eleven components: an offerer, a zone (or account that
       *      can cancel the order or restrict who can fulfill the order depending on
       *      the type), the order type (specifying partial fill support as well as
       *      restricted order status), the start and end time, a hash that will be
       *      provided to the zone when validating restricted orders, a salt, a key
       *      corresponding to a given conduit, a counter, and an arbitrary number of
       *      offer items that can be spent along with consideration items that must
       *      be received by their respective recipient.
       */
      struct OrderComponents {
          address offerer;
          address zone;
          OfferItem[] offer;
          ConsiderationItem[] consideration;
          OrderType orderType;
          uint256 startTime;
          uint256 endTime;
          bytes32 zoneHash;
          uint256 salt;
          bytes32 conduitKey;
          uint256 counter;
      }
      /**
       * @dev An offer item has five components: an item type (ETH or other native
       *      tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and
       *      ERC1155), a token address, a dual-purpose "identifierOrCriteria"
       *      component that will either represent a tokenId or a merkle root
       *      depending on the item type, and a start and end amount that support
       *      increasing or decreasing amounts over the duration of the respective
       *      order.
       */
      struct OfferItem {
          ItemType itemType;
          address token;
          uint256 identifierOrCriteria;
          uint256 startAmount;
          uint256 endAmount;
      }
      /**
       * @dev A consideration item has the same five components as an offer item and
       *      an additional sixth component designating the required recipient of the
       *      item.
       */
      struct ConsiderationItem {
          ItemType itemType;
          address token;
          uint256 identifierOrCriteria;
          uint256 startAmount;
          uint256 endAmount;
          address payable recipient;
      }
      /**
       * @dev A spent item is translated from a utilized offer item and has four
       *      components: an item type (ETH or other native tokens, ERC20, ERC721, and
       *      ERC1155), a token address, a tokenId, and an amount.
       */
      struct SpentItem {
          ItemType itemType;
          address token;
          uint256 identifier;
          uint256 amount;
      }
      /**
       * @dev A received item is translated from a utilized consideration item and has
       *      the same four components as a spent item, as well as an additional fifth
       *      component designating the required recipient of the item.
       */
      struct ReceivedItem {
          ItemType itemType;
          address token;
          uint256 identifier;
          uint256 amount;
          address payable recipient;
      }
      /**
       * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155
       *      matching, a group of six functions may be called that only requires a
       *      subset of the usual order arguments. Note the use of a "basicOrderType"
       *      enum; this represents both the usual order type as well as the "route"
       *      of the basic order (a simple derivation function for the basic order
       *      type is `basicOrderType = orderType + (4 * basicOrderRoute)`.)
       */
      struct BasicOrderParameters {
          // calldata offset
          address considerationToken; // 0x24
          uint256 considerationIdentifier; // 0x44
          uint256 considerationAmount; // 0x64
          address payable offerer; // 0x84
          address zone; // 0xa4
          address offerToken; // 0xc4
          uint256 offerIdentifier; // 0xe4
          uint256 offerAmount; // 0x104
          BasicOrderType basicOrderType; // 0x124
          uint256 startTime; // 0x144
          uint256 endTime; // 0x164
          bytes32 zoneHash; // 0x184
          uint256 salt; // 0x1a4
          bytes32 offererConduitKey; // 0x1c4
          bytes32 fulfillerConduitKey; // 0x1e4
          uint256 totalOriginalAdditionalRecipients; // 0x204
          AdditionalRecipient[] additionalRecipients; // 0x224
          bytes signature; // 0x244
          // Total length, excluding dynamic array data: 0x264 (580)
      }
      /**
       * @dev Basic orders can supply any number of additional recipients, with the
       *      implied assumption that they are supplied from the offered ETH (or other
       *      native token) or ERC20 token for the order.
       */
      struct AdditionalRecipient {
          uint256 amount;
          address payable recipient;
      }
      /**
       * @dev The full set of order components, with the exception of the counter,
       *      must be supplied when fulfilling more sophisticated orders or groups of
       *      orders. The total number of original consideration items must also be
       *      supplied, as the caller may specify additional consideration items.
       */
      struct OrderParameters {
          address offerer; // 0x00
          address zone; // 0x20
          OfferItem[] offer; // 0x40
          ConsiderationItem[] consideration; // 0x60
          OrderType orderType; // 0x80
          uint256 startTime; // 0xa0
          uint256 endTime; // 0xc0
          bytes32 zoneHash; // 0xe0
          uint256 salt; // 0x100
          bytes32 conduitKey; // 0x120
          uint256 totalOriginalConsiderationItems; // 0x140
          // offer.length                          // 0x160
      }
      /**
       * @dev Orders require a signature in addition to the other order parameters.
       */
      struct Order {
          OrderParameters parameters;
          bytes signature;
      }
      /**
       * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
       *      and a denominator (the total size of the order) in addition to the
       *      signature and other order parameters. It also supports an optional field
       *      for supplying extra data; this data will be provided to the zone if the
       *      order type is restricted and the zone is not the caller, or will be
       *      provided to the offerer as context for contract order types.
       */
      struct AdvancedOrder {
          OrderParameters parameters;
          uint120 numerator;
          uint120 denominator;
          bytes signature;
          bytes extraData;
      }
      /**
       * @dev Orders can be validated (either explicitly via `validate`, or as a
       *      consequence of a full or partial fill), specifically cancelled (they can
       *      also be cancelled in bulk via incrementing a per-zone counter), and
       *      partially or fully filled (with the fraction filled represented by a
       *      numerator and denominator).
       */
      struct OrderStatus {
          bool isValidated;
          bool isCancelled;
          uint120 numerator;
          uint120 denominator;
      }
      /**
       * @dev A criteria resolver specifies an order, side (offer vs. consideration),
       *      and item index. It then provides a chosen identifier (i.e. tokenId)
       *      alongside a merkle proof demonstrating the identifier meets the required
       *      criteria.
       */
      struct CriteriaResolver {
          uint256 orderIndex;
          Side side;
          uint256 index;
          uint256 identifier;
          bytes32[] criteriaProof;
      }
      /**
       * @dev A fulfillment is applied to a group of orders. It decrements a series of
       *      offer and consideration items, then generates a single execution
       *      element. A given fulfillment can be applied to as many offer and
       *      consideration items as desired, but must contain at least one offer and
       *      at least one consideration that match. The fulfillment must also remain
       *      consistent on all key parameters across all offer items (same offerer,
       *      token, type, tokenId, and conduit preference) as well as across all
       *      consideration items (token, type, tokenId, and recipient).
       */
      struct Fulfillment {
          FulfillmentComponent[] offerComponents;
          FulfillmentComponent[] considerationComponents;
      }
      /**
       * @dev Each fulfillment component contains one index referencing a specific
       *      order and another referencing a specific offer or consideration item.
       */
      struct FulfillmentComponent {
          uint256 orderIndex;
          uint256 itemIndex;
      }
      /**
       * @dev An execution is triggered once all consideration items have been zeroed
       *      out. It sends the item in question from the offerer to the item's
       *      recipient, optionally sourcing approvals from either this contract
       *      directly or from the offerer's chosen conduit if one is specified. An
       *      execution is not provided as an argument, but rather is derived via
       *      orders, criteria resolvers, and fulfillments (where the total number of
       *      executions will be less than or equal to the total number of indicated
       *      fulfillments) and returned as part of `matchOrders`.
       */
      struct Execution {
          ReceivedItem item;
          address offerer;
          bytes32 conduitKey;
      }
      /**
       * @dev Restricted orders are validated post-execution by calling validateOrder
       *      on the zone. This struct provides context about the order fulfillment
       *      and any supplied extraData, as well as all order hashes fulfilled in a
       *      call to a match or fulfillAvailable method.
       */
      struct ZoneParameters {
          bytes32 orderHash;
          address fulfiller;
          address offerer;
          SpentItem[] offer;
          ReceivedItem[] consideration;
          bytes extraData;
          bytes32[] orderHashes;
          uint256 startTime;
          uint256 endTime;
          bytes32 zoneHash;
      }
      /**
       * @dev Zones and contract offerers can communicate which schemas they implement
       *      along with any associated metadata related to each schema.
       */
      struct Schema {
          uint256 id;
          bytes metadata;
      }
      using StructPointers for OrderComponents global;
      using StructPointers for OfferItem global;
      using StructPointers for ConsiderationItem global;
      using StructPointers for SpentItem global;
      using StructPointers for ReceivedItem global;
      using StructPointers for BasicOrderParameters global;
      using StructPointers for AdditionalRecipient global;
      using StructPointers for OrderParameters global;
      using StructPointers for Order global;
      using StructPointers for AdvancedOrder global;
      using StructPointers for OrderStatus global;
      using StructPointers for CriteriaResolver global;
      using StructPointers for Fulfillment global;
      using StructPointers for FulfillmentComponent global;
      using StructPointers for Execution global;
      using StructPointers for ZoneParameters global;
      /**
       * @dev This library provides a set of functions for converting structs to
       *      pointers.
       */
      library StructPointers {
          /**
           * @dev Get a MemoryPointer from OrderComponents.
           *
           * @param obj The OrderComponents object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderComponents memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderComponents.
           *
           * @param obj The OrderComponents object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderComponents calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OfferItem.
           *
           * @param obj The OfferItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OfferItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OfferItem.
           *
           * @param obj The OfferItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OfferItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ConsiderationItem.
           *
           * @param obj The ConsiderationItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ConsiderationItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ConsiderationItem.
           *
           * @param obj The ConsiderationItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ConsiderationItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from SpentItem.
           *
           * @param obj The SpentItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              SpentItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from SpentItem.
           *
           * @param obj The SpentItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              SpentItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ReceivedItem.
           *
           * @param obj The ReceivedItem object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ReceivedItem memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ReceivedItem.
           *
           * @param obj The ReceivedItem object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ReceivedItem calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from BasicOrderParameters.
           *
           * @param obj The BasicOrderParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              BasicOrderParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from BasicOrderParameters.
           *
           * @param obj The BasicOrderParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              BasicOrderParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from AdditionalRecipient.
           *
           * @param obj The AdditionalRecipient object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              AdditionalRecipient memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from AdditionalRecipient.
           *
           * @param obj The AdditionalRecipient object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              AdditionalRecipient calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OrderParameters.
           *
           * @param obj The OrderParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderParameters.
           *
           * @param obj The OrderParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Order.
           *
           * @param obj The Order object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Order memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Order.
           *
           * @param obj The Order object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Order calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from AdvancedOrder.
           *
           * @param obj The AdvancedOrder object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              AdvancedOrder memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from AdvancedOrder.
           *
           * @param obj The AdvancedOrder object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              AdvancedOrder calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from OrderStatus.
           *
           * @param obj The OrderStatus object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              OrderStatus memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from OrderStatus.
           *
           * @param obj The OrderStatus object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              OrderStatus calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from CriteriaResolver.
           *
           * @param obj The CriteriaResolver object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              CriteriaResolver memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from CriteriaResolver.
           *
           * @param obj The CriteriaResolver object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              CriteriaResolver calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Fulfillment.
           *
           * @param obj The Fulfillment object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Fulfillment memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Fulfillment.
           *
           * @param obj The Fulfillment object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Fulfillment calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from FulfillmentComponent.
           *
           * @param obj The FulfillmentComponent object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              FulfillmentComponent memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from FulfillmentComponent.
           *
           * @param obj The FulfillmentComponent object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              FulfillmentComponent calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from Execution.
           *
           * @param obj The Execution object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              Execution memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from Execution.
           *
           * @param obj The Execution object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              Execution calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a MemoryPointer from ZoneParameters.
           *
           * @param obj The ZoneParameters object.
           *
           * @return ptr The MemoryPointer.
           */
          function toMemoryPointer(
              ZoneParameters memory obj
          ) internal pure returns (MemoryPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
          /**
           * @dev Get a CalldataPointer from ZoneParameters.
           *
           * @param obj The ZoneParameters object.
           *
           * @return ptr The CalldataPointer.
           */
          function toCalldataPointer(
              ZoneParameters calldata obj
          ) internal pure returns (CalldataPointer ptr) {
              assembly {
                  ptr := obj
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      import {ReceivedItem, Schema, SpentItem} from "../lib/ConsiderationStructs.sol";
      import {IERC165} from "../interfaces/IERC165.sol";
      /**
       * @title ContractOffererInterface
       * @notice Contains the minimum interfaces needed to interact with a contract
       *         offerer.
       */
      interface ContractOffererInterface is IERC165 {
          /**
           * @dev Generates an order with the specified minimum and maximum spent
           *      items, and optional context (supplied as extraData).
           *
           * @param fulfiller       The address of the fulfiller.
           * @param minimumReceived The minimum items that the caller is willing to
           *                        receive.
           * @param maximumSpent    The maximum items the caller is willing to spend.
           * @param context         Additional context of the order.
           *
           * @return offer         A tuple containing the offer items.
           * @return consideration A tuple containing the consideration items.
           */
          function generateOrder(
              address fulfiller,
              SpentItem[] calldata minimumReceived,
              SpentItem[] calldata maximumSpent,
              bytes calldata context // encoded based on the schemaID
          ) external returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
          /**
           * @dev Ratifies an order with the specified offer, consideration, and
           *      optional context (supplied as extraData).
           *
           * @param offer         The offer items.
           * @param consideration The consideration items.
           * @param context       Additional context of the order.
           * @param orderHashes   The hashes to ratify.
           * @param contractNonce The nonce of the contract.
           *
           * @return ratifyOrderMagicValue The magic value returned by the contract
           *                               offerer.
           */
          function ratifyOrder(
              SpentItem[] calldata offer,
              ReceivedItem[] calldata consideration,
              bytes calldata context, // encoded based on the schemaID
              bytes32[] calldata orderHashes,
              uint256 contractNonce
          ) external returns (bytes4 ratifyOrderMagicValue);
          /**
           * @dev View function to preview an order generated in response to a minimum
           *      set of received items, maximum set of spent items, and context
           *      (supplied as extraData).
           *
           * @param caller          The address of the caller (e.g. Seaport).
           * @param fulfiller       The address of the fulfiller (e.g. the account
           *                        calling Seaport).
           * @param minimumReceived The minimum items that the caller is willing to
           *                        receive.
           * @param maximumSpent    The maximum items the caller is willing to spend.
           * @param context         Additional context of the order.
           *
           * @return offer         A tuple containing the offer items.
           * @return consideration A tuple containing the consideration items.
           */
          function previewOrder(
              address caller,
              address fulfiller,
              SpentItem[] calldata minimumReceived,
              SpentItem[] calldata maximumSpent,
              bytes calldata context // encoded based on the schemaID
          ) external view returns (SpentItem[] memory offer, ReceivedItem[] memory consideration);
          /**
           * @dev Gets the metadata for this contract offerer.
           *
           * @return name    The name of the contract offerer.
           * @return schemas The schemas supported by the contract offerer.
           */
          function getSeaportMetadata() external view returns (string memory name, Schema[] memory schemas); // map to Seaport Improvement Proposal IDs
          function supportsInterface(bytes4 interfaceId) external view override returns (bool);
          // Additional functions and/or events based on implemented schemaIDs
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.19;
      /**
       * @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.19;
      interface ISeaDropTokenContractMetadata {
          /**
           * @dev Emit an event for token metadata reveals/updates,
           *      according to EIP-4906.
           *
           * @param _fromTokenId The start token id.
           * @param _toTokenId   The end token id.
           */
          event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
          /**
           * @dev Emit an event when the URI for the collection-level metadata
           *      is updated.
           */
          event ContractURIUpdated(string newContractURI);
          /**
           * @dev Emit an event with the previous and new provenance hash after
           *      being updated.
           */
          event ProvenanceHashUpdated(bytes32 previousHash, bytes32 newHash);
          /**
           * @dev Emit an event when the EIP-2981 royalty info is updated.
           */
          event RoyaltyInfoUpdated(address receiver, uint256 basisPoints);
          /**
           * @notice Throw if the max supply exceeds uint64, a limit
           *         due to the storage of bit-packed variables.
           */
          error CannotExceedMaxSupplyOfUint64(uint256 got);
          /**
           * @dev Revert with an error when attempting to set the provenance
           *      hash after the mint has started.
           */
          error ProvenanceHashCannotBeSetAfterMintStarted();
          /**
           * @dev Revert with an error when attempting to set the provenance
           *      hash after it has already been set.
           */
          error ProvenanceHashCannotBeSetAfterAlreadyBeingSet();
          /**
           * @notice Sets the base URI for the token metadata and emits an event.
           *
           * @param tokenURI The new base URI to set.
           */
          function setBaseURI(string calldata tokenURI) external;
          /**
           * @notice Sets the contract URI for contract metadata.
           *
           * @param newContractURI The new contract URI.
           */
          function setContractURI(string calldata newContractURI) external;
          /**
           * @notice Sets the provenance hash and emits an event.
           *
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it has not been
           *         modified after mint started.
           *
           *         This function will revert after the first item has been minted.
           *
           * @param newProvenanceHash The new provenance hash to set.
           */
          function setProvenanceHash(bytes32 newProvenanceHash) external;
          /**
           * @notice Sets the default royalty information.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator of
           *   10_000 basis points.
           */
          function setDefaultRoyalty(address receiver, uint96 feeNumerator) external;
          /**
           * @notice Returns the base URI for token metadata.
           */
          function baseURI() external view returns (string memory);
          /**
           * @notice Returns the contract URI.
           */
          function contractURI() external view returns (string memory);
          /**
           * @notice Returns the provenance hash.
           *         The provenance hash is used for random reveals, which
           *         is a hash of the ordered metadata to show it is unmodified
           *         after mint has started.
           */
          function provenanceHash() external view returns (bytes32);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.19;
      import {
          ISeaDropTokenContractMetadata
      } from "./ISeaDropTokenContractMetadata.sol";
      interface IERC1155ContractMetadata is ISeaDropTokenContractMetadata {
          /**
           * @dev A struct representing the supply info for a token id,
           *      packed into one storage slot.
           *
           * @param maxSupply   The max supply for the token id.
           * @param totalSupply The total token supply for the token id.
           *                    Subtracted when an item is burned.
           * @param totalMinted The total number of tokens minted for the token id.
           */
          struct TokenSupply {
              uint64 maxSupply; // 64/256 bits
              uint64 totalSupply; // 128/256 bits
              uint64 totalMinted; // 192/256 bits
          }
          /**
           * @dev Emit an event when the max token supply for a token id is updated.
           */
          event MaxSupplyUpdated(uint256 tokenId, uint256 newMaxSupply);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply.
           */
          error MintExceedsMaxSupply(uint256 total, uint256 maxSupply);
          /**
           * @notice Sets the max supply for a token id and emits an event.
           *
           * @param tokenId      The token id to set the max supply for.
           * @param newMaxSupply The new max supply to set.
           */
          function setMaxSupply(uint256 tokenId, uint256 newMaxSupply) external;
          /**
           * @notice Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @notice Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @notice Returns the max token supply for a token id.
           */
          function maxSupply(uint256 tokenId) external view returns (uint256);
          /**
           * @notice Returns the total supply for a token id.
           */
          function totalSupply(uint256 tokenId) external view returns (uint256);
          /**
           * @notice Returns the total minted for a token id.
           */
          function totalMinted(uint256 tokenId) external view returns (uint256);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      interface ICreatorToken {
          event TransferValidatorUpdated(address oldValidator, address newValidator);
          function getTransferValidator() external view returns (address validator);
          function getTransferValidationFunction()
              external
              view
              returns (bytes4 functionSignature, bool isViewFunction);
          function setTransferValidator(address validator) external;
      }
      interface ILegacyCreatorToken {
          event TransferValidatorUpdated(address oldValidator, address newValidator);
          function getTransferValidator() external view returns (address validator);
          function setTransferValidator(address validator) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      interface ITransferValidator721 {
          /// @notice Ensure that a transfer has been authorized for a specific tokenId
          function validateTransfer(
              address caller,
              address from,
              address to,
              uint256 tokenId
          ) external view;
      }
      interface ITransferValidator1155 {
          /// @notice Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and reduce the transferable amount remaining
          function validateTransfer(
              address caller,
              address from,
              address to,
              uint256 tokenId,
              uint256 amount
          ) external;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.17;
      import { ICreatorToken } from "../interfaces/ICreatorToken.sol";
      /**
       * @title  TokenTransferValidator
       * @notice Functionality to use a transfer validator.
       */
      abstract contract TokenTransferValidator is ICreatorToken {
          /// @dev Store the transfer validator. The null address means no transfer validator is set.
          address internal _transferValidator;
          /// @notice Revert with an error if the transfer validator is being set to the same address.
          error SameTransferValidator();
          /// @notice Returns the currently active transfer validator.
          ///         The null address means no transfer validator is set.
          function getTransferValidator() external view returns (address) {
              return _transferValidator;
          }
          /// @notice Set the transfer validator.
          ///         The external method that uses this must include access control.
          function _setTransferValidator(address newValidator) internal {
              address oldValidator = _transferValidator;
              if (oldValidator == newValidator) {
                  revert SameTransferValidator();
              }
              _transferValidator = newValidator;
              emit TransferValidatorUpdated(oldValidator, newValidator);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple ERC2981 NFT Royalty Standard implementation.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol)
      abstract contract ERC2981 {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The royalty fee numerator exceeds the fee denominator.
          error RoyaltyOverflow();
          /// @dev The royalty receiver cannot be the zero address.
          error RoyaltyReceiverIsZeroAddress();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The default royalty info is given by:
          /// ```
          ///     let packed := sload(_ERC2981_MASTER_SLOT_SEED)
          ///     let receiver := shr(96, packed)
          ///     let royaltyFraction := xor(packed, shl(96, receiver))
          /// ```
          ///
          /// The per token royalty info is given by.
          /// ```
          ///     mstore(0x00, tokenId)
          ///     mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
          ///     let packed := sload(keccak256(0x00, 0x40))
          ///     let receiver := shr(96, packed)
          ///     let royaltyFraction := xor(packed, shl(96, receiver))
          /// ```
          uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          ERC2981                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Checks that `_feeDenominator` is non-zero.
          constructor() {
              require(_feeDenominator() != 0, "Fee denominator cannot be zero.");
          }
          /// @dev Returns the denominator for the royalty amount.
          /// Defaults to 10000, which represents fees in basis points.
          /// Override this function to return a custom amount if needed.
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
          /// See: https://eips.ethereum.org/EIPS/eip-165
          /// This function call must use less than 30000 gas.
          function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let s := shr(224, interfaceId)
                  // ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a.
                  result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a))
              }
          }
          /// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`.
          function royaltyInfo(uint256 tokenId, uint256 salePrice)
              public
              view
              virtual
              returns (address receiver, uint256 royaltyAmount)
          {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  let packed := sload(keccak256(0x00, 0x40))
                  receiver := shr(96, packed)
                  if iszero(receiver) {
                      packed := sload(mload(0x20))
                      receiver := shr(96, packed)
                  }
                  let x := salePrice
                  let y := xor(packed, shl(96, receiver)) // `feeNumerator`.
                  // Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
                  // Out-of-gas revert. Should not be triggered in practice, but included for safety.
                  returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y))))
                  royaltyAmount := div(mul(x, y), feeDenominator)
              }
          }
          /// @dev Sets the default royalty `receiver` and `feeNumerator`.
          ///
          /// Requirements:
          /// - `receiver` must not be the zero address.
          /// - `feeNumerator` must not be greater than the fee denominator.
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  feeNumerator := shr(160, shl(160, feeNumerator))
                  if gt(feeNumerator, feeDenominator) {
                      mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
                      revert(0x1c, 0x04)
                  }
                  let packed := shl(96, receiver)
                  if iszero(packed) {
                      mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator))
              }
          }
          /// @dev Sets the default royalty `receiver` and `feeNumerator` to zero.
          function _deleteDefaultRoyalty() internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  sstore(_ERC2981_MASTER_SLOT_SEED, 0)
              }
          }
          /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`.
          ///
          /// Requirements:
          /// - `receiver` must not be the zero address.
          /// - `feeNumerator` must not be greater than the fee denominator.
          function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator)
              internal
              virtual
          {
              uint256 feeDenominator = _feeDenominator();
              /// @solidity memory-safe-assembly
              assembly {
                  feeNumerator := shr(160, shl(160, feeNumerator))
                  if gt(feeNumerator, feeDenominator) {
                      mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`.
                      revert(0x1c, 0x04)
                  }
                  let packed := shl(96, receiver)
                  if iszero(packed) {
                      mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  sstore(keccak256(0x00, 0x40), or(packed, feeNumerator))
              }
          }
          /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero.
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, tokenId)
                  mstore(0x20, _ERC2981_MASTER_SLOT_SEED)
                  sstore(keccak256(0x00, 0x40), 0)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Simple single owner authorization mixin.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
      /// @dev While the ownable portion follows
      /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
      /// the nomenclature for the 2-step ownership handover may be unique to this codebase.
      abstract contract Ownable {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                       CUSTOM ERRORS                        */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The caller is not authorized to call the function.
          error Unauthorized();
          /// @dev The `newOwner` cannot be the zero address.
          error NewOwnerIsZeroAddress();
          /// @dev The `pendingOwner` does not have a valid handover request.
          error NoHandoverRequest();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                           EVENTS                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
          /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
          /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
          /// despite it not being as lightweight as a single argument event.
          event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
          /// @dev An ownership handover to `pendingOwner` has been requested.
          event OwnershipHandoverRequested(address indexed pendingOwner);
          /// @dev The ownership handover to `pendingOwner` has been canceled.
          event OwnershipHandoverCanceled(address indexed pendingOwner);
          /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
          uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
              0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
          /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
          uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
              0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
          /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
          uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
              0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                          STORAGE                           */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
          /// It is intentionally chosen to be a high value
          /// to avoid collision with lower slots.
          /// The choice of manual storage layout is to enable compatibility
          /// with both regular and upgradeable contracts.
          uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
          /// The ownership handover slot of `newOwner` is given by:
          /// ```
          ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
          ///     let handoverSlot := keccak256(0x00, 0x20)
          /// ```
          /// It stores the expiry timestamp of the two-step ownership handover.
          uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     INTERNAL FUNCTIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Initializes the owner directly without authorization guard.
          /// This function must be called upon initialization,
          /// regardless of whether the contract is upgradeable or not.
          /// This is to enable generalization to both regular and upgradeable contracts,
          /// and to save gas in case the initial owner is not the caller.
          /// For performance reasons, this function will not check if there
          /// is an existing owner.
          function _initializeOwner(address newOwner) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Clean the upper 96 bits.
                  newOwner := shr(96, shl(96, newOwner))
                  // Store the new value.
                  sstore(not(_OWNER_SLOT_NOT), newOwner)
                  // Emit the {OwnershipTransferred} event.
                  log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
              }
          }
          /// @dev Sets the owner directly without authorization guard.
          function _setOwner(address newOwner) internal virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  let ownerSlot := not(_OWNER_SLOT_NOT)
                  // Clean the upper 96 bits.
                  newOwner := shr(96, shl(96, newOwner))
                  // Emit the {OwnershipTransferred} event.
                  log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                  // Store the new value.
                  sstore(ownerSlot, newOwner)
              }
          }
          /// @dev Throws if the sender is not the owner.
          function _checkOwner() internal view virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // If the caller is not the stored owner, revert.
                  if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                      mstore(0x00, 0x82b42900) // `Unauthorized()`.
                      revert(0x1c, 0x04)
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  PUBLIC UPDATE FUNCTIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Allows the owner to transfer the ownership to `newOwner`.
          function transferOwnership(address newOwner) public payable virtual onlyOwner {
              /// @solidity memory-safe-assembly
              assembly {
                  if iszero(shl(96, newOwner)) {
                      mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                      revert(0x1c, 0x04)
                  }
              }
              _setOwner(newOwner);
          }
          /// @dev Allows the owner to renounce their ownership.
          function renounceOwnership() public payable virtual onlyOwner {
              _setOwner(address(0));
          }
          /// @dev Request a two-step ownership handover to the caller.
          /// The request will automatically expire in 48 hours (172800 seconds) by default.
          function requestOwnershipHandover() public payable virtual {
              unchecked {
                  uint256 expires = block.timestamp + ownershipHandoverValidFor();
                  /// @solidity memory-safe-assembly
                  assembly {
                      // Compute and set the handover slot to `expires`.
                      mstore(0x0c, _HANDOVER_SLOT_SEED)
                      mstore(0x00, caller())
                      sstore(keccak256(0x0c, 0x20), expires)
                      // Emit the {OwnershipHandoverRequested} event.
                      log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
                  }
              }
          }
          /// @dev Cancels the two-step ownership handover to the caller, if any.
          function cancelOwnershipHandover() public payable virtual {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute and set the handover slot to 0.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, caller())
                  sstore(keccak256(0x0c, 0x20), 0)
                  // Emit the {OwnershipHandoverCanceled} event.
                  log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
              }
          }
          /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
          /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
          function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute and set the handover slot to 0.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, pendingOwner)
                  let handoverSlot := keccak256(0x0c, 0x20)
                  // If the handover does not exist, or has expired.
                  if gt(timestamp(), sload(handoverSlot)) {
                      mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                      revert(0x1c, 0x04)
                  }
                  // Set the handover slot to 0.
                  sstore(handoverSlot, 0)
              }
              _setOwner(pendingOwner);
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   PUBLIC READ FUNCTIONS                    */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the owner of the contract.
          function owner() public view virtual returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := sload(not(_OWNER_SLOT_NOT))
              }
          }
          /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
          function ownershipHandoverExpiresAt(address pendingOwner)
              public
              view
              virtual
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  // Compute the handover slot.
                  mstore(0x0c, _HANDOVER_SLOT_SEED)
                  mstore(0x00, pendingOwner)
                  // Load the handover slot.
                  result := sload(keccak256(0x0c, 0x20))
              }
          }
          /// @dev Returns how long a two-step ownership handover is valid for in seconds.
          function ownershipHandoverValidFor() public view virtual returns (uint64) {
              return 48 * 3600;
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         MODIFIERS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Marks a function as only callable by the owner.
          modifier onlyOwner() virtual {
              _checkOwner();
              _;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.19;
      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) || (address(this).code.length == 0 && _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
      pragma solidity ^0.8.19;
      import { CreatorPayout, PublicDrop } from "./ERC721SeaDropStructs.sol";
      interface SeaDropErrorsAndEvents {
          /**
           * @notice The SeaDrop token types, emitted as part of
           *         `event SeaDropTokenDeployed`.
           */
          enum SEADROP_TOKEN_TYPE {
              ERC721_STANDARD,
              ERC721_CLONE,
              ERC721_UPGRADEABLE,
              ERC1155_STANDARD,
              ERC1155_CLONE,
              ERC1155_UPGRADEABLE
          }
          /**
           * @notice An event to signify that a SeaDrop token contract was deployed.
           */
          event SeaDropTokenDeployed(SEADROP_TOKEN_TYPE tokenType);
          /**
           * @notice Revert with an error if the function selector is not supported.
           */
          error UnsupportedFunctionSelector(bytes4 selector);
          /**
           * @dev Revert with an error if the drop stage is not active.
           */
          error NotActive(
              uint256 currentTimestamp,
              uint256 startTimestamp,
              uint256 endTimestamp
          );
          /**
           * @dev Revert with an error if the mint quantity exceeds the max allowed
           *      to be minted per wallet.
           */
          error MintQuantityExceedsMaxMintedPerWallet(uint256 total, uint256 allowed);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply.
           */
          error MintQuantityExceedsMaxSupply(uint256 total, uint256 maxSupply);
          /**
           * @dev Revert with an error if the mint quantity exceeds the max token
           *      supply for the stage.
           *      Note: The `maxTokenSupplyForStage` for public mint is
           *      always `type(uint).max`.
           */
          error MintQuantityExceedsMaxTokenSupplyForStage(
              uint256 total,
              uint256 maxTokenSupplyForStage
          );
          /**
           * @dev Revert if the fee recipient is the zero address.
           */
          error FeeRecipientCannotBeZeroAddress();
          /**
           * @dev Revert if the fee recipient is not already included.
           */
          error FeeRecipientNotPresent();
          /**
           * @dev Revert if the fee basis points is greater than 10_000.
           */
          error InvalidFeeBps(uint256 feeBps);
          /**
           * @dev Revert if the fee recipient is already included.
           */
          error DuplicateFeeRecipient();
          /**
           * @dev Revert if the fee recipient is restricted and not allowed.
           */
          error FeeRecipientNotAllowed(address got);
          /**
           * @dev Revert if the creator payout address is the zero address.
           */
          error CreatorPayoutAddressCannotBeZeroAddress();
          /**
           * @dev Revert if the creator payouts are not set.
           */
          error CreatorPayoutsNotSet();
          /**
           * @dev Revert if the creator payout basis points are zero.
           */
          error CreatorPayoutBasisPointsCannotBeZero();
          /**
           * @dev Revert if the total basis points for the creator payouts
           *      don't equal exactly 10_000.
           */
          error InvalidCreatorPayoutTotalBasisPoints(
              uint256 totalReceivedBasisPoints
          );
          /**
           * @dev Revert if the creator payout basis points don't add up to 10_000.
           */
          error InvalidCreatorPayoutBasisPoints(uint256 totalReceivedBasisPoints);
          /**
           * @dev Revert with an error if the allow list proof is invalid.
           */
          error InvalidProof();
          /**
           * @dev Revert if a supplied signer address is the zero address.
           */
          error SignerCannotBeZeroAddress();
          /**
           * @dev Revert with an error if a signer is not included in
           *      the enumeration when removing.
           */
          error SignerNotPresent();
          /**
           * @dev Revert with an error if a payer is not included in
           *      the enumeration when removing.
           */
          error PayerNotPresent();
          /**
           * @dev Revert with an error if a payer is already included in mapping
           *      when adding.
           */
          error DuplicatePayer();
          /**
           * @dev Revert with an error if a signer is already included in mapping
           *      when adding.
           */
          error DuplicateSigner();
          /**
           * @dev Revert with an error if the payer is not allowed. The minter must
           *      pay for their own mint.
           */
          error PayerNotAllowed(address got);
          /**
           * @dev Revert if a supplied payer address is the zero address.
           */
          error PayerCannotBeZeroAddress();
          /**
           * @dev Revert if the start time is greater than the end time.
           */
          error InvalidStartAndEndTime(uint256 startTime, uint256 endTime);
          /**
           * @dev Revert with an error if the signer payment token is not the same.
           */
          error InvalidSignedPaymentToken(address got, address want);
          /**
           * @dev Revert with an error if supplied signed mint price is less than
           *      the minimum specified.
           */
          error InvalidSignedMintPrice(
              address paymentToken,
              uint256 got,
              uint256 minimum
          );
          /**
           * @dev Revert with an error if supplied signed maxTotalMintableByWallet
           *      is greater than the maximum specified.
           */
          error InvalidSignedMaxTotalMintableByWallet(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed
           *      maxTotalMintableByWalletPerToken is greater than the maximum
           *      specified.
           */
          error InvalidSignedMaxTotalMintableByWalletPerToken(
              uint256 got,
              uint256 maximum
          );
          /**
           * @dev Revert with an error if the fromTokenId is not within range.
           */
          error InvalidSignedFromTokenId(uint256 got, uint256 minimum);
          /**
           * @dev Revert with an error if the toTokenId is not within range.
           */
          error InvalidSignedToTokenId(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed start time is less than
           *      the minimum specified.
           */
          error InvalidSignedStartTime(uint256 got, uint256 minimum);
          /**
           * @dev Revert with an error if supplied signed end time is greater than
           *      the maximum specified.
           */
          error InvalidSignedEndTime(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed maxTokenSupplyForStage
           *      is greater than the maximum specified.
           */
          error InvalidSignedMaxTokenSupplyForStage(uint256 got, uint256 maximum);
          /**
           * @dev Revert with an error if supplied signed feeBps is greater than
           *      the maximum specified, or less than the minimum.
           */
          error InvalidSignedFeeBps(uint256 got, uint256 minimumOrMaximum);
          /**
           * @dev Revert with an error if signed mint did not specify to restrict
           *      fee recipients.
           */
          error SignedMintsMustRestrictFeeRecipients();
          /**
           * @dev Revert with an error if a signature for a signed mint has already
           *      been used.
           */
          error SignatureAlreadyUsed();
          /**
           * @dev Revert with an error if the contract has no balance to withdraw.
           */
          error NoBalanceToWithdraw();
          /**
           * @dev Revert with an error if the caller is not an allowed Seaport.
           */
          error InvalidCallerOnlyAllowedSeaport(address caller);
          /**
           * @dev Revert with an error if the order does not have the ERC1155 magic
           *      consideration item to signify a consecutive mint.
           */
          error MustSpecifyERC1155ConsiderationItemForSeaDropMint();
          /**
           * @dev Revert with an error if the extra data version is not supported.
           */
          error UnsupportedExtraDataVersion(uint8 version);
          /**
           * @dev Revert with an error if the extra data encoding is not supported.
           */
          error InvalidExtraDataEncoding(uint8 version);
          /**
           * @dev Revert with an error if the provided substandard is not supported.
           */
          error InvalidSubstandard(uint8 substandard);
          /**
           * @dev Revert with an error if the implementation contract is called without
           *      delegatecall.
           */
          error OnlyDelegateCalled();
          /**
           * @dev Revert with an error if the provided allowed Seaport is the
           *      zero address.
           */
          error AllowedSeaportCannotBeZeroAddress();
          /**
           * @dev Emit an event when allowed Seaport contracts are updated.
           */
          event AllowedSeaportUpdated(address[] allowedSeaport);
          /**
           * @dev An event with details of a SeaDrop mint, for analytical purposes.
           *
           * @param payer          The address who payed for the tx.
           * @param dropStageIndex The drop stage index. Items minted through
           *                       public mint have dropStageIndex of 0
           */
          event SeaDropMint(address payer, uint256 dropStageIndex);
          /**
           * @dev An event with updated allow list data.
           *
           * @param previousMerkleRoot The previous allow list merkle root.
           * @param newMerkleRoot      The new allow list merkle root.
           * @param publicKeyURI       If the allow list is encrypted, the public key
           *                           URIs that can decrypt the list.
           *                           Empty if unencrypted.
           * @param allowListURI       The URI for the allow list.
           */
          event AllowListUpdated(
              bytes32 indexed previousMerkleRoot,
              bytes32 indexed newMerkleRoot,
              string[] publicKeyURI,
              string allowListURI
          );
          /**
           * @dev An event with updated drop URI.
           */
          event DropURIUpdated(string newDropURI);
          /**
           * @dev An event with the updated creator payout address.
           */
          event CreatorPayoutsUpdated(CreatorPayout[] creatorPayouts);
          /**
           * @dev An event with the updated allowed fee recipient.
           */
          event AllowedFeeRecipientUpdated(
              address indexed feeRecipient,
              bool indexed allowed
          );
          /**
           * @dev An event with the updated signer.
           */
          event SignerUpdated(address indexed signer, bool indexed allowed);
          /**
           * @dev An event with the updated payer.
           */
          event PayerUpdated(address indexed payer, bool indexed allowed);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      enum OrderType {
          // 0: no partial fills, anyone can execute
          FULL_OPEN,
          // 1: partial fills supported, anyone can execute
          PARTIAL_OPEN,
          // 2: no partial fills, only offerer or zone can execute
          FULL_RESTRICTED,
          // 3: partial fills supported, only offerer or zone can execute
          PARTIAL_RESTRICTED,
          // 4: contract order type
          CONTRACT
      }
      enum BasicOrderType {
          // 0: no partial fills, anyone can execute
          ETH_TO_ERC721_FULL_OPEN,
          // 1: partial fills supported, anyone can execute
          ETH_TO_ERC721_PARTIAL_OPEN,
          // 2: no partial fills, only offerer or zone can execute
          ETH_TO_ERC721_FULL_RESTRICTED,
          // 3: partial fills supported, only offerer or zone can execute
          ETH_TO_ERC721_PARTIAL_RESTRICTED,
          // 4: no partial fills, anyone can execute
          ETH_TO_ERC1155_FULL_OPEN,
          // 5: partial fills supported, anyone can execute
          ETH_TO_ERC1155_PARTIAL_OPEN,
          // 6: no partial fills, only offerer or zone can execute
          ETH_TO_ERC1155_FULL_RESTRICTED,
          // 7: partial fills supported, only offerer or zone can execute
          ETH_TO_ERC1155_PARTIAL_RESTRICTED,
          // 8: no partial fills, anyone can execute
          ERC20_TO_ERC721_FULL_OPEN,
          // 9: partial fills supported, anyone can execute
          ERC20_TO_ERC721_PARTIAL_OPEN,
          // 10: no partial fills, only offerer or zone can execute
          ERC20_TO_ERC721_FULL_RESTRICTED,
          // 11: partial fills supported, only offerer or zone can execute
          ERC20_TO_ERC721_PARTIAL_RESTRICTED,
          // 12: no partial fills, anyone can execute
          ERC20_TO_ERC1155_FULL_OPEN,
          // 13: partial fills supported, anyone can execute
          ERC20_TO_ERC1155_PARTIAL_OPEN,
          // 14: no partial fills, only offerer or zone can execute
          ERC20_TO_ERC1155_FULL_RESTRICTED,
          // 15: partial fills supported, only offerer or zone can execute
          ERC20_TO_ERC1155_PARTIAL_RESTRICTED,
          // 16: no partial fills, anyone can execute
          ERC721_TO_ERC20_FULL_OPEN,
          // 17: partial fills supported, anyone can execute
          ERC721_TO_ERC20_PARTIAL_OPEN,
          // 18: no partial fills, only offerer or zone can execute
          ERC721_TO_ERC20_FULL_RESTRICTED,
          // 19: partial fills supported, only offerer or zone can execute
          ERC721_TO_ERC20_PARTIAL_RESTRICTED,
          // 20: no partial fills, anyone can execute
          ERC1155_TO_ERC20_FULL_OPEN,
          // 21: partial fills supported, anyone can execute
          ERC1155_TO_ERC20_PARTIAL_OPEN,
          // 22: no partial fills, only offerer or zone can execute
          ERC1155_TO_ERC20_FULL_RESTRICTED,
          // 23: partial fills supported, only offerer or zone can execute
          ERC1155_TO_ERC20_PARTIAL_RESTRICTED
      }
      enum BasicOrderRouteType {
          // 0: provide Ether (or other native token) to receive offered ERC721 item.
          ETH_TO_ERC721,
          // 1: provide Ether (or other native token) to receive offered ERC1155 item.
          ETH_TO_ERC1155,
          // 2: provide ERC20 item to receive offered ERC721 item.
          ERC20_TO_ERC721,
          // 3: provide ERC20 item to receive offered ERC1155 item.
          ERC20_TO_ERC1155,
          // 4: provide ERC721 item to receive offered ERC20 item.
          ERC721_TO_ERC20,
          // 5: provide ERC1155 item to receive offered ERC20 item.
          ERC1155_TO_ERC20
      }
      enum ItemType {
          // 0: ETH on mainnet, MATIC on polygon, etc.
          NATIVE,
          // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work)
          ERC20,
          // 2: ERC721 items
          ERC721,
          // 3: ERC1155 items
          ERC1155,
          // 4: ERC721 items where a number of tokenIds are supported
          ERC721_WITH_CRITERIA,
          // 5: ERC1155 items where a number of ids are supported
          ERC1155_WITH_CRITERIA
      }
      enum Side {
          // 0: Items that can be spent
          OFFER,
          // 1: Items that must be received
          CONSIDERATION
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.13;
      type CalldataPointer is uint256;
      type ReturndataPointer is uint256;
      type MemoryPointer is uint256;
      using CalldataPointerLib for CalldataPointer global;
      using MemoryPointerLib for MemoryPointer global;
      using ReturndataPointerLib for ReturndataPointer global;
      using CalldataReaders for CalldataPointer global;
      using ReturndataReaders for ReturndataPointer global;
      using MemoryReaders for MemoryPointer global;
      using MemoryWriters for MemoryPointer global;
      CalldataPointer constant CalldataStart = CalldataPointer.wrap(0x04);
      MemoryPointer constant FreeMemoryPPtr = MemoryPointer.wrap(0x40);
      uint256 constant IdentityPrecompileAddress = 0x4;
      uint256 constant OffsetOrLengthMask = 0xffffffff;
      uint256 constant _OneWord = 0x20;
      uint256 constant _FreeMemoryPointerSlot = 0x40;
      /// @dev Allocates `size` bytes in memory by increasing the free memory pointer
      ///    and returns the memory pointer to the first byte of the allocated region.
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function malloc(uint256 size) pure returns (MemoryPointer mPtr) {
          assembly {
              mPtr := mload(_FreeMemoryPointerSlot)
              mstore(_FreeMemoryPointerSlot, add(mPtr, size))
          }
      }
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function getFreeMemoryPointer() pure returns (MemoryPointer mPtr) {
          mPtr = FreeMemoryPPtr.readMemoryPointer();
      }
      // (Free functions cannot have visibility.)
      // solhint-disable-next-line func-visibility
      function setFreeMemoryPointer(MemoryPointer mPtr) pure {
          FreeMemoryPPtr.write(mPtr);
      }
      library CalldataPointerLib {
          function lt(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              CalldataPointer a,
              CalldataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(CalldataPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          /// @dev Resolves an offset stored at `cdPtr + headOffset` to a calldata.
          ///      pointer `cdPtr` must point to some parent object with a dynamic
          ///      type's head stored at `cdPtr + headOffset`.
          function pptr(
              CalldataPointer cdPtr,
              uint256 headOffset
          ) internal pure returns (CalldataPointer cdPtrChild) {
              cdPtrChild = cdPtr.offset(
                  cdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
              );
          }
          /// @dev Resolves an offset stored at `cdPtr` to a calldata pointer.
          ///      `cdPtr` must point to some parent object with a dynamic type as its
          ///      first member, e.g. `struct { bytes data; }`
          function pptr(
              CalldataPointer cdPtr
          ) internal pure returns (CalldataPointer cdPtrChild) {
              cdPtrChild = cdPtr.offset(cdPtr.readUint256() & OffsetOrLengthMask);
          }
          /// @dev Returns the calldata pointer one word after `cdPtr`.
          function next(
              CalldataPointer cdPtr
          ) internal pure returns (CalldataPointer cdPtrNext) {
              assembly {
                  cdPtrNext := add(cdPtr, _OneWord)
              }
          }
          /// @dev Returns the calldata pointer `_offset` bytes after `cdPtr`.
          function offset(
              CalldataPointer cdPtr,
              uint256 _offset
          ) internal pure returns (CalldataPointer cdPtrNext) {
              assembly {
                  cdPtrNext := add(cdPtr, _offset)
              }
          }
          /// @dev Copies `size` bytes from calldata starting at `src` to memory at
          ///      `dst`.
          function copy(
              CalldataPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal pure {
              assembly {
                  calldatacopy(dst, src, size)
              }
          }
      }
      library ReturndataPointerLib {
          function lt(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              ReturndataPointer a,
              ReturndataPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(ReturndataPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          /// @dev Resolves an offset stored at `rdPtr + headOffset` to a returndata
          ///      pointer. `rdPtr` must point to some parent object with a dynamic
          ///      type's head stored at `rdPtr + headOffset`.
          function pptr(
              ReturndataPointer rdPtr,
              uint256 headOffset
          ) internal pure returns (ReturndataPointer rdPtrChild) {
              rdPtrChild = rdPtr.offset(
                  rdPtr.offset(headOffset).readUint256() & OffsetOrLengthMask
              );
          }
          /// @dev Resolves an offset stored at `rdPtr` to a returndata pointer.
          ///    `rdPtr` must point to some parent object with a dynamic type as its
          ///    first member, e.g. `struct { bytes data; }`
          function pptr(
              ReturndataPointer rdPtr
          ) internal pure returns (ReturndataPointer rdPtrChild) {
              rdPtrChild = rdPtr.offset(rdPtr.readUint256() & OffsetOrLengthMask);
          }
          /// @dev Returns the returndata pointer one word after `cdPtr`.
          function next(
              ReturndataPointer rdPtr
          ) internal pure returns (ReturndataPointer rdPtrNext) {
              assembly {
                  rdPtrNext := add(rdPtr, _OneWord)
              }
          }
          /// @dev Returns the returndata pointer `_offset` bytes after `cdPtr`.
          function offset(
              ReturndataPointer rdPtr,
              uint256 _offset
          ) internal pure returns (ReturndataPointer rdPtrNext) {
              assembly {
                  rdPtrNext := add(rdPtr, _offset)
              }
          }
          /// @dev Copies `size` bytes from returndata starting at `src` to memory at
          /// `dst`.
          function copy(
              ReturndataPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal pure {
              assembly {
                  returndatacopy(dst, src, size)
              }
          }
      }
      library MemoryPointerLib {
          function copy(
              MemoryPointer src,
              MemoryPointer dst,
              uint256 size
          ) internal view {
              assembly {
                  let success := staticcall(
                      gas(),
                      IdentityPrecompileAddress,
                      src,
                      size,
                      dst,
                      size
                  )
                  if or(iszero(returndatasize()), iszero(success)) {
                      revert(0, 0)
                  }
              }
          }
          function lt(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := lt(a, b)
              }
          }
          function gt(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := gt(a, b)
              }
          }
          function eq(
              MemoryPointer a,
              MemoryPointer b
          ) internal pure returns (bool c) {
              assembly {
                  c := eq(a, b)
              }
          }
          function isNull(MemoryPointer a) internal pure returns (bool b) {
              assembly {
                  b := iszero(a)
              }
          }
          function hash(
              MemoryPointer ptr,
              uint256 length
          ) internal pure returns (bytes32 _hash) {
              assembly {
                  _hash := keccak256(ptr, length)
              }
          }
          /// @dev Returns the memory pointer one word after `mPtr`.
          function next(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer mPtrNext) {
              assembly {
                  mPtrNext := add(mPtr, _OneWord)
              }
          }
          /// @dev Returns the memory pointer `_offset` bytes after `mPtr`.
          function offset(
              MemoryPointer mPtr,
              uint256 _offset
          ) internal pure returns (MemoryPointer mPtrNext) {
              assembly {
                  mPtrNext := add(mPtr, _offset)
              }
          }
          /// @dev Resolves a pointer at `mPtr + headOffset` to a memory
          ///    pointer. `mPtr` must point to some parent object with a dynamic
          ///    type's pointer stored at `mPtr + headOffset`.
          function pptr(
              MemoryPointer mPtr,
              uint256 headOffset
          ) internal pure returns (MemoryPointer mPtrChild) {
              mPtrChild = mPtr.offset(headOffset).readMemoryPointer();
          }
          /// @dev Resolves a pointer stored at `mPtr` to a memory pointer.
          ///    `mPtr` must point to some parent object with a dynamic type as its
          ///    first member, e.g. `struct { bytes data; }`
          function pptr(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer mPtrChild) {
              mPtrChild = mPtr.readMemoryPointer();
          }
      }
      library CalldataReaders {
          /// @dev Reads the value at `cdPtr` and applies a mask to return only the
          ///    last 4 bytes.
          function readMaskedUint256(
              CalldataPointer cdPtr
          ) internal pure returns (uint256 value) {
              value = cdPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `cdPtr` in calldata.
          function readBool(
              CalldataPointer cdPtr
          ) internal pure returns (bool value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the address at `cdPtr` in calldata.
          function readAddress(
              CalldataPointer cdPtr
          ) internal pure returns (address value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes1 at `cdPtr` in calldata.
          function readBytes1(
              CalldataPointer cdPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes2 at `cdPtr` in calldata.
          function readBytes2(
              CalldataPointer cdPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes3 at `cdPtr` in calldata.
          function readBytes3(
              CalldataPointer cdPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes4 at `cdPtr` in calldata.
          function readBytes4(
              CalldataPointer cdPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes5 at `cdPtr` in calldata.
          function readBytes5(
              CalldataPointer cdPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes6 at `cdPtr` in calldata.
          function readBytes6(
              CalldataPointer cdPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes7 at `cdPtr` in calldata.
          function readBytes7(
              CalldataPointer cdPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes8 at `cdPtr` in calldata.
          function readBytes8(
              CalldataPointer cdPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes9 at `cdPtr` in calldata.
          function readBytes9(
              CalldataPointer cdPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes10 at `cdPtr` in calldata.
          function readBytes10(
              CalldataPointer cdPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes11 at `cdPtr` in calldata.
          function readBytes11(
              CalldataPointer cdPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes12 at `cdPtr` in calldata.
          function readBytes12(
              CalldataPointer cdPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes13 at `cdPtr` in calldata.
          function readBytes13(
              CalldataPointer cdPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes14 at `cdPtr` in calldata.
          function readBytes14(
              CalldataPointer cdPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes15 at `cdPtr` in calldata.
          function readBytes15(
              CalldataPointer cdPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes16 at `cdPtr` in calldata.
          function readBytes16(
              CalldataPointer cdPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes17 at `cdPtr` in calldata.
          function readBytes17(
              CalldataPointer cdPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes18 at `cdPtr` in calldata.
          function readBytes18(
              CalldataPointer cdPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes19 at `cdPtr` in calldata.
          function readBytes19(
              CalldataPointer cdPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes20 at `cdPtr` in calldata.
          function readBytes20(
              CalldataPointer cdPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes21 at `cdPtr` in calldata.
          function readBytes21(
              CalldataPointer cdPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes22 at `cdPtr` in calldata.
          function readBytes22(
              CalldataPointer cdPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes23 at `cdPtr` in calldata.
          function readBytes23(
              CalldataPointer cdPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes24 at `cdPtr` in calldata.
          function readBytes24(
              CalldataPointer cdPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes25 at `cdPtr` in calldata.
          function readBytes25(
              CalldataPointer cdPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes26 at `cdPtr` in calldata.
          function readBytes26(
              CalldataPointer cdPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes27 at `cdPtr` in calldata.
          function readBytes27(
              CalldataPointer cdPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes28 at `cdPtr` in calldata.
          function readBytes28(
              CalldataPointer cdPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes29 at `cdPtr` in calldata.
          function readBytes29(
              CalldataPointer cdPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes30 at `cdPtr` in calldata.
          function readBytes30(
              CalldataPointer cdPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes31 at `cdPtr` in calldata.
          function readBytes31(
              CalldataPointer cdPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the bytes32 at `cdPtr` in calldata.
          function readBytes32(
              CalldataPointer cdPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint8 at `cdPtr` in calldata.
          function readUint8(
              CalldataPointer cdPtr
          ) internal pure returns (uint8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint16 at `cdPtr` in calldata.
          function readUint16(
              CalldataPointer cdPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint24 at `cdPtr` in calldata.
          function readUint24(
              CalldataPointer cdPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint32 at `cdPtr` in calldata.
          function readUint32(
              CalldataPointer cdPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint40 at `cdPtr` in calldata.
          function readUint40(
              CalldataPointer cdPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint48 at `cdPtr` in calldata.
          function readUint48(
              CalldataPointer cdPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint56 at `cdPtr` in calldata.
          function readUint56(
              CalldataPointer cdPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint64 at `cdPtr` in calldata.
          function readUint64(
              CalldataPointer cdPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint72 at `cdPtr` in calldata.
          function readUint72(
              CalldataPointer cdPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint80 at `cdPtr` in calldata.
          function readUint80(
              CalldataPointer cdPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint88 at `cdPtr` in calldata.
          function readUint88(
              CalldataPointer cdPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint96 at `cdPtr` in calldata.
          function readUint96(
              CalldataPointer cdPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint104 at `cdPtr` in calldata.
          function readUint104(
              CalldataPointer cdPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint112 at `cdPtr` in calldata.
          function readUint112(
              CalldataPointer cdPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint120 at `cdPtr` in calldata.
          function readUint120(
              CalldataPointer cdPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint128 at `cdPtr` in calldata.
          function readUint128(
              CalldataPointer cdPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint136 at `cdPtr` in calldata.
          function readUint136(
              CalldataPointer cdPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint144 at `cdPtr` in calldata.
          function readUint144(
              CalldataPointer cdPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint152 at `cdPtr` in calldata.
          function readUint152(
              CalldataPointer cdPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint160 at `cdPtr` in calldata.
          function readUint160(
              CalldataPointer cdPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint168 at `cdPtr` in calldata.
          function readUint168(
              CalldataPointer cdPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint176 at `cdPtr` in calldata.
          function readUint176(
              CalldataPointer cdPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint184 at `cdPtr` in calldata.
          function readUint184(
              CalldataPointer cdPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint192 at `cdPtr` in calldata.
          function readUint192(
              CalldataPointer cdPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint200 at `cdPtr` in calldata.
          function readUint200(
              CalldataPointer cdPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint208 at `cdPtr` in calldata.
          function readUint208(
              CalldataPointer cdPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint216 at `cdPtr` in calldata.
          function readUint216(
              CalldataPointer cdPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint224 at `cdPtr` in calldata.
          function readUint224(
              CalldataPointer cdPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint232 at `cdPtr` in calldata.
          function readUint232(
              CalldataPointer cdPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint240 at `cdPtr` in calldata.
          function readUint240(
              CalldataPointer cdPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint248 at `cdPtr` in calldata.
          function readUint248(
              CalldataPointer cdPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the uint256 at `cdPtr` in calldata.
          function readUint256(
              CalldataPointer cdPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int8 at `cdPtr` in calldata.
          function readInt8(
              CalldataPointer cdPtr
          ) internal pure returns (int8 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int16 at `cdPtr` in calldata.
          function readInt16(
              CalldataPointer cdPtr
          ) internal pure returns (int16 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int24 at `cdPtr` in calldata.
          function readInt24(
              CalldataPointer cdPtr
          ) internal pure returns (int24 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int32 at `cdPtr` in calldata.
          function readInt32(
              CalldataPointer cdPtr
          ) internal pure returns (int32 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int40 at `cdPtr` in calldata.
          function readInt40(
              CalldataPointer cdPtr
          ) internal pure returns (int40 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int48 at `cdPtr` in calldata.
          function readInt48(
              CalldataPointer cdPtr
          ) internal pure returns (int48 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int56 at `cdPtr` in calldata.
          function readInt56(
              CalldataPointer cdPtr
          ) internal pure returns (int56 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int64 at `cdPtr` in calldata.
          function readInt64(
              CalldataPointer cdPtr
          ) internal pure returns (int64 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int72 at `cdPtr` in calldata.
          function readInt72(
              CalldataPointer cdPtr
          ) internal pure returns (int72 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int80 at `cdPtr` in calldata.
          function readInt80(
              CalldataPointer cdPtr
          ) internal pure returns (int80 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int88 at `cdPtr` in calldata.
          function readInt88(
              CalldataPointer cdPtr
          ) internal pure returns (int88 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int96 at `cdPtr` in calldata.
          function readInt96(
              CalldataPointer cdPtr
          ) internal pure returns (int96 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int104 at `cdPtr` in calldata.
          function readInt104(
              CalldataPointer cdPtr
          ) internal pure returns (int104 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int112 at `cdPtr` in calldata.
          function readInt112(
              CalldataPointer cdPtr
          ) internal pure returns (int112 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int120 at `cdPtr` in calldata.
          function readInt120(
              CalldataPointer cdPtr
          ) internal pure returns (int120 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int128 at `cdPtr` in calldata.
          function readInt128(
              CalldataPointer cdPtr
          ) internal pure returns (int128 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int136 at `cdPtr` in calldata.
          function readInt136(
              CalldataPointer cdPtr
          ) internal pure returns (int136 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int144 at `cdPtr` in calldata.
          function readInt144(
              CalldataPointer cdPtr
          ) internal pure returns (int144 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int152 at `cdPtr` in calldata.
          function readInt152(
              CalldataPointer cdPtr
          ) internal pure returns (int152 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int160 at `cdPtr` in calldata.
          function readInt160(
              CalldataPointer cdPtr
          ) internal pure returns (int160 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int168 at `cdPtr` in calldata.
          function readInt168(
              CalldataPointer cdPtr
          ) internal pure returns (int168 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int176 at `cdPtr` in calldata.
          function readInt176(
              CalldataPointer cdPtr
          ) internal pure returns (int176 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int184 at `cdPtr` in calldata.
          function readInt184(
              CalldataPointer cdPtr
          ) internal pure returns (int184 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int192 at `cdPtr` in calldata.
          function readInt192(
              CalldataPointer cdPtr
          ) internal pure returns (int192 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int200 at `cdPtr` in calldata.
          function readInt200(
              CalldataPointer cdPtr
          ) internal pure returns (int200 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int208 at `cdPtr` in calldata.
          function readInt208(
              CalldataPointer cdPtr
          ) internal pure returns (int208 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int216 at `cdPtr` in calldata.
          function readInt216(
              CalldataPointer cdPtr
          ) internal pure returns (int216 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int224 at `cdPtr` in calldata.
          function readInt224(
              CalldataPointer cdPtr
          ) internal pure returns (int224 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int232 at `cdPtr` in calldata.
          function readInt232(
              CalldataPointer cdPtr
          ) internal pure returns (int232 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int240 at `cdPtr` in calldata.
          function readInt240(
              CalldataPointer cdPtr
          ) internal pure returns (int240 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int248 at `cdPtr` in calldata.
          function readInt248(
              CalldataPointer cdPtr
          ) internal pure returns (int248 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
          /// @dev Reads the int256 at `cdPtr` in calldata.
          function readInt256(
              CalldataPointer cdPtr
          ) internal pure returns (int256 value) {
              assembly {
                  value := calldataload(cdPtr)
              }
          }
      }
      library ReturndataReaders {
          /// @dev Reads value at `rdPtr` & applies a mask to return only last 4 bytes
          function readMaskedUint256(
              ReturndataPointer rdPtr
          ) internal pure returns (uint256 value) {
              value = rdPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `rdPtr` in returndata.
          function readBool(
              ReturndataPointer rdPtr
          ) internal pure returns (bool value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the address at `rdPtr` in returndata.
          function readAddress(
              ReturndataPointer rdPtr
          ) internal pure returns (address value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes1 at `rdPtr` in returndata.
          function readBytes1(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes2 at `rdPtr` in returndata.
          function readBytes2(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes3 at `rdPtr` in returndata.
          function readBytes3(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes4 at `rdPtr` in returndata.
          function readBytes4(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes5 at `rdPtr` in returndata.
          function readBytes5(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes6 at `rdPtr` in returndata.
          function readBytes6(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes7 at `rdPtr` in returndata.
          function readBytes7(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes8 at `rdPtr` in returndata.
          function readBytes8(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes9 at `rdPtr` in returndata.
          function readBytes9(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes10 at `rdPtr` in returndata.
          function readBytes10(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes11 at `rdPtr` in returndata.
          function readBytes11(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes12 at `rdPtr` in returndata.
          function readBytes12(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes13 at `rdPtr` in returndata.
          function readBytes13(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes14 at `rdPtr` in returndata.
          function readBytes14(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes15 at `rdPtr` in returndata.
          function readBytes15(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes16 at `rdPtr` in returndata.
          function readBytes16(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes17 at `rdPtr` in returndata.
          function readBytes17(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes18 at `rdPtr` in returndata.
          function readBytes18(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes19 at `rdPtr` in returndata.
          function readBytes19(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes20 at `rdPtr` in returndata.
          function readBytes20(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes21 at `rdPtr` in returndata.
          function readBytes21(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes22 at `rdPtr` in returndata.
          function readBytes22(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes23 at `rdPtr` in returndata.
          function readBytes23(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes24 at `rdPtr` in returndata.
          function readBytes24(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes25 at `rdPtr` in returndata.
          function readBytes25(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes26 at `rdPtr` in returndata.
          function readBytes26(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes27 at `rdPtr` in returndata.
          function readBytes27(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes28 at `rdPtr` in returndata.
          function readBytes28(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes29 at `rdPtr` in returndata.
          function readBytes29(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes30 at `rdPtr` in returndata.
          function readBytes30(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes31 at `rdPtr` in returndata.
          function readBytes31(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the bytes32 at `rdPtr` in returndata.
          function readBytes32(
              ReturndataPointer rdPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint8 at `rdPtr` in returndata.
          function readUint8(
              ReturndataPointer rdPtr
          ) internal pure returns (uint8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint16 at `rdPtr` in returndata.
          function readUint16(
              ReturndataPointer rdPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint24 at `rdPtr` in returndata.
          function readUint24(
              ReturndataPointer rdPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint32 at `rdPtr` in returndata.
          function readUint32(
              ReturndataPointer rdPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint40 at `rdPtr` in returndata.
          function readUint40(
              ReturndataPointer rdPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint48 at `rdPtr` in returndata.
          function readUint48(
              ReturndataPointer rdPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint56 at `rdPtr` in returndata.
          function readUint56(
              ReturndataPointer rdPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint64 at `rdPtr` in returndata.
          function readUint64(
              ReturndataPointer rdPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint72 at `rdPtr` in returndata.
          function readUint72(
              ReturndataPointer rdPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint80 at `rdPtr` in returndata.
          function readUint80(
              ReturndataPointer rdPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint88 at `rdPtr` in returndata.
          function readUint88(
              ReturndataPointer rdPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint96 at `rdPtr` in returndata.
          function readUint96(
              ReturndataPointer rdPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint104 at `rdPtr` in returndata.
          function readUint104(
              ReturndataPointer rdPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint112 at `rdPtr` in returndata.
          function readUint112(
              ReturndataPointer rdPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint120 at `rdPtr` in returndata.
          function readUint120(
              ReturndataPointer rdPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint128 at `rdPtr` in returndata.
          function readUint128(
              ReturndataPointer rdPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint136 at `rdPtr` in returndata.
          function readUint136(
              ReturndataPointer rdPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint144 at `rdPtr` in returndata.
          function readUint144(
              ReturndataPointer rdPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint152 at `rdPtr` in returndata.
          function readUint152(
              ReturndataPointer rdPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint160 at `rdPtr` in returndata.
          function readUint160(
              ReturndataPointer rdPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint168 at `rdPtr` in returndata.
          function readUint168(
              ReturndataPointer rdPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint176 at `rdPtr` in returndata.
          function readUint176(
              ReturndataPointer rdPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint184 at `rdPtr` in returndata.
          function readUint184(
              ReturndataPointer rdPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint192 at `rdPtr` in returndata.
          function readUint192(
              ReturndataPointer rdPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint200 at `rdPtr` in returndata.
          function readUint200(
              ReturndataPointer rdPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint208 at `rdPtr` in returndata.
          function readUint208(
              ReturndataPointer rdPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint216 at `rdPtr` in returndata.
          function readUint216(
              ReturndataPointer rdPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint224 at `rdPtr` in returndata.
          function readUint224(
              ReturndataPointer rdPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint232 at `rdPtr` in returndata.
          function readUint232(
              ReturndataPointer rdPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint240 at `rdPtr` in returndata.
          function readUint240(
              ReturndataPointer rdPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint248 at `rdPtr` in returndata.
          function readUint248(
              ReturndataPointer rdPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the uint256 at `rdPtr` in returndata.
          function readUint256(
              ReturndataPointer rdPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int8 at `rdPtr` in returndata.
          function readInt8(
              ReturndataPointer rdPtr
          ) internal pure returns (int8 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int16 at `rdPtr` in returndata.
          function readInt16(
              ReturndataPointer rdPtr
          ) internal pure returns (int16 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int24 at `rdPtr` in returndata.
          function readInt24(
              ReturndataPointer rdPtr
          ) internal pure returns (int24 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int32 at `rdPtr` in returndata.
          function readInt32(
              ReturndataPointer rdPtr
          ) internal pure returns (int32 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int40 at `rdPtr` in returndata.
          function readInt40(
              ReturndataPointer rdPtr
          ) internal pure returns (int40 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int48 at `rdPtr` in returndata.
          function readInt48(
              ReturndataPointer rdPtr
          ) internal pure returns (int48 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int56 at `rdPtr` in returndata.
          function readInt56(
              ReturndataPointer rdPtr
          ) internal pure returns (int56 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int64 at `rdPtr` in returndata.
          function readInt64(
              ReturndataPointer rdPtr
          ) internal pure returns (int64 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int72 at `rdPtr` in returndata.
          function readInt72(
              ReturndataPointer rdPtr
          ) internal pure returns (int72 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int80 at `rdPtr` in returndata.
          function readInt80(
              ReturndataPointer rdPtr
          ) internal pure returns (int80 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int88 at `rdPtr` in returndata.
          function readInt88(
              ReturndataPointer rdPtr
          ) internal pure returns (int88 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int96 at `rdPtr` in returndata.
          function readInt96(
              ReturndataPointer rdPtr
          ) internal pure returns (int96 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int104 at `rdPtr` in returndata.
          function readInt104(
              ReturndataPointer rdPtr
          ) internal pure returns (int104 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int112 at `rdPtr` in returndata.
          function readInt112(
              ReturndataPointer rdPtr
          ) internal pure returns (int112 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int120 at `rdPtr` in returndata.
          function readInt120(
              ReturndataPointer rdPtr
          ) internal pure returns (int120 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int128 at `rdPtr` in returndata.
          function readInt128(
              ReturndataPointer rdPtr
          ) internal pure returns (int128 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int136 at `rdPtr` in returndata.
          function readInt136(
              ReturndataPointer rdPtr
          ) internal pure returns (int136 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int144 at `rdPtr` in returndata.
          function readInt144(
              ReturndataPointer rdPtr
          ) internal pure returns (int144 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int152 at `rdPtr` in returndata.
          function readInt152(
              ReturndataPointer rdPtr
          ) internal pure returns (int152 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int160 at `rdPtr` in returndata.
          function readInt160(
              ReturndataPointer rdPtr
          ) internal pure returns (int160 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int168 at `rdPtr` in returndata.
          function readInt168(
              ReturndataPointer rdPtr
          ) internal pure returns (int168 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int176 at `rdPtr` in returndata.
          function readInt176(
              ReturndataPointer rdPtr
          ) internal pure returns (int176 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int184 at `rdPtr` in returndata.
          function readInt184(
              ReturndataPointer rdPtr
          ) internal pure returns (int184 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int192 at `rdPtr` in returndata.
          function readInt192(
              ReturndataPointer rdPtr
          ) internal pure returns (int192 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int200 at `rdPtr` in returndata.
          function readInt200(
              ReturndataPointer rdPtr
          ) internal pure returns (int200 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int208 at `rdPtr` in returndata.
          function readInt208(
              ReturndataPointer rdPtr
          ) internal pure returns (int208 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int216 at `rdPtr` in returndata.
          function readInt216(
              ReturndataPointer rdPtr
          ) internal pure returns (int216 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int224 at `rdPtr` in returndata.
          function readInt224(
              ReturndataPointer rdPtr
          ) internal pure returns (int224 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int232 at `rdPtr` in returndata.
          function readInt232(
              ReturndataPointer rdPtr
          ) internal pure returns (int232 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int240 at `rdPtr` in returndata.
          function readInt240(
              ReturndataPointer rdPtr
          ) internal pure returns (int240 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int248 at `rdPtr` in returndata.
          function readInt248(
              ReturndataPointer rdPtr
          ) internal pure returns (int248 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
          /// @dev Reads the int256 at `rdPtr` in returndata.
          function readInt256(
              ReturndataPointer rdPtr
          ) internal pure returns (int256 value) {
              assembly {
                  returndatacopy(0, rdPtr, _OneWord)
                  value := mload(0)
              }
          }
      }
      library MemoryReaders {
          /// @dev Reads the memory pointer at `mPtr` in memory.
          function readMemoryPointer(
              MemoryPointer mPtr
          ) internal pure returns (MemoryPointer value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads value at `mPtr` & applies a mask to return only last 4 bytes
          function readMaskedUint256(
              MemoryPointer mPtr
          ) internal pure returns (uint256 value) {
              value = mPtr.readUint256() & OffsetOrLengthMask;
          }
          /// @dev Reads the bool at `mPtr` in memory.
          function readBool(MemoryPointer mPtr) internal pure returns (bool value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the address at `mPtr` in memory.
          function readAddress(
              MemoryPointer mPtr
          ) internal pure returns (address value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes1 at `mPtr` in memory.
          function readBytes1(
              MemoryPointer mPtr
          ) internal pure returns (bytes1 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes2 at `mPtr` in memory.
          function readBytes2(
              MemoryPointer mPtr
          ) internal pure returns (bytes2 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes3 at `mPtr` in memory.
          function readBytes3(
              MemoryPointer mPtr
          ) internal pure returns (bytes3 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes4 at `mPtr` in memory.
          function readBytes4(
              MemoryPointer mPtr
          ) internal pure returns (bytes4 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes5 at `mPtr` in memory.
          function readBytes5(
              MemoryPointer mPtr
          ) internal pure returns (bytes5 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes6 at `mPtr` in memory.
          function readBytes6(
              MemoryPointer mPtr
          ) internal pure returns (bytes6 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes7 at `mPtr` in memory.
          function readBytes7(
              MemoryPointer mPtr
          ) internal pure returns (bytes7 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes8 at `mPtr` in memory.
          function readBytes8(
              MemoryPointer mPtr
          ) internal pure returns (bytes8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes9 at `mPtr` in memory.
          function readBytes9(
              MemoryPointer mPtr
          ) internal pure returns (bytes9 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes10 at `mPtr` in memory.
          function readBytes10(
              MemoryPointer mPtr
          ) internal pure returns (bytes10 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes11 at `mPtr` in memory.
          function readBytes11(
              MemoryPointer mPtr
          ) internal pure returns (bytes11 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes12 at `mPtr` in memory.
          function readBytes12(
              MemoryPointer mPtr
          ) internal pure returns (bytes12 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes13 at `mPtr` in memory.
          function readBytes13(
              MemoryPointer mPtr
          ) internal pure returns (bytes13 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes14 at `mPtr` in memory.
          function readBytes14(
              MemoryPointer mPtr
          ) internal pure returns (bytes14 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes15 at `mPtr` in memory.
          function readBytes15(
              MemoryPointer mPtr
          ) internal pure returns (bytes15 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes16 at `mPtr` in memory.
          function readBytes16(
              MemoryPointer mPtr
          ) internal pure returns (bytes16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes17 at `mPtr` in memory.
          function readBytes17(
              MemoryPointer mPtr
          ) internal pure returns (bytes17 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes18 at `mPtr` in memory.
          function readBytes18(
              MemoryPointer mPtr
          ) internal pure returns (bytes18 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes19 at `mPtr` in memory.
          function readBytes19(
              MemoryPointer mPtr
          ) internal pure returns (bytes19 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes20 at `mPtr` in memory.
          function readBytes20(
              MemoryPointer mPtr
          ) internal pure returns (bytes20 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes21 at `mPtr` in memory.
          function readBytes21(
              MemoryPointer mPtr
          ) internal pure returns (bytes21 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes22 at `mPtr` in memory.
          function readBytes22(
              MemoryPointer mPtr
          ) internal pure returns (bytes22 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes23 at `mPtr` in memory.
          function readBytes23(
              MemoryPointer mPtr
          ) internal pure returns (bytes23 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes24 at `mPtr` in memory.
          function readBytes24(
              MemoryPointer mPtr
          ) internal pure returns (bytes24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes25 at `mPtr` in memory.
          function readBytes25(
              MemoryPointer mPtr
          ) internal pure returns (bytes25 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes26 at `mPtr` in memory.
          function readBytes26(
              MemoryPointer mPtr
          ) internal pure returns (bytes26 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes27 at `mPtr` in memory.
          function readBytes27(
              MemoryPointer mPtr
          ) internal pure returns (bytes27 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes28 at `mPtr` in memory.
          function readBytes28(
              MemoryPointer mPtr
          ) internal pure returns (bytes28 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes29 at `mPtr` in memory.
          function readBytes29(
              MemoryPointer mPtr
          ) internal pure returns (bytes29 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes30 at `mPtr` in memory.
          function readBytes30(
              MemoryPointer mPtr
          ) internal pure returns (bytes30 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes31 at `mPtr` in memory.
          function readBytes31(
              MemoryPointer mPtr
          ) internal pure returns (bytes31 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the bytes32 at `mPtr` in memory.
          function readBytes32(
              MemoryPointer mPtr
          ) internal pure returns (bytes32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint8 at `mPtr` in memory.
          function readUint8(MemoryPointer mPtr) internal pure returns (uint8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint16 at `mPtr` in memory.
          function readUint16(
              MemoryPointer mPtr
          ) internal pure returns (uint16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint24 at `mPtr` in memory.
          function readUint24(
              MemoryPointer mPtr
          ) internal pure returns (uint24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint32 at `mPtr` in memory.
          function readUint32(
              MemoryPointer mPtr
          ) internal pure returns (uint32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint40 at `mPtr` in memory.
          function readUint40(
              MemoryPointer mPtr
          ) internal pure returns (uint40 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint48 at `mPtr` in memory.
          function readUint48(
              MemoryPointer mPtr
          ) internal pure returns (uint48 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint56 at `mPtr` in memory.
          function readUint56(
              MemoryPointer mPtr
          ) internal pure returns (uint56 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint64 at `mPtr` in memory.
          function readUint64(
              MemoryPointer mPtr
          ) internal pure returns (uint64 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint72 at `mPtr` in memory.
          function readUint72(
              MemoryPointer mPtr
          ) internal pure returns (uint72 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint80 at `mPtr` in memory.
          function readUint80(
              MemoryPointer mPtr
          ) internal pure returns (uint80 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint88 at `mPtr` in memory.
          function readUint88(
              MemoryPointer mPtr
          ) internal pure returns (uint88 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint96 at `mPtr` in memory.
          function readUint96(
              MemoryPointer mPtr
          ) internal pure returns (uint96 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint104 at `mPtr` in memory.
          function readUint104(
              MemoryPointer mPtr
          ) internal pure returns (uint104 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint112 at `mPtr` in memory.
          function readUint112(
              MemoryPointer mPtr
          ) internal pure returns (uint112 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint120 at `mPtr` in memory.
          function readUint120(
              MemoryPointer mPtr
          ) internal pure returns (uint120 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint128 at `mPtr` in memory.
          function readUint128(
              MemoryPointer mPtr
          ) internal pure returns (uint128 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint136 at `mPtr` in memory.
          function readUint136(
              MemoryPointer mPtr
          ) internal pure returns (uint136 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint144 at `mPtr` in memory.
          function readUint144(
              MemoryPointer mPtr
          ) internal pure returns (uint144 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint152 at `mPtr` in memory.
          function readUint152(
              MemoryPointer mPtr
          ) internal pure returns (uint152 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint160 at `mPtr` in memory.
          function readUint160(
              MemoryPointer mPtr
          ) internal pure returns (uint160 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint168 at `mPtr` in memory.
          function readUint168(
              MemoryPointer mPtr
          ) internal pure returns (uint168 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint176 at `mPtr` in memory.
          function readUint176(
              MemoryPointer mPtr
          ) internal pure returns (uint176 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint184 at `mPtr` in memory.
          function readUint184(
              MemoryPointer mPtr
          ) internal pure returns (uint184 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint192 at `mPtr` in memory.
          function readUint192(
              MemoryPointer mPtr
          ) internal pure returns (uint192 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint200 at `mPtr` in memory.
          function readUint200(
              MemoryPointer mPtr
          ) internal pure returns (uint200 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint208 at `mPtr` in memory.
          function readUint208(
              MemoryPointer mPtr
          ) internal pure returns (uint208 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint216 at `mPtr` in memory.
          function readUint216(
              MemoryPointer mPtr
          ) internal pure returns (uint216 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint224 at `mPtr` in memory.
          function readUint224(
              MemoryPointer mPtr
          ) internal pure returns (uint224 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint232 at `mPtr` in memory.
          function readUint232(
              MemoryPointer mPtr
          ) internal pure returns (uint232 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint240 at `mPtr` in memory.
          function readUint240(
              MemoryPointer mPtr
          ) internal pure returns (uint240 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint248 at `mPtr` in memory.
          function readUint248(
              MemoryPointer mPtr
          ) internal pure returns (uint248 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the uint256 at `mPtr` in memory.
          function readUint256(
              MemoryPointer mPtr
          ) internal pure returns (uint256 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int8 at `mPtr` in memory.
          function readInt8(MemoryPointer mPtr) internal pure returns (int8 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int16 at `mPtr` in memory.
          function readInt16(MemoryPointer mPtr) internal pure returns (int16 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int24 at `mPtr` in memory.
          function readInt24(MemoryPointer mPtr) internal pure returns (int24 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int32 at `mPtr` in memory.
          function readInt32(MemoryPointer mPtr) internal pure returns (int32 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int40 at `mPtr` in memory.
          function readInt40(MemoryPointer mPtr) internal pure returns (int40 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int48 at `mPtr` in memory.
          function readInt48(MemoryPointer mPtr) internal pure returns (int48 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int56 at `mPtr` in memory.
          function readInt56(MemoryPointer mPtr) internal pure returns (int56 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int64 at `mPtr` in memory.
          function readInt64(MemoryPointer mPtr) internal pure returns (int64 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int72 at `mPtr` in memory.
          function readInt72(MemoryPointer mPtr) internal pure returns (int72 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int80 at `mPtr` in memory.
          function readInt80(MemoryPointer mPtr) internal pure returns (int80 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int88 at `mPtr` in memory.
          function readInt88(MemoryPointer mPtr) internal pure returns (int88 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int96 at `mPtr` in memory.
          function readInt96(MemoryPointer mPtr) internal pure returns (int96 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int104 at `mPtr` in memory.
          function readInt104(
              MemoryPointer mPtr
          ) internal pure returns (int104 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int112 at `mPtr` in memory.
          function readInt112(
              MemoryPointer mPtr
          ) internal pure returns (int112 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int120 at `mPtr` in memory.
          function readInt120(
              MemoryPointer mPtr
          ) internal pure returns (int120 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int128 at `mPtr` in memory.
          function readInt128(
              MemoryPointer mPtr
          ) internal pure returns (int128 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int136 at `mPtr` in memory.
          function readInt136(
              MemoryPointer mPtr
          ) internal pure returns (int136 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int144 at `mPtr` in memory.
          function readInt144(
              MemoryPointer mPtr
          ) internal pure returns (int144 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int152 at `mPtr` in memory.
          function readInt152(
              MemoryPointer mPtr
          ) internal pure returns (int152 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int160 at `mPtr` in memory.
          function readInt160(
              MemoryPointer mPtr
          ) internal pure returns (int160 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int168 at `mPtr` in memory.
          function readInt168(
              MemoryPointer mPtr
          ) internal pure returns (int168 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int176 at `mPtr` in memory.
          function readInt176(
              MemoryPointer mPtr
          ) internal pure returns (int176 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int184 at `mPtr` in memory.
          function readInt184(
              MemoryPointer mPtr
          ) internal pure returns (int184 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int192 at `mPtr` in memory.
          function readInt192(
              MemoryPointer mPtr
          ) internal pure returns (int192 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int200 at `mPtr` in memory.
          function readInt200(
              MemoryPointer mPtr
          ) internal pure returns (int200 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int208 at `mPtr` in memory.
          function readInt208(
              MemoryPointer mPtr
          ) internal pure returns (int208 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int216 at `mPtr` in memory.
          function readInt216(
              MemoryPointer mPtr
          ) internal pure returns (int216 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int224 at `mPtr` in memory.
          function readInt224(
              MemoryPointer mPtr
          ) internal pure returns (int224 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int232 at `mPtr` in memory.
          function readInt232(
              MemoryPointer mPtr
          ) internal pure returns (int232 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int240 at `mPtr` in memory.
          function readInt240(
              MemoryPointer mPtr
          ) internal pure returns (int240 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int248 at `mPtr` in memory.
          function readInt248(
              MemoryPointer mPtr
          ) internal pure returns (int248 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
          /// @dev Reads the int256 at `mPtr` in memory.
          function readInt256(
              MemoryPointer mPtr
          ) internal pure returns (int256 value) {
              assembly {
                  value := mload(mPtr)
              }
          }
      }
      library MemoryWriters {
          /// @dev Writes `valuePtr` to memory at `mPtr`.
          function write(MemoryPointer mPtr, MemoryPointer valuePtr) internal pure {
              assembly {
                  mstore(mPtr, valuePtr)
              }
          }
          /// @dev Writes a boolean `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, bool value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes an address `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, address value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes a bytes32 `value` to `mPtr` in memory.
          /// Separate name to disambiguate literal write parameters.
          function writeBytes32(MemoryPointer mPtr, bytes32 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes a uint256 `value` to `mPtr` in memory.
          function write(MemoryPointer mPtr, uint256 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
          /// @dev Writes an int256 `value` to `mPtr` in memory.
          /// Separate name to disambiguate literal write parameters.
          function writeInt(MemoryPointer mPtr, int256 value) internal pure {
              assembly {
                  mstore(mPtr, value)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.7;
      /**
       * @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`.
           *
           * 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/Address.sol)
      pragma solidity ^0.8.19;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @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 if target is a contract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(target.code.length > 0, "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
      pragma solidity ^0.8.19;
      import { AllowListData, CreatorPayout } from "./SeaDropStructs.sol";
      /**
       * @notice A struct defining public drop data.
       *         Designed to fit efficiently in two storage slots.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token address. Null for
       *                                 native token.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed. (The limit for this field is
       *                                 2^16 - 1)
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct PublicDrop {
          uint80 startPrice; // 80/512 bits
          uint80 endPrice; // 160/512 bits
          uint40 startTime; // 200/512 bits
          uint40 endTime; // 240/512 bits
          address paymentToken; // 400/512 bits
          uint16 maxTotalMintableByWallet; // 416/512 bits
          uint16 feeBps; // 432/512 bits
          bool restrictFeeRecipients; // 440/512 bits
      }
      /**
       * @notice A struct defining mint params for an allow list.
       *         An allow list leaf will be composed of `msg.sender` and
       *         the following params.
       *
       *         Note: Since feeBps is encoded in the leaf, backend should ensure
       *         that feeBps is acceptable before generating a proof.
       *
       * @param startPrice               The start price per token. (Up to 1.2m
       *                                 of native token, e.g. ETH, MATIC)
       * @param endPrice                 The end price per token. If this differs
       *                                 from startPrice, the current price will
       *                                 be calculated based on the current time.
       * @param startTime                The start time, ensure this is not zero.
       * @param endTime                  The end time, ensure this is not zero.
       * @param paymentToken             The payment token for the mint. Null for
       *                                 native token.
       * @param maxTotalMintableByWallet Maximum total number of mints a user is
       *                                 allowed.
       * @param maxTokenSupplyForStage   The limit of token supply this stage can
       *                                 mint within.
       * @param dropStageIndex           The drop stage index to emit with the event
       *                                 for analytical purposes. This should be
       *                                 non-zero since the public mint emits with
       *                                 index zero.
       * @param feeBps                   Fee out of 10_000 basis points to be
       *                                 collected.
       * @param restrictFeeRecipients    If false, allow any fee recipient;
       *                                 if true, check fee recipient is allowed.
       */
      struct MintParams {
          uint256 startPrice;
          uint256 endPrice;
          uint256 startTime;
          uint256 endTime;
          address paymentToken;
          uint256 maxTotalMintableByWallet;
          uint256 maxTokenSupplyForStage;
          uint256 dropStageIndex; // non-zero
          uint256 feeBps;
          bool restrictFeeRecipients;
      }
      /**
       * @dev Struct containing internal SeaDrop implementation logic
       *      mint details to avoid stack too deep.
       *
       * @param feeRecipient The fee recipient.
       * @param payer        The payer of the mint.
       * @param minter       The mint recipient.
       * @param quantity     The number of tokens to mint.
       * @param withEffects  Whether to apply state changes of the mint.
       */
      struct MintDetails {
          address feeRecipient;
          address payer;
          address minter;
          uint256 quantity;
          bool withEffects;
      }
      /**
       * @notice A struct to configure multiple contract options in one transaction.
       */
      struct MultiConfigureStruct {
          uint256 maxSupply;
          string baseURI;
          string contractURI;
          PublicDrop publicDrop;
          string dropURI;
          AllowListData allowListData;
          CreatorPayout[] creatorPayouts;
          bytes32 provenanceHash;
          address[] allowedFeeRecipients;
          address[] disallowedFeeRecipients;
          address[] allowedPayers;
          address[] disallowedPayers;
          // Server-signed
          address[] allowedSigners;
          address[] disallowedSigners;
          // ERC-2981
          address royaltyReceiver;
          uint96 royaltyBps;
          // Mint
          address mintRecipient;
          uint256 mintQuantity;
      }
      

      File 3 of 3: CreatorTokenTransferValidator
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.24;
      import "../Constants.sol";
      import "../interfaces/IEOARegistry.sol";
      import "../interfaces/ITransferValidator.sol";
      import "./TransferPolicy.sol";
      import {CreatorTokenTransferValidatorConfiguration} from "./CreatorTokenTransferValidatorConfiguration.sol";
      import "@limitbreak/permit-c/PermitC.sol";
      import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
      import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
      import "@opensea/tstorish/Tstorish.sol";
      /**
       * @title  CreatorTokenTransferValidator
       * @author Limit Break, Inc.
       * @notice The CreatorTokenTransferValidator contract is designed to provide a customizable and secure transfer 
       *         validation mechanism for NFT collections. This contract allows the owner of an NFT collection to configure 
       *         the transfer security level, blacklisted accounts and codehashes, whitelisted accounts and codehashes, and
       *         authorized accounts and codehashes for each collection.
       *
       * @dev    <h4>Features</h4>
       *         - Transfer security levels: Provides different levels of transfer security, 
       *           from open transfers to completely restricted transfers.
       *         - Blacklist: Allows the owner of a collection to blacklist specific operator addresses or codehashes
       *           from executing transfers on behalf of others.
       *         - Whitelist: Allows the owner of a collection to whitelist specific operator addresses or codehashes
       *           permitted to execute transfers on behalf of others or send/receive tokens when otherwise disabled by 
       *           security policy.
       *         - Authorizers: Allows the owner of a collection to enable authorizer contracts, that can perform 
       *           authorization-based filtering of transfers.
       *
       * @dev    <h4>Benefits</h4>
       *         - Enhanced security: Allows creators to have more control over their NFT collections, ensuring the safety 
       *           and integrity of their assets.
       *         - Flexibility: Provides collection owners the ability to customize transfer rules as per their requirements.
       *         - Compliance: Facilitates compliance with regulations by enabling creators to restrict transfers based on 
       *           specific criteria.
       *
       * @dev    <h4>Intended Usage</h4>
       *         - The CreatorTokenTransferValidatorV3 contract is intended to be used by NFT collection owners to manage and 
       *           enforce transfer policies. This contract is integrated with the following varations of creator token 
       *           NFT contracts to validate transfers according to the defined security policies.
       *
       *           - ERC721-C:   Creator token implenting OpenZeppelin's ERC-721 standard.
       *           - ERC721-AC:  Creator token implenting Azuki's ERC-721A standard.
       *           - ERC721-CW:  Creator token implementing OpenZeppelin's ERC-721 standard with opt-in staking to 
       *                         wrap/upgrade a pre-existing ERC-721 collection.
       *           - ERC721-ACW: Creator token implementing Azuki's ERC721-A standard with opt-in staking to 
       *                         wrap/upgrade a pre-existing ERC-721 collection.
       *           - ERC1155-C:  Creator token implenting OpenZeppelin's ERC-1155 standard.
       *           - ERC1155-CW: Creator token implementing OpenZeppelin's ERC-1155 standard with opt-in staking to 
       *                         wrap/upgrade a pre-existing ERC-1155 collection.
       *
       *          <h4>Transfer Security Levels</h4>
       *          - Recommended: Recommended defaults are same as Level 3 (Whitelisting with OTC Enabled).
       *            - Caller Constraints: OperatorWhitelistEnableOTC
       *            - Receiver Constraints: None
       *          - Level 1: No transfer restrictions.
       *            - Caller Constraints: None
       *            - Receiver Constraints: None
       *          - Level 2: Only non-blacklisted operators can initiate transfers, over-the-counter (OTC) trading enabled.
       *            - Caller Constraints: OperatorBlacklistEnableOTC
       *            - Receiver Constraints: None
       *          - Level 3: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled.
       *            - Caller Constraints: OperatorWhitelistEnableOTC
       *            - Receiver Constraints: None
       *          - Level 4: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled.
       *            - Caller Constraints: OperatorWhitelistDisableOTC
       *            - Receiver Constraints: None
       *          - Level 5: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled. 
       *                     Transfers to contracts with code are not allowed, unless present on the whitelist.
       *            - Caller Constraints: OperatorWhitelistEnableOTC
       *            - Receiver Constraints: NoCode
       *          - Level 6: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading enabled. 
       *                     Transfers are allowed only to Externally Owned Accounts (EOAs), unless present on the whitelist.
       *            - Caller Constraints: OperatorWhitelistEnableOTC
       *            - Receiver Constraints: EOA
       *          - Level 7: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled. 
       *                     Transfers to contracts with code are not allowed, unless present on the whitelist.
       *            - Caller Constraints: OperatorWhitelistDisableOTC
       *            - Receiver Constraints: NoCode
       *          - Level 8: Only whitelisted accounts can initiate transfers, over-the-counter (OTC) trading disabled. 
       *                     Transfers are allowed only to Externally Owned Accounts (EOAs), unless present on the whitelist.
       *            - Caller Constraints: OperatorWhitelistDisableOTC
       *            - Receiver Constraints: EOA
       */
      contract CreatorTokenTransferValidator is IEOARegistry, ITransferValidator, ERC165, Tstorish, PermitC {
          using EnumerableSet for EnumerableSet.AddressSet;
          using EnumerableSet for EnumerableSet.Bytes32Set;
          /*************************************************************************/
          /*                             CUSTOM ERRORS                             */
          /*************************************************************************/
          /// @dev Thrown when attempting to set a list id that does not exist.
          error CreatorTokenTransferValidator__ListDoesNotExist();
          /// @dev Thrown when attempting to transfer the ownership of a list to the zero address.
          error CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress();
          /// @dev Thrown when attempting to call a function that requires the caller to be the list owner.
          error CreatorTokenTransferValidator__CallerDoesNotOwnList();
          /// @dev Thrown when validating a transfer for a collection using whitelists and the operator is not on the whitelist.
          error CreatorTokenTransferValidator__CallerMustBeWhitelisted();
          /// @dev Thrown when authorizing a transfer for a collection using authorizers and the msg.sender is not in the authorizer list.
          error CreatorTokenTransferValidator__CallerMustBeAnAuthorizer();
          /// @dev Thrown when attempting to call a function that requires owner or default admin role for a collection that the caller does not have.
          error CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
          /// @dev Thrown when constructor args are not valid
          error CreatorTokenTransferValidator__InvalidConstructorArgs();
          /// @dev Thrown when setting the transfer security level to an invalid value.
          error CreatorTokenTransferValidator__InvalidTransferSecurityLevel();
          /// @dev Thrown when validating a transfer for a collection using blacklists and the operator is on the blacklist.
          error CreatorTokenTransferValidator__OperatorIsBlacklisted();
          /// @dev Thrown when validating a transfer for a collection that does not allow receiver to have code and the receiver has code.
          error CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode();
          /// @dev Thrown when validating a transfer for a collection that requires receivers be verified EOAs and the receiver is not verified.
          error CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified();
          /// @dev Thrown when a frozen account is the receiver of a transfer
          error CreatorTokenTransferValidator__ReceiverAccountIsFrozen();
          /// @dev Thrown when a frozen account is the sender of a transfer
          error CreatorTokenTransferValidator__SenderAccountIsFrozen();
          /// @dev Thrown when validating a transfer for a collection that is in soulbound token mode.
          error CreatorTokenTransferValidator__TokenIsSoulbound();
          /// @dev Thrown when an authorizer attempts to set a wildcard authorized operator on collections that don't allow wildcards
          error CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();
          /// @dev Thrown when attempting to set a authorized operator when authorization mode is disabled.
          error CreatorTokenTransferValidator__AuthorizationDisabledForCollection();
          /// @dev Thrown when attempting to validate a permitted transfer where the permit type does not match the collection-defined token type.
          error CreatorTokenTransferValidator__TokenTypesDoNotMatch();
          /*************************************************************************/
          /*                                EVENTS                                 */
          /*************************************************************************/
          /// @dev Emitted when a new list is created.
          event CreatedList(uint256 indexed id, string name);
          /// @dev Emitted when a list is applied to a collection.
          event AppliedListToCollection(address indexed collection, uint120 indexed id);
          /// @dev Emitted when the ownership of a list is transferred to a new owner.
          event ReassignedListOwnership(uint256 indexed id, address indexed newOwner);
          /// @dev Emitted when an account is added to the list of frozen accounts for a collection.
          event AccountFrozenForCollection(address indexed collection, address indexed account);
          /// @dev Emitted when an account is removed from the list of frozen accounts for a collection.
          event AccountUnfrozenForCollection(address indexed collection, address indexed account);
          /// @dev Emitted when an address is added to a list.
          event AddedAccountToList(uint8 indexed kind, uint256 indexed id, address indexed account);
          /// @dev Emitted when a codehash is added to a list.
          event AddedCodeHashToList(uint8 indexed kind, uint256 indexed id, bytes32 indexed codehash);
          /// @dev Emitted when an address is removed from a list.
          event RemovedAccountFromList(uint8 indexed kind, uint256 indexed id, address indexed account);
          /// @dev Emitted when a codehash is removed from a list.
          event RemovedCodeHashFromList(uint8 indexed kind, uint256 indexed id, bytes32 indexed codehash);
          /// @dev Emitted when the security level for a collection is updated.
          event SetTransferSecurityLevel(address indexed collection, uint8 level);
          /// @dev Emitted when a collection updates its authorization mode.
          event SetAuthorizationModeEnabled(address indexed collection, bool disabled, bool authorizersCannotSetWildcardOperators);
          /// @dev Emitted when a collection turns account freezing on or off.
          event SetAccountFreezingModeEnabled(address indexed collection, bool enabled);
          /// @dev Emitted when a collection's token type is updated.
          event SetTokenType(address indexed collection, uint16 tokenType);
          /*************************************************************************/
          /*                                STRUCTS                                */
          /*************************************************************************/
          /**
           * @dev This struct is internally for the storage of account and codehash lists.
           */
          struct List {
              EnumerableSet.AddressSet enumerableAccounts;
              EnumerableSet.Bytes32Set enumerableCodehashes;
              mapping (address => bool) nonEnumerableAccounts;
              mapping (bytes32 => bool) nonEnumerableCodehashes;
          }
          /**
           * @dev This struct is internally for the storage of account lists.
           */
          struct AccountList {
              EnumerableSet.AddressSet enumerableAccounts;
              mapping (address => bool) nonEnumerableAccounts;
          }
          /*************************************************************************/
          /*                               CONSTANTS                               */
          /*************************************************************************/
          /// @dev Immutable lookup table for constant gas determination of caller constraints by security level.
          /// @dev Created during contract construction using defined constants.
          uint256 private immutable _callerConstraintsLookup;
          /// @dev Immutable lookup table for constant gas determination of receiver constraints by security level.
          /// @dev Created during contract construction using defined constants.
          uint256 private immutable _receiverConstraintsLookup;
          /// @dev The address of the EOA Registry to use to validate an account is a verified EOA.
          address private immutable _eoaRegistry;
          /// @dev The legacy Creator Token Transfer Validator Interface
          bytes4 private constant LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID = 0x00000000;
          /// @dev The default admin role value for contracts that implement access control.
          bytes32 private constant DEFAULT_ACCESS_CONTROL_ADMIN_ROLE = 0x00;
          /// @dev Value representing a zero value code hash.
          bytes32 private constant BYTES32_ZERO = 0x0000000000000000000000000000000000000000000000000000000000000000;
          address private constant WILDCARD_OPERATOR_ADDRESS = address(0x01);
          uint16 private constant DEFAULT_TOKEN_TYPE = 0;
          /*************************************************************************/
          /*                                STORAGE                                */
          /*************************************************************************/
          /// @notice Keeps track of the most recently created list id.
          uint120 public lastListId;
          /// @notice Mapping of list ids to list owners
          mapping (uint120 => address) public listOwners;
          /// @dev Mapping of collection addresses to their security policy settings
          mapping (address => CollectionSecurityPolicyV3) internal collectionSecurityPolicies;
          /// @dev Mapping of list ids to blacklist settings
          mapping (uint120 => List) internal blacklists;
          /// @dev Mapping of list ids to whitelist settings
          mapping (uint120 => List) internal whitelists;
          /// @dev Mapping of list ids to authorizers
          mapping (uint120 => List) internal authorizers;
          /// @dev Mapping of collections to accounts that are frozen for those collections
          mapping (address => AccountList) internal frozenAccounts;
          constructor(
              address defaultOwner,
              address eoaRegistry_,
              string memory name,
              string memory version,
              address validatorConfiguration
          ) 
          Tstorish()
          PermitC(
              name,
              version,
              defaultOwner,
              CreatorTokenTransferValidatorConfiguration(validatorConfiguration).getNativeValueToCheckPauseState()
          ) {
              if (defaultOwner == address(0) || eoaRegistry_ == address(0)) {
                  revert CreatorTokenTransferValidator__InvalidConstructorArgs();
              }
              _createDefaultList(defaultOwner);
              _eoaRegistry = eoaRegistry_;
              _callerConstraintsLookup = _constructCallerConstraintsTable();
              _receiverConstraintsLookup = _constructReceiverConstraintsTable();
          }
          /**
           * @dev This function is only called during contract construction to create the default list.
           */
          function _createDefaultList(address defaultOwner) internal {
              uint120 id = 0;
              listOwners[id] = defaultOwner;
              emit CreatedList(id, "DEFAULT LIST");
              emit ReassignedListOwnership(id, defaultOwner);
          }
          /**
           * @dev This function is only called during contract construction to create the caller constraints
           * @dev lookup table.
           */
          function _constructCallerConstraintsTable() internal pure returns (uint256) {
              return 
              (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_RECOMMENDED << 3))
                  | (CALLER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_ONE << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_TWO << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_THREE << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_FOUR << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_FIVE << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC << (TRANSFER_SECURITY_LEVEL_SIX << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_SEVEN << 3))
                  | (CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC << (TRANSFER_SECURITY_LEVEL_EIGHT << 3))
                  | (CALLER_CONSTRAINTS_SBT << (TRANSFER_SECURITY_LEVEL_NINE << 3));
          }
          /**
           * @dev This function is only called during contract construction to create the receiver constraints
           * @dev lookup table.
           */
          function _constructReceiverConstraintsTable() internal pure returns (uint256) {
              return 
              (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_RECOMMENDED << 3))
                  | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_ONE << 3))
                  | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_TWO << 3))
                  | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_THREE << 3))
                  | (RECEIVER_CONSTRAINTS_NONE << (TRANSFER_SECURITY_LEVEL_FOUR << 3))
                  | (RECEIVER_CONSTRAINTS_NO_CODE << (TRANSFER_SECURITY_LEVEL_FIVE << 3))
                  | (RECEIVER_CONSTRAINTS_EOA << (TRANSFER_SECURITY_LEVEL_SIX << 3))
                  | (RECEIVER_CONSTRAINTS_NO_CODE << (TRANSFER_SECURITY_LEVEL_SEVEN << 3))
                  | (RECEIVER_CONSTRAINTS_EOA << (TRANSFER_SECURITY_LEVEL_EIGHT << 3))
                  | (RECEIVER_CONSTRAINTS_SBT << (TRANSFER_SECURITY_LEVEL_NINE << 3));
          }
          /*************************************************************************/
          /*                               MODIFIERS                               */
          /*************************************************************************/
          /**
           * @dev This modifier restricts a function call to the owner of the list `id`.
           * @dev Throws when the caller is not the list owner.
           */
          modifier onlyListOwner(uint120 id) {
              _requireCallerOwnsList(id);
              _;
          }
          /*************************************************************************/
          /*                          APPLY TRANSFER POLICIES                      */
          /*************************************************************************/
          /**
           * @notice Apply the collection transfer policy to a transfer operation of a creator token.
           *
           * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
           *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
           *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
           *
           * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
           *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
           *      expected in the wild.
           *
           * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
           *      are on the list of frozen accounts for the collection.
           * @dev Throws when the collection is set to Level 9 - Soulbound Token.
           * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
           *      and the transfer is not approved by an authorizer for the collection.
           * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
           *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
           *      authorizer for the collection..
           * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
           *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
           *      is not approved by an authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Transfer is allowed or denied based on the applied transfer policy.
           *
           * @param caller      The address initiating the transfer.
           * @param from        The address of the token owner.
           * @param to          The address of the token receiver.
           */
          function validateTransfer(address caller, address from, address to) public view {
              (bytes4 errorSelector,) = _validateTransfer(_callerAuthorizedCheckCollection, msg.sender, caller, from, to, 0);
              if (errorSelector != SELECTOR_NO_ERROR) {
                  _revertCustomErrorSelectorAsm(errorSelector);
              }
          }
          /**
           * @notice Apply the collection transfer policy to a transfer operation of a creator token.
           *
           * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
           *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
           *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
           *
           * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
           *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
           *      expected in the wild.
           *
           * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
           *      are on the list of frozen accounts for the collection.
           * @dev Throws when the collection is set to Level 9 - Soulbound Token.
           * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
           *      and the transfer is not approved by an authorizer for the collection.
           * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
           *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
           *      authorizer for the collection..
           * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
           *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
           *      is not approved by an authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Transfer is allowed or denied based on the applied transfer policy.
           *
           * @param caller      The address initiating the transfer.
           * @param from        The address of the token owner.
           * @param to          The address of the token receiver.
           * @param tokenId     The token id being transferred.
           */
          function validateTransfer(address caller, address from, address to, uint256 tokenId) public view {
              (bytes4 errorSelector,) = _validateTransfer(_callerAuthorizedCheckToken, msg.sender, caller, from, to, tokenId);
              if (errorSelector != SELECTOR_NO_ERROR) {
                  _revertCustomErrorSelectorAsm(errorSelector);
              }
          }
          /**
           * @notice Apply the collection transfer policy to a transfer operation of a creator token.
           *
           * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
           *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
           *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
           *
           * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
           *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
           *      expected in the wild.
           *
           * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
           *      are on the list of frozen accounts for the collection.
           * @dev Throws when the collection is set to Level 9 - Soulbound Token.
           * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
           *      and the transfer is not approved by an authorizer for the collection.
           * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
           *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
           *      authorizer for the collection..
           * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
           *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
           *      is not approved by an authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Transfer is allowed or denied based on the applied transfer policy.
           *
           * @param caller      The address initiating the transfer.
           * @param from        The address of the token owner.
           * @param to          The address of the token receiver.
           * @param tokenId     The token id being transferred.
           */
          function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 /*amount*/) external {
              validateTransfer(caller, from, to, tokenId);
          }
          /**
           * @notice Apply the collection transfer policy to a transfer operation of a creator token.
           *
           * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
           *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
           *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
           *
           * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
           *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
           *      expected in the wild.
           *
           * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
           *      are on the list of frozen accounts for the collection.
           * @dev Throws when the collection is set to Level 9 - Soulbound Token.
           * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
           *      and the transfer is not approved by an authorizer for the collection.
           * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
           *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
           *      authorizer for the collection..
           * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
           *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
           *      is not approved by an authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Transfer is allowed or denied based on the applied transfer policy.
           *
           * @param caller      The address initiating the transfer.
           * @param from        The address of the token owner.
           * @param to          The address of the token receiver.
           */
          function applyCollectionTransferPolicy(address caller, address from, address to) external view {
              validateTransfer(caller, from, to);
          }
          /**
           * @notice Returns the caller and receiver constraints for the specified transfer security level.
           * 
           * @param level The transfer security level to return the caller and receiver constraints for.
           * 
           * @return callerConstraints    The `CallerConstraints` value for the level.
           * @return receiverConstraints The `ReceiverConstraints` value for the level.
           */
          function transferSecurityPolicies(
              uint256 level
          ) public view returns (uint256 callerConstraints, uint256 receiverConstraints) {
              callerConstraints = uint8((_callerConstraintsLookup >> (level << 3)));
              receiverConstraints = uint8((_receiverConstraintsLookup >> (level << 3)));
          }
          /**
           * @notice Sets an operator for an authorized transfer that skips transfer security level
           *         validation for caller and receiver constraints.
           * 
           * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
           *         to prevent unauthorized transfers of the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when using the wildcard operator address and the collection does not allow
           *         for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The `operator` is stored as an authorized operator for transfers.
           * 
           * @param operator  The address of the operator to set as authorized for transfers.
           * @param token     The address of the token to authorize.
           * @param tokenId   The token id to set the authorized operator for.
           */
          function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external {
              _setOperatorInTransientStorage(operator, token, tokenId, false);
          }
          /**
           * @notice Clears the authorized operator for a token to prevent additional transfers that
           *         do not conform to the transfer security level for the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when using the wildcard operator address and the collection does not allow
           *         for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The authorized operator for the token is cleared from storage.
           * 
           * @param token     The address of the token to authorize.
           * @param tokenId   The token id to set the authorized operator for.
           */
          function afterAuthorizedTransfer(address token, uint256 tokenId) public {
              _setOperatorInTransientStorage(address(uint160(uint256(BYTES32_ZERO))), token, tokenId, false);
          }
          /**
           * @notice Sets an operator for an authorized transfer that skips transfer security level
           *         validation for caller and receiver constraints.
           * @notice This overload of `beforeAuthorizedTransfer` defaults to a tokenId of 0.
           * 
           * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
           *         to prevent unauthorized transfers of the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when using the wildcard operator address and the collection does not allow
           *         for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The `operator` is stored as an authorized operator for transfers.
           * 
           * @param operator  The address of the operator to set as authorized for transfers.
           * @param token     The address of the token to authorize.
           */
          function beforeAuthorizedTransfer(address operator, address token) external {
              _setOperatorInTransientStorage(operator, token, 0, true);
          }
          /**
           * @notice Clears the authorized operator for a token to prevent additional transfers that
           *         do not conform to the transfer security level for the token.
           * @notice This overload of `afterAuthorizedTransfer` defaults to a tokenId of 0.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when using the wildcard operator address and the collection does not allow
           *         for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The authorized operator for the token is cleared from storage.
           * 
           * @param token     The address of the token to authorize.
           */
          function afterAuthorizedTransfer(address token) external {
              afterAuthorizedTransfer(token, 0);
          }
          /**
           * @notice Sets the wildcard operator to authorize any operator to transfer a token while
           *         skipping transfer security level validation for caller and receiver constraints.
           * 
           * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
           *         to prevent unauthorized transfers of the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when the collection does not allow for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The wildcard operator is stored as an authorized operator for transfers.
           * 
           * @param token     The address of the token to authorize.
           * @param tokenId   The token id to set the authorized operator for.
           */
          function beforeAuthorizedTransfer(address token, uint256 tokenId) external {
              _setOperatorInTransientStorage(WILDCARD_OPERATOR_ADDRESS, token, tokenId, false);
          }
          /**
           * @notice Sets the wildcard operator to authorize any operator to transfer a token while
           *         skipping transfer security level validation for caller and receiver constraints.
           * 
           * @dev    An authorizer *MUST* clear the authorization with a call to `afterAuthorizedTransfer`
           *         to prevent unauthorized transfers of the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when the collection does not allow for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The wildcard operator is stored as an authorized operator for transfers.
           * 
           * @param token     The address of the token to authorize.
           * @param tokenId   The token id to set the authorized operator for.
           */
          function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 /*amount*/) external {
              _setOperatorInTransientStorage(WILDCARD_OPERATOR_ADDRESS, token, tokenId, false);
          }
          /**
           * @notice Clears the authorized operator for a token to prevent additional transfers that
           *         do not conform to the transfer security level for the token.
           * 
           * @dev    Throws when authorization mode is disabled for the collection.
           * @dev    Throws when using the wildcard operator address and the collection does not allow
           *         for wildcard authorized operators.
           * @dev    Throws when the caller is not an allowed authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The authorized operator for the token is cleared from storage.
           * 
           * @param token     The address of the token to authorize.
           * @param tokenId   The token id to set the authorized operator for.
           */
          function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external {
              afterAuthorizedTransfer(token, tokenId);
          }
          /*************************************************************************/
          /*                              LIST MANAGEMENT                          */
          /*************************************************************************/
          /**
           * @notice Creates a new list id.  The list id is a handle to allow editing of blacklisted and whitelisted accounts
           *         and codehashes.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. A new list with the specified name is created.
           *      2. The caller is set as the owner of the new list.
           *      3. A `CreatedList` event is emitted.
           *      4. A `ReassignedListOwnership` event is emitted.
           *
           * @param  name The name of the new list.
           * @return id   The id of the new list.
           */
          function createList(string calldata name) public returns (uint120 id) {
              unchecked {
                  id = ++lastListId;
              }
              listOwners[id] = msg.sender;
              emit CreatedList(id, name);
              emit ReassignedListOwnership(id, msg.sender);
          }
          /**
           * @notice Creates a new list id, and copies all blacklisted and whitelisted accounts and codehashes from the
           *         specified source list.
           *
           * @dev    <h4>Postconditions:</h4>
           *         1. A new list with the specified name is created.
           *         2. The caller is set as the owner of the new list.
           *         3. A `CreatedList` event is emitted.
           *         4. A `ReassignedListOwnership` event is emitted.
           *         5. All blacklisted and whitelisted accounts and codehashes from the specified source list are copied
           *            to the new list.
           *         6. An `AddedAccountToList` event is emitted for each blacklisted and whitelisted account copied.
           *         7. An `AddedCodeHashToList` event is emitted for each blacklisted and whitelisted codehash copied.
           *
           * @param  name         The name of the new list.
           * @param  sourceListId The id of the source list to copy from.
           * @return id           The id of the new list.
           */
          function createListCopy(string calldata name, uint120 sourceListId) external returns (uint120 id) {
              unchecked {
                  id = ++lastListId;
              }
              unchecked {
                  if (sourceListId > id - 1) {
                      revert CreatorTokenTransferValidator__ListDoesNotExist();
                  }
              }
              listOwners[id] = msg.sender;
              emit CreatedList(id, name);
              emit ReassignedListOwnership(id, msg.sender);
              List storage sourceBlacklist = blacklists[sourceListId];
              List storage sourceWhitelist = whitelists[sourceListId];
              List storage sourceAuthorizers = authorizers[sourceListId];
              List storage targetBlacklist = blacklists[id];
              List storage targetWhitelist = whitelists[id];
              List storage targetAuthorizers = authorizers[id];
              _copyAddressSet(LIST_TYPE_BLACKLIST, id, sourceBlacklist, targetBlacklist);
              _copyBytes32Set(LIST_TYPE_BLACKLIST, id, sourceBlacklist, targetBlacklist);
              _copyAddressSet(LIST_TYPE_WHITELIST, id, sourceWhitelist, targetWhitelist);
              _copyBytes32Set(LIST_TYPE_WHITELIST, id, sourceWhitelist, targetWhitelist);
              _copyAddressSet(LIST_TYPE_AUTHORIZERS, id, sourceAuthorizers, targetAuthorizers);
              _copyBytes32Set(LIST_TYPE_AUTHORIZERS, id, sourceAuthorizers, targetAuthorizers);
          }
          /**
           * @notice Transfer ownership of a list to a new owner.
           *
           * @dev Throws when the new owner is the zero address.
           * @dev Throws when the caller does not own the specified list.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The list ownership is transferred to the new owner.
           *      2. A `ReassignedListOwnership` event is emitted.
           *
           * @param id       The id of the list.
           * @param newOwner The address of the new owner.
           */
          function reassignOwnershipOfList(uint120 id, address newOwner) public {
              if(newOwner == address(0)) {
                  revert CreatorTokenTransferValidator__ListOwnershipCannotBeTransferredToZeroAddress();
              }
              _reassignOwnershipOfList(id, newOwner);
          }
          /**
           * @notice Renounce the ownership of a list, rendering the list immutable.
           *
           * @dev Throws when the caller does not own the specified list.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The ownership of the specified list is renounced.
           *      2. A `ReassignedListOwnership` event is emitted.
           *
           * @param id The id of the list.
           */
          function renounceOwnershipOfList(uint120 id) public {
              _reassignOwnershipOfList(id, address(0));
          }
          /**
           * @notice Set the transfer security level, authorization mode and account freezing mode settings of a collection.
           *
           * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The transfer security level of the specified collection is set to the new value.
           *      2. The authorization mode setting of the specified collection is set to the new value.
           *      3. The authorization wildcard operator mode setting of the specified collection is set to the new value.
           *      4. The account freezing mode setting of the specified collection is set to the new value.
           *      5. A `SetTransferSecurityLevel` event is emitted.
           *      6. A `SetAuthorizationModeEnabled` event is emitted.
           *      7. A `SetAccountFreezingModeEnabled` event is emitted.
           *
           * @param collection                 The address of the collection.
           * @param level                      The new transfer security level to apply.
           * @param disableAuthorizationMode   Flag if the collection allows for authorizer mode.
           * @param disableWildcardOperators   Flag if the authorizer can set wildcard operators.
           * @param enableAccountFreezingMode  Flag if the collection is using account freezing.
           */
          function setTransferSecurityLevelOfCollection(
              address collection, 
              uint8 level,
              bool disableAuthorizationMode,
              bool disableWildcardOperators,
              bool enableAccountFreezingMode) external {
              if (level > TRANSFER_SECURITY_LEVEL_NINE) {
                  revert CreatorTokenTransferValidator__InvalidTransferSecurityLevel();
              }
              _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
              collectionSecurityPolicies[collection].transferSecurityLevel = level;
              collectionSecurityPolicies[collection].disableAuthorizationMode = disableAuthorizationMode;
              collectionSecurityPolicies[collection].authorizersCannotSetWildcardOperators = disableWildcardOperators;
              collectionSecurityPolicies[collection].enableAccountFreezingMode = enableAccountFreezingMode;
              emit SetTransferSecurityLevel(collection, level);
              emit SetAuthorizationModeEnabled(collection, disableAuthorizationMode, disableWildcardOperators);
              emit SetAccountFreezingModeEnabled(collection, enableAccountFreezingMode);
          }
          /**
           * @notice Set the token type setting of a collection.
           *
           * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The token type of the specified collection is set to the new value.
           *      2. A `SetTokenType` event is emitted.
           *
           * @param collection  The address of the collection.
           * @param tokenType   The new transfer security level to apply.
           */
          function setTokenTypeOfCollection(
              address collection, 
              uint16 tokenType
          ) external {
              _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
              collectionSecurityPolicies[collection].tokenType = tokenType;
              emit SetTokenType(collection, tokenType);
          }
          /**
           * @notice Applies the specified list to a collection.
           * 
           * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
           * @dev Throws when the specified list id does not exist.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The list of the specified collection is set to the new value.
           *      2. An `AppliedListToCollection` event is emitted.
           *
           * @param collection The address of the collection.
           * @param id         The id of the operator whitelist.
           */
          function applyListToCollection(address collection, uint120 id) public {
              _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
              if (id > lastListId) {
                  revert CreatorTokenTransferValidator__ListDoesNotExist();
              }
              collectionSecurityPolicies[collection].listId = id;
              emit AppliedListToCollection(collection, id);
          }
          /**
           * @notice Adds accounts to the frozen accounts list of a collection.
           * 
           * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The accounts are added to the list of frozen accounts for a collection.
           *      2. A `AccountFrozenForCollection` event is emitted for each account added to the list.
           *
           * @param collection        The address of the collection.
           * @param accountsToFreeze  The list of accounts to added to frozen accounts.
           */
          function freezeAccountsForCollection(address collection, address[] calldata accountsToFreeze) external {
              _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
              AccountList storage accounts = frozenAccounts[collection];
              for (uint256 i = 0; i < accountsToFreeze.length;) {
                  address accountToFreeze = accountsToFreeze[i];
                  if (accounts.enumerableAccounts.add(accountToFreeze)) {
                      emit AccountFrozenForCollection(collection, accountToFreeze);
                      accounts.nonEnumerableAccounts[accountToFreeze] = true;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Removes accounts to the frozen accounts list of a collection.
           * 
           * @dev Throws when the caller is neither collection contract, nor the owner or admin of the specified collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. The accounts are removed from the list of frozen accounts for a collection.
           *      2. A `AccountUnfrozenForCollection` event is emitted for each account removed from the list.
           *
           * @param collection          The address of the collection.
           * @param accountsToUnfreeze  The list of accounts to remove from frozen accounts.
           */
          function unfreezeAccountsForCollection(address collection, address[] calldata accountsToUnfreeze) external {
              _requireCallerIsNFTOrContractOwnerOrAdmin(collection);
              AccountList storage accounts = frozenAccounts[collection];
              for (uint256 i = 0; i < accountsToUnfreeze.length;) {
                  address accountToUnfreeze = accountsToUnfreeze[i];
                  if (accounts.enumerableAccounts.remove(accountToUnfreeze)) {
                      emit AccountUnfrozenForCollection(collection, accountToUnfreeze);
                      accounts.nonEnumerableAccounts[accountToUnfreeze] = false;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Get the security policy of the specified collection.
           * @param collection The address of the collection.
           * @return           The security policy of the specified collection, which includes:
           *                   Transfer security level, operator whitelist id, permitted contract receiver allowlist id,
           *                   authorizer mode, if authorizer can set a wildcard operator, and if account freezing is
           *                   enabled.
           */
          function getCollectionSecurityPolicy(
              address collection
          ) external view returns (CollectionSecurityPolicyV3 memory) {
              return collectionSecurityPolicies[collection];
          }
          /**
           * @notice Adds one or more accounts to a blacklist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts not previously in the list are added.
           *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to add.
           */
          function addAccountsToBlacklist(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _addAccountsToList(blacklists[id], LIST_TYPE_BLACKLIST, id, accounts);
          }
          /**
           * @notice Adds one or more accounts to a whitelist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts not previously in the list are added.
           *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to add.
           */
          function addAccountsToWhitelist(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _addAccountsToList(whitelists[id], LIST_TYPE_WHITELIST, id, accounts);
          }
          /**
           * @notice Adds one or more accounts to authorizers.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts not previously in the list are added.
           *      2. An `AddedAccountToList` event is emitted for each account that is newly added to the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to add.
           */
          function addAccountsToAuthorizers(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _addAccountsToList(authorizers[id], LIST_TYPE_AUTHORIZERS, id, accounts);
          }
          /**
           * @notice Adds one or more codehashes to a blacklist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the codehashes array is empty.
           * @dev Throws when a codehash is zero.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes not previously in the list are added.
           *      2. An `AddedCodeHashToList` event is emitted for each codehash that is newly added to the list.
           *
           * @param id         The id of the list.
           * @param codehashes The codehashes to add.
           */
          function addCodeHashesToBlacklist(
              uint120 id, 
              bytes32[] calldata codehashes
          ) external {
              _addCodeHashesToList(blacklists[id], LIST_TYPE_BLACKLIST, id, codehashes);
          }
          /**
           * @notice Adds one or more codehashes to a whitelist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the codehashes array is empty.
           * @dev Throws when a codehash is zero.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes not previously in the list are added.
           *      2. An `AddedCodeHashToList` event is emitted for each codehash that is newly added to the list.
           *
           * @param id         The id of the list.
           * @param codehashes The codehashes to add.
           */
          function addCodeHashesToWhitelist(
              uint120 id, 
              bytes32[] calldata codehashes
          ) external {
              _addCodeHashesToList(whitelists[id], LIST_TYPE_WHITELIST, id, codehashes);
          }
          /**
           * @notice Removes one or more accounts from a blacklist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts previously in the list are removed.
           *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to remove.
           */
          function removeAccountsFromBlacklist(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _removeAccountsFromList(blacklists[id], LIST_TYPE_BLACKLIST, id, accounts);
          }
          /**
           * @notice Removes one or more accounts from a whitelist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts previously in the list are removed.
           *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to remove.
           */
          function removeAccountsFromWhitelist(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _removeAccountsFromList(whitelists[id], LIST_TYPE_WHITELIST, id, accounts);
          }
          /**
           * @notice Removes one or more accounts from authorizers.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the accounts array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts previously in the list are removed.
           *      2. A `RemovedAccountFromList` event is emitted for each account that is removed from the list.
           *
           * @param id       The id of the list.
           * @param accounts The addresses of the accounts to remove.
           */
          function removeAccountsFromAuthorizers(
              uint120 id, 
              address[] calldata accounts
          ) external {
              _removeAccountsFromList(authorizers[id], LIST_TYPE_AUTHORIZERS, id, accounts);
          }
          /**
           * @notice Removes one or more codehashes from a blacklist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the codehashes array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes previously in the list are removed.
           *      2. A `RemovedCodeHashFromList` event is emitted for each codehash that is removed from the list.
           *
           * @param id         The id of the list.
           * @param codehashes The codehashes to remove.
           */
          function removeCodeHashesFromBlacklist(
              uint120 id, 
              bytes32[] calldata codehashes
          ) external {
              _removeCodeHashesFromList(blacklists[id], LIST_TYPE_BLACKLIST, id, codehashes);
          }
          /**
           * @notice Removes one or more codehashes from a whitelist.
           *
           * @dev Throws when the caller does not own the specified list.
           * @dev Throws when the codehashes array is empty.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes previously in the list are removed.
           *      2. A `RemovedCodeHashFromList` event is emitted for each codehash that is removed from the list.
           *
           * @param id         The id of the list.
           * @param codehashes The codehashes to remove.
           */
          function removeCodeHashesFromWhitelist(
              uint120 id, 
              bytes32[] calldata codehashes
          ) external {
              _removeCodeHashesFromList(whitelists[id], LIST_TYPE_WHITELIST, id, codehashes);
          }
          /**
           * @notice Get blacklisted accounts by list id.
           * @param  id The id of the list.
           * @return An array of blacklisted accounts.
           */
          function getBlacklistedAccounts(uint120 id) public view returns (address[] memory) {
              return blacklists[id].enumerableAccounts.values();
          }
          /**
           * @notice Get whitelisted accounts by list id.
           * @param  id The id of the list.
           * @return An array of whitelisted accounts.
           */
          function getWhitelistedAccounts(uint120 id) public view returns (address[] memory) {
              return whitelists[id].enumerableAccounts.values();
          }
          /**
           * @notice Get authorizor accounts by list id.
           * @param  id The id of the list.
           * @return An array of authorizer accounts.
           */
          function getAuthorizerAccounts(uint120 id) public view returns (address[] memory) {
              return authorizers[id].enumerableAccounts.values();
          }
          /**
           * @notice Get blacklisted codehashes by list id.
           * @param id The id of the list.
           * @return   An array of blacklisted codehashes.
           */
          function getBlacklistedCodeHashes(uint120 id) public view returns (bytes32[] memory) {
              return blacklists[id].enumerableCodehashes.values();
          }
          /**
           * @notice Get whitelisted codehashes by list id.
           * @param id The id of the list.
           * @return   An array of whitelisted codehashes.
           */
          function getWhitelistedCodeHashes(uint120 id) public view returns (bytes32[] memory) {
              return whitelists[id].enumerableCodehashes.values();
          }
          /**
           * @notice Check if an account is blacklisted in a specified list.
           * @param id       The id of the list.
           * @param account  The address of the account to check.
           * @return         True if the account is blacklisted in the specified list, false otherwise.
           */
          function isAccountBlacklisted(uint120 id, address account) public view returns (bool) {
              return blacklists[id].nonEnumerableAccounts[account];
          }
          /**
           * @notice Check if an account is whitelisted in a specified list.
           * @param id       The id of the list.
           * @param account  The address of the account to check.
           * @return         True if the account is whitelisted in the specified list, false otherwise.
           */
          function isAccountWhitelisted(uint120 id, address account) public view returns (bool) {
              return whitelists[id].nonEnumerableAccounts[account];
          }
          /**
           * @notice Check if an account is an authorizer in a specified list.
           * @param id       The id of the list.
           * @param account  The address of the account to check.
           * @return         True if the account is an authorizer in the specified list, false otherwise.
           */
          function isAccountAuthorizer(uint120 id, address account) public view returns (bool) {
              return authorizers[id].nonEnumerableAccounts[account];
          }
          /**
           * @notice Check if a codehash is blacklisted in a specified list.
           * @param id       The id of the list.
           * @param codehash  The codehash to check.
           * @return         True if the codehash is blacklisted in the specified list, false otherwise.
           */
          function isCodeHashBlacklisted(uint120 id, bytes32 codehash) public view returns (bool) {
              return blacklists[id].nonEnumerableCodehashes[codehash];
          }
          /**
           * @notice Check if a codehash is whitelisted in a specified list.
           * @param id       The id of the list.
           * @param codehash  The codehash to check.
           * @return         True if the codehash is whitelisted in the specified list, false otherwise.
           */
          function isCodeHashWhitelisted(uint120 id, bytes32 codehash) public view returns (bool) {
              return whitelists[id].nonEnumerableCodehashes[codehash];
          }
          /**
           * @notice Get blacklisted accounts by collection.
           * @param collection The address of the collection.
           * @return           An array of blacklisted accounts.
           */
          function getBlacklistedAccountsByCollection(address collection) external view returns (address[] memory) {
              return getBlacklistedAccounts(collectionSecurityPolicies[collection].listId);
          }
          /**
           * @notice Get whitelisted accounts by collection.
           * @param collection The address of the collection.
           * @return           An array of whitelisted accounts.
           */
          function getWhitelistedAccountsByCollection(address collection) external view returns (address[] memory) {
              return getWhitelistedAccounts(collectionSecurityPolicies[collection].listId);
          }
          /**
           * @notice Get authorizer accounts by collection.
           * @param collection The address of the collection.
           * @return           An array of authorizer accounts.
           */
          function getAuthorizerAccountsByCollection(address collection) external view returns (address[] memory) {
              return getAuthorizerAccounts(collectionSecurityPolicies[collection].listId);
          }
          /**
           * @notice Get frozen accounts by collection.
           * @param collection The address of the collection.
           * @return           An array of frozen accounts.
           */
          function getFrozenAccountsByCollection(address collection) external view returns (address[] memory) {
              return frozenAccounts[collection].enumerableAccounts.values();
          }
          /**
           * @notice Get blacklisted codehashes by collection.
           * @param collection The address of the collection.
           * @return           An array of blacklisted codehashes.
           */
          function getBlacklistedCodeHashesByCollection(address collection) external view returns (bytes32[] memory) {
              return getBlacklistedCodeHashes(collectionSecurityPolicies[collection].listId);
          }
          /**
           * @notice Get whitelisted codehashes by collection.
           * @param collection The address of the collection.
           * @return           An array of whitelisted codehashes.
           */
          function getWhitelistedCodeHashesByCollection(address collection) external view returns (bytes32[] memory) {
              return getWhitelistedCodeHashes(collectionSecurityPolicies[collection].listId);
          }
          /**
           * @notice Check if an account is blacklisted by a specified collection.
           * @param collection The address of the collection.
           * @param account    The address of the account to check.
           * @return           True if the account is blacklisted by the specified collection, false otherwise.
           */
          function isAccountBlacklistedByCollection(address collection, address account) external view returns (bool) {
              return isAccountBlacklisted(collectionSecurityPolicies[collection].listId, account);
          }
          /**
           * @notice Check if an account is whitelisted by a specified collection.
           * @param collection The address of the collection.
           * @param account    The address of the account to check.
           * @return           True if the account is whitelisted by the specified collection, false otherwise.
           */
          function isAccountWhitelistedByCollection(address collection, address account) external view returns (bool) {
              return isAccountWhitelisted(collectionSecurityPolicies[collection].listId, account);
          }
          /**
           * @notice Check if an account is an authorizer of a specified collection.
           * @param collection The address of the collection.
           * @param account    The address of the account to check.
           * @return           True if the account is an authorizer by the specified collection, false otherwise.
           */
          function isAccountAuthorizerOfCollection(address collection, address account) external view returns (bool) {
              return isAccountAuthorizer(collectionSecurityPolicies[collection].listId, account);
          }
          /**
           * @notice Check if an account is frozen for a specified collection.
           * @param collection The address of the collection.
           * @param account    The address of the account to check.
           * @return           True if the account is frozen by the specified collection, false otherwise.
           */
          function isAccountFrozenForCollection(address collection, address account) external view returns (bool) {
              return frozenAccounts[collection].nonEnumerableAccounts[account];
          }
          /**
           * @notice Check if a codehash is blacklisted by a specified collection.
           * @param collection The address of the collection.
           * @param codehash   The codehash to check.
           * @return           True if the codehash is blacklisted by the specified collection, false otherwise.
           */
          function isCodeHashBlacklistedByCollection(address collection, bytes32 codehash) external view returns (bool) {
              return isCodeHashBlacklisted(collectionSecurityPolicies[collection].listId, codehash);
          }
          /**
           * @notice Check if a codehash is whitelisted by a specified collection.
           * @param collection The address of the collection.
           * @param codehash   The codehash to check.
           * @return           True if the codehash is whitelisted by the specified collection, false otherwise.
           */
          function isCodeHashWhitelistedByCollection(address collection, bytes32 codehash) external view returns (bool) {
              return isCodeHashWhitelisted(collectionSecurityPolicies[collection].listId, codehash);
          }
          /// @notice Returns true if the specified account has verified a signature on the registry, false otherwise.
          function isVerifiedEOA(address account) public view returns (bool) {
              return IEOARegistry(_eoaRegistry).isVerifiedEOA(account);
          }
          /// @notice ERC-165 Interface Support
          /// @dev    Do not remove LEGACY from this contract or future contracts.  
          ///         Doing so will break backwards compatibility with V1 and V2 creator tokens.
          function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
              return
                  interfaceId == LEGACY_TRANSFER_VALIDATOR_INTERFACE_ID ||
                  interfaceId == type(ITransferValidator).interfaceId ||
                  interfaceId == type(IPermitC).interfaceId ||
                  interfaceId == type(IEOARegistry).interfaceId ||
                  super.supportsInterface(interfaceId);
          }
          /*************************************************************************/
          /*                                HELPERS                                */
          /*************************************************************************/
          /**
           * @notice Reverts the transaction if the caller is not the owner or assigned the default
           * @notice admin role of the contract at `tokenAddress`.
           *
           * @dev    Throws when the caller is neither owner nor assigned the default admin role.
           * 
           * @param tokenAddress The contract address of the token to check permissions for.
           */
          function _requireCallerIsNFTOrContractOwnerOrAdmin(address tokenAddress) internal view {
              address caller = msg.sender;
              
              if(caller == tokenAddress) {
                  return;
              }
              (address contractOwner,) = _safeOwner(tokenAddress);
              if(caller == contractOwner) {
                  return;
              }
              (bool callerIsContractAdmin,) = _safeHasRole(tokenAddress, DEFAULT_ACCESS_CONTROL_ADMIN_ROLE, caller);
              if(callerIsContractAdmin) {
                  return;
              }
              revert CreatorTokenTransferValidator__CallerMustHaveElevatedPermissionsForSpecifiedNFT();
          }
          /**
           * @notice Copies all addresses in `ptrFromList` to `ptrToList`.
           * 
           * @dev    This function will copy all addresses from one list to another list.
           * @dev    Note: If used to copy adddresses to an existing list the current list contents will not be
           * @dev    deleted before copying. New addresses will be appeneded to the end of the list and the
           * @dev    non-enumerable mapping key value will be set to true.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Addresses in from list that are not already present in to list are added to the to list.
           *      2. Emits an `AddedAccountToList` event for each address copied to the list.
           * 
           * @param  listType          The type of list addresses are being copied from and to.
           * @param  destinationListId The id of the list being copied to.
           * @param  ptrFromList       The storage pointer for the list being copied from.
           * @param  ptrToList         The storage pointer for the list being copied to.
           */
          function _copyAddressSet(
              uint8 listType,
              uint120 destinationListId,
              List storage ptrFromList,
              List storage ptrToList
          ) private {
              EnumerableSet.AddressSet storage ptrFromSet = ptrFromList.enumerableAccounts;
              EnumerableSet.AddressSet storage ptrToSet = ptrToList.enumerableAccounts;
              mapping (address => bool) storage ptrToNonEnumerableSet = ptrToList.nonEnumerableAccounts;
              uint256 sourceLength = ptrFromSet.length();
              address account;
              for (uint256 i = 0; i < sourceLength;) {
                  account = ptrFromSet.at(i); 
                  if (ptrToSet.add(account)) {
                      emit AddedAccountToList(listType, destinationListId, account);
                      ptrToNonEnumerableSet[account] = true;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Copies all codehashes in `ptrFromList` to `ptrToList`.
           * 
           * @dev    This function will copy all codehashes from one list to another list.
           * @dev    Note: If used to copy codehashes to an existing list the current list contents will not be
           * @dev    deleted before copying. New codehashes will be appeneded to the end of the list and the
           * @dev    non-enumerable mapping key value will be set to true.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes in from list that are not already present in to list are added to the to list.
           *      2. Emits an `AddedCodeHashToList` event for each codehash copied to the list.
           * 
           * @param  listType          The type of list codehashes are being copied from and to.
           * @param  destinationListId The id of the list being copied to.
           * @param  ptrFromList       The storage pointer for the list being copied from.
           * @param  ptrToList         The storage pointer for the list being copied to.
           */
          function _copyBytes32Set(
              uint8 listType,
              uint120 destinationListId,
              List storage ptrFromList,
              List storage ptrToList
          ) private {
              EnumerableSet.Bytes32Set storage ptrFromSet = ptrFromList.enumerableCodehashes;
              EnumerableSet.Bytes32Set storage ptrToSet = ptrToList.enumerableCodehashes;
              mapping (bytes32 => bool) storage ptrToNonEnumerableSet = ptrToList.nonEnumerableCodehashes;
              uint256 sourceLength = ptrFromSet.length();
              bytes32 codehash;
              for (uint256 i = 0; i < sourceLength;) {
                  codehash = ptrFromSet.at(i);
                  if (ptrToSet.add(codehash)) {
                      emit AddedCodeHashToList(listType, destinationListId, codehash);
                      ptrToNonEnumerableSet[codehash] = true;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Adds one or more accounts to a list.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts that were not previously in the list are added to the list.
           *      2. An `AddedAccountToList` event is emitted for each account that was not
           *         previously on the list.
           * 
           * @param list     The storage pointer for the list to add accounts to.
           * @param listType The type of list the accounts are being added to.
           * @param id       The id of the list the accounts are being added to.
           * @param accounts An array of accounts to add to the list.
           */
          function _addAccountsToList(
              List storage list,
              uint8 listType,
              uint120 id,
              address[] calldata accounts
          ) internal onlyListOwner(id) {
              address account;
              for (uint256 i = 0; i < accounts.length;) {
                  account = accounts[i];
                  if (list.enumerableAccounts.add(account)) {
                      emit AddedAccountToList(listType, id, account);
                      list.nonEnumerableAccounts[account] = true;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Adds one or more codehashes to a list.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes that were not previously in the list are added to the list.
           *      2. An `AddedCodeHashToList` event is emitted for each codehash that was not
           *         previously on the list.
           * 
           * @param list        The storage pointer for the list to add codehashes to.
           * @param listType    The type of list the codehashes are being added to.
           * @param id          The id of the list the codehashes are being added to.
           * @param codehashes  An array of codehashes to add to the list.
           */
          function _addCodeHashesToList(
              List storage list,
              uint8 listType,
              uint120 id,
              bytes32[] calldata codehashes
          ) internal onlyListOwner(id) {
              bytes32 codehash;
              for (uint256 i = 0; i < codehashes.length;) {
                  codehash = codehashes[i];
                  if (list.enumerableCodehashes.add(codehash)) {
                      emit AddedCodeHashToList(listType, id, codehash);
                      list.nonEnumerableCodehashes[codehash] = true;
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Removes one or more accounts from a list.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Accounts that were previously in the list are removed from the list.
           *      2. An `RemovedAccountFromList` event is emitted for each account that was
           *         previously on the list.
           * 
           * @param list        The storage pointer for the list to remove accounts from.
           * @param listType    The type of list the accounts are being removed from.
           * @param id          The id of the list the accounts are being removed from.
           * @param accounts    An array of accounts to remove from the list.
           */
          function _removeAccountsFromList(
              List storage list, 
              uint8 listType,
              uint120 id, 
              address[] memory accounts
          ) internal onlyListOwner(id) {
              address account;
              for (uint256 i = 0; i < accounts.length;) {
                  account = accounts[i];
                  if (list.enumerableAccounts.remove(account)) {
                      emit RemovedAccountFromList(listType, id, account);
                      delete list.nonEnumerableAccounts[account];
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Removes one or more codehashes from a list.
           * 
           * @dev <h4>Postconditions:</h4>
           *      1. Codehashes that were previously in the list are removed from the list.
           *      2. An `RemovedCodeHashFromList` event is emitted for each codehash that was
           *         previously on the list.
           * 
           * @param list        The storage pointer for the list to remove codehashes from.
           * @param listType    The type of list the codehashes are being removed from.
           * @param id          The id of the list the codehashes are being removed from.
           * @param codehashes  An array of codehashes to remove from the list.
           */
          function _removeCodeHashesFromList(
              List storage list, 
              uint8 listType, 
              uint120 id, 
              bytes32[] calldata codehashes
          ) internal onlyListOwner(id) {
              bytes32 codehash;
              for (uint256 i = 0; i < codehashes.length;) {
                  codehash = codehashes[i];
                  if (list.enumerableCodehashes.remove(codehash)) {
                      emit RemovedCodeHashFromList(listType, id, codehash);
                      delete list.nonEnumerableCodehashes[codehash];
                  }
                  unchecked {
                      ++i;
                  }
              }
          }
          /**
           * @notice Sets the owner of list `id` to `newOwner`.
           * 
           * @dev    Throws when the caller is not the owner of the list.
           * 
           * @dev    <h4>Postconditions:</h4>
           *         1. The owner of list `id` is set to `newOwner`.
           *         2. Emits a `ReassignedListOwnership` event.
           * 
           * @param id       The id of the list to reassign ownership of.
           * @param newOwner The account to assign ownership of the list to.
           */
          function _reassignOwnershipOfList(uint120 id, address newOwner) private {
              _requireCallerOwnsList(id);
              listOwners[id] = newOwner;
              emit ReassignedListOwnership(id, newOwner);
          }
          /**
           * @notice Requires the caller to be the owner of list `id`.
           * 
           * @dev    Throws when the caller is not the owner of the list.
           * 
           * @param id  The id of the list to check ownership of.
           */
          function _requireCallerOwnsList(uint120 id) private view {
              if (msg.sender != listOwners[id]) {
                  revert CreatorTokenTransferValidator__CallerDoesNotOwnList();
              }
          }
          /**
           * @dev Internal function used to efficiently retrieve the code length of `account`.
           * 
           * @param account The address to get the deployed code length for.
           * 
           * @return length The length of deployed code at the address.
           */
          function _getCodeLengthAsm(address account) internal view returns (uint256 length) {
              assembly { length := extcodesize(account) }
          }
          /**
           * @dev Internal function used to efficiently retrieve the codehash of `account`.
           * 
           * @param account The address to get the deployed codehash for.
           * 
           * @return codehash The codehash of the deployed code at the address.
           */
          function _getCodeHashAsm(address account) internal view returns (bytes32 codehash) {
              assembly { codehash := extcodehash(account) }
          }
          /**
           * @dev Hook that is called before any permitted token transfer that goes through Permit-C.
           *      Applies the collection transfer policy, using the operator that called Permit-C as the caller.
           *      This allows creator token standard protections to extend to permitted transfers.
           * 
           * @param token  The collection address of the token being transferred.
           * @param from   The address of the token owner.
           * @param to     The address of the token receiver.
           * @param id     The token id being transferred.
           */
          function _beforeTransferFrom(
              uint256 tokenType,
              address token, 
              address from, 
              address to, 
              uint256 id, 
              uint256 /*amount*/
          ) internal override returns (bool isError) {
              (bytes4 selector, uint16 collectionTokenType) = _validateTransfer(_callerAuthorizedCheckToken, token, msg.sender, from, to, id);
              if (collectionTokenType == DEFAULT_TOKEN_TYPE || collectionTokenType == tokenType) {
                  isError = SELECTOR_NO_ERROR != selector;
              } else {
                  revert CreatorTokenTransferValidator__TokenTypesDoNotMatch();
              }
          }
          /**
           * @notice Apply the collection transfer policy to a transfer operation of a creator token.
           *
           * @dev If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
           *      _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
           *      that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
           *
           * @dev The order of checking whitelisted accounts, authorized operator check and whitelisted codehashes
           *      is very deliberate.  The order of operations is determined by the most frequently used settings that are
           *      expected in the wild.
           *
           * @dev Throws when the collection has enabled account freezing mode and either the `from` or `to` addresses
           *      are on the list of frozen accounts for the collection.
           * @dev Throws when the collection is set to Level 9 - Soulbound Token.
           * @dev Throws when the receiver has deployed code and isn't whitelisted, if ReceiverConstraints.NoCode is set
           *      and the transfer is not approved by an authorizer for the collection.
           * @dev Throws when the receiver has never verified a signature to prove they are an EOA and the receiver
           *      isn't whitelisted, if the ReceiverConstraints.EOA is set and the transfer is not approved by an 
           *      authorizer for the collection..
           * @dev Throws when `msg.sender` is blacklisted, if CallerConstraints.OperatorBlacklistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when `msg.sender` isn't whitelisted, if CallerConstraints.OperatorWhitelistEnableOTC is set, unless
           *      `msg.sender` is also the `from` address or the transfer is approved by an authorizer for the collection.
           * @dev Throws when neither `msg.sender` nor `from` are whitelisted, if 
           *      CallerConstraints.OperatorWhitelistDisableOTC is set and the transfer 
           *      is not approved by an authorizer for the collection.
           *
           * @dev <h4>Postconditions:</h4>
           *      1. Transfer is allowed or denied based on the applied transfer policy.
           *
           * @param collection  The collection address of the token being transferred.
           * @param caller      The address initiating the transfer.
           * @param from        The address of the token owner.
           * @param to          The address of the token receiver.
           * @param tokenId     The token id being transferred.
           * 
           * @return The selector value for an error if the transfer is not allowed, `SELECTOR_NO_ERROR` if the transfer is allowed.
           */
          function _validateTransfer(
              function(address,address,uint256) internal view returns(bool) _callerAuthorizedParam,
              address collection, 
              address caller, 
              address from, 
              address to,
              uint256 tokenId
          ) internal view returns (bytes4,uint16) {
              if (caller == address(this)) { 
                  // If the caller is self (Permit-C Processor) it means we have already applied operator validation in the 
                  // _beforeTransferFrom callback.  In this case, the security policy was already applied and the operator
                  // that used the Permit-C processor passed the security policy check and transfer can be safely allowed.
                  return (SELECTOR_NO_ERROR, DEFAULT_TOKEN_TYPE);
              }
              CollectionSecurityPolicyV3 storage collectionSecurityPolicy = collectionSecurityPolicies[collection];
              uint120 listId = collectionSecurityPolicy.listId;
              (uint256 callerConstraints, uint256 receiverConstraints) = 
                  transferSecurityPolicies(collectionSecurityPolicy.transferSecurityLevel);
              if (collectionSecurityPolicy.enableAccountFreezingMode) {
                  AccountList storage frozenAccountList = frozenAccounts[collection];
                  
                  if (frozenAccountList.nonEnumerableAccounts[from]) {
                      return (CreatorTokenTransferValidator__SenderAccountIsFrozen.selector, DEFAULT_TOKEN_TYPE);
                  }
                  if (frozenAccountList.nonEnumerableAccounts[to]) {
                      return (CreatorTokenTransferValidator__ReceiverAccountIsFrozen.selector, DEFAULT_TOKEN_TYPE);
                  }
              }
              if (callerConstraints == CALLER_CONSTRAINTS_SBT) {
                  return (CreatorTokenTransferValidator__TokenIsSoulbound.selector, DEFAULT_TOKEN_TYPE);
              }
              List storage whitelist = whitelists[listId];
              if (receiverConstraints == RECEIVER_CONSTRAINTS_NO_CODE) {
                  if (_getCodeLengthAsm(to) > 0) {
                      if (!whitelist.nonEnumerableAccounts[to]) {
                          // Cache _callerAuthorizedParam on stack to avoid stack too deep
                          function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                          if(!_callerAuthorized(collection, caller, tokenId)) {
                              if (!whitelist.nonEnumerableCodehashes[_getCodeHashAsm(to)]) {
                                  return (CreatorTokenTransferValidator__ReceiverMustNotHaveDeployedCode.selector, DEFAULT_TOKEN_TYPE);
                              }
                          }
                      }
                  }
              } else if (receiverConstraints == RECEIVER_CONSTRAINTS_EOA) {
                  if (!isVerifiedEOA(to)) {
                      if (!whitelist.nonEnumerableAccounts[to]) {
                          // Cache _callerAuthorizedParam on stack to avoid stack too deep
                          function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                          if(!_callerAuthorized(collection, caller, tokenId)) {
                              if (!whitelist.nonEnumerableCodehashes[_getCodeHashAsm(to)]) {
                                  return (CreatorTokenTransferValidator__ReceiverProofOfEOASignatureUnverified.selector, DEFAULT_TOKEN_TYPE);
                              }
                          }
                      }
                  }
              }
              if (caller == from) {
                  if (callerConstraints != CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
              }
              if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC) {
                  // Cache _callerAuthorizedParam on stack to avoid stack too deep
                  function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                  if(_callerAuthorized(collection, caller, tokenId)) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  List storage blacklist = blacklists[listId];
                  if (blacklist.nonEnumerableAccounts[caller]) {
                      return (CreatorTokenTransferValidator__OperatorIsBlacklisted.selector, DEFAULT_TOKEN_TYPE);
                  }
                  if (blacklist.nonEnumerableCodehashes[_getCodeHashAsm(caller)]) {
                      return (CreatorTokenTransferValidator__OperatorIsBlacklisted.selector, DEFAULT_TOKEN_TYPE);
                  }
              } else if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC) {
                  if (whitelist.nonEnumerableAccounts[caller]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  // Cache _callerAuthorizedParam on stack to avoid stack too deep
                  function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                  if( _callerAuthorized(collection, caller, tokenId)) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  if (whitelist.nonEnumerableCodehashes[_getCodeHashAsm(caller)]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  return (CreatorTokenTransferValidator__CallerMustBeWhitelisted.selector, DEFAULT_TOKEN_TYPE);
              } else if (callerConstraints == CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC) {
                  mapping(address => bool) storage accountWhitelist = whitelist.nonEnumerableAccounts;
                  if (accountWhitelist[caller]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  if (accountWhitelist[from]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  // Cache _callerAuthorizedParam on stack to avoid stack too deep
                  function(address,address,uint256) internal view returns(bool) _callerAuthorized = _callerAuthorizedParam;
                  if(_callerAuthorized(collection, caller, tokenId)) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  mapping(bytes32 => bool) storage codehashWhitelist = whitelist.nonEnumerableCodehashes;
                  // Cache caller on stack to avoid stack too deep
                  address tmpAddress = caller;
                  if (codehashWhitelist[_getCodeHashAsm(tmpAddress)]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  // Cache from on stack to avoid stack too deep
                  tmpAddress = from;
                  if (codehashWhitelist[_getCodeHashAsm(tmpAddress)]) {
                      return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
                  }
                  return (CreatorTokenTransferValidator__CallerMustBeWhitelisted.selector, DEFAULT_TOKEN_TYPE);
              }
              return (SELECTOR_NO_ERROR, collectionSecurityPolicy.tokenType);
          }
          /**
           * @dev Internal function used to efficiently revert with a custom error selector.
           *
           * @param errorSelector The error selector to revert with.
           */
          function _revertCustomErrorSelectorAsm(bytes4 errorSelector) internal pure {
              assembly {
                  mstore(0x00, errorSelector)
                  revert(0x00, 0x04)
              }
          }
          /**
           * @dev Internal function used to check if authorization mode can be activated for a transfer.
           * 
           * @dev Throws when the collection has not enabled authorization mode.
           * @dev Throws when the wildcard operator is being set for a collection that does not
           *      allow wildcard operators.
           * @dev Throws when the authorizer is not in the list of approved authorizers for
           *      the collection.
           * 
           * @param collection  The collection address to activate authorization mode for a transfer.
           * @param operator    The operator specified by the authorizer to allow transfers.
           * @param authorizer  The address of the authorizer making the call.
           */
          function _checkCollectionAllowsAuthorizerAndOperator(
              address collection, 
              address operator, 
              address authorizer
          ) internal view {
              CollectionSecurityPolicyV3 storage collectionSecurityPolicy = collectionSecurityPolicies[collection];
              if (collectionSecurityPolicy.disableAuthorizationMode) {
                  revert CreatorTokenTransferValidator__AuthorizationDisabledForCollection();
              }
              if (collectionSecurityPolicy.authorizersCannotSetWildcardOperators) {
                  if (operator == WILDCARD_OPERATOR_ADDRESS) {
                      revert CreatorTokenTransferValidator__WildcardOperatorsCannotBeAuthorizedForCollection();
                  }
              }
              if (!authorizers[collectionSecurityPolicy.listId].nonEnumerableAccounts[authorizer]) {
                  revert CreatorTokenTransferValidator__CallerMustBeAnAuthorizer();
              }
          }
          /**
           * @dev Modifier to apply the allowed authorizer and operator for collection checks.
           * 
           * @dev Throws when the collection has not enabled authorization mode.
           * @dev Throws when the wildcard operator is being set for a collection that does not
           *      allow wildcard operators.
           * @dev Throws when the authorizer is not in the list of approved authorizers for
           *      the collection.
           * 
           * @param collection  The collection address to activate authorization mode for a transfer.
           * @param operator    The operator specified by the authorizer to allow transfers.
           * @param authorizer  The address of the authorizer making the call.
           */
          modifier whenAuthorizerAndOperatorEnabledForCollection(
              address collection, 
              address operator, 
              address authorizer
          ) {
              _checkCollectionAllowsAuthorizerAndOperator(collection, operator, authorizer);
              _;
          }
          /**
           * @dev Internal function for setting the authorized operator in storage for a token and collection.
           * 
           * @param operator         The allowed operator for an authorized transfer.
           * @param collection       The address of the collection that the operator is authorized for.
           * @param tokenId          The id of the token that is authorized.
           * @param allowAnyTokenId  Flag if the authorizer is enabling transfers for any token id
           */
          function _setOperatorInTransientStorage(
              address operator,
              address collection, 
              uint256 tokenId,
              bool allowAnyTokenId
          ) internal whenAuthorizerAndOperatorEnabledForCollection(collection, operator, msg.sender) {
              _setTstorish(_getTransientOperatorSlot(collection), (allowAnyTokenId ? 1 << 255 : 0) | uint256(uint160(operator)));
              _setTstorish(_getTransientOperatorSlot(collection, tokenId), uint256(uint160(operator)));
          }
          /**
           * @dev Internal function to check if a caller is an authorized operator for the token being transferred.
           * 
           * @param caller     The caller of the token transfer.
           * @param collection The collection address of the token being transferred.
           * @param tokenId    The id of the token being transferred.
           * 
           * @return isAuthorized  True if the caller is authorized to transfer the token, false otherwise.
           */
          function _callerAuthorizedCheckToken(
            address collection,
              address caller,
              uint256 tokenId
          ) internal view returns (bool isAuthorized) {
              uint256 slotValue;
              (isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection, tokenId));
              if (isAuthorized) return true;
              (isAuthorized, slotValue) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
              isAuthorized = isAuthorized && slotValue >> 255 == 1;
          }
          /**
           * @dev Internal function to check if a caller is an authorized operator for the collection being transferred.
           * 
           * @param caller     The caller of the token transfer.
           * @param collection The collection address of the token being transferred.
           * 
           * @return isAuthorized  True if the caller is authorized to transfer the collection, false otherwise.
           */
          function _callerAuthorizedCheckCollection(
            address collection,
              address caller,
              uint256 /*tokenId*/
          ) internal view returns (bool isAuthorized) {
              (isAuthorized, ) = _callerAuthorized(caller, _getTransientOperatorSlot(collection));
          }
          /**
           * @dev Internal function to check if a caller is an authorized operator. 
           * @dev This overload of `_callerAuthorized` checks a specific storage slot for the caller address.
           * 
           * @param caller     The caller of the token transfer.
           * @param slot       The storage slot to check for the caller address.
           * 
           * @return isAuthorized  True if the caller is authorized to transfer the token, false otherwise.
           * @return slotValue     The transient storage value in `slot`, used to check for allow any token id flag if necessary.
           */
          function _callerAuthorized(address caller, uint256 slot) internal view returns (bool isAuthorized, uint256 slotValue) {
              slotValue = _getTstorish(slot);
              address authorizedOperator = address(uint160(slotValue));
              isAuthorized = authorizedOperator == WILDCARD_OPERATOR_ADDRESS || authorizedOperator == caller;
          }
          /**
           * @dev Internal function used to compute the transient storage slot for the authorized 
           *      operator of a token in a collection.
           * 
           * @param collection The collection address of the token being transferred.
           * @param tokenId    The id of the token being transferred.
           * 
           * @return operatorSlot The storage slot location for the authorized operator value.
           */
          function _getTransientOperatorSlot(
              address collection, 
              uint256 tokenId
          ) internal pure returns (uint256 operatorSlot) {
              assembly {
                  mstore(0x00, collection)
                  mstore(0x20, tokenId)
                  operatorSlot := shr(4, keccak256(0x00, 0x40))
             }
          }
          /**
           * @dev Internal function used to compute the transient storage slot for the authorized operator of a collection.
           * 
           * @param collection The collection address of the token being transferred.
           * 
           * @return operatorSlot The storage slot location for the authorized operator value.
           */
          function _getTransientOperatorSlot(address collection) internal pure returns (uint256 operatorSlot) {
              return uint256(uint160(collection));
          }
          /**
           * @dev A gas efficient, and fallback-safe way to call the owner function on a token contract.
           *      This will get the owner if it exists - and when the function is unimplemented, the
           *      presence of a fallback function will not result in halted execution.
           * 
           * @param tokenAddress  The address of the token collection to get the owner of.
           * 
           * @return owner   The owner of the token collection contract.
           * @return isError True if there was an error in retrieving the owner, false if the call was successful.
           */
          function _safeOwner(
              address tokenAddress
          ) internal view returns(address owner, bool isError) {
              assembly {
                  function _callOwner(_tokenAddress) -> _owner, _isError {
                      mstore(0x00, 0x8da5cb5b)
                      if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _tokenAddress, 0x1C, 0x04, 0x00, 0x20)) {
                          _owner := mload(0x00)
                          leave
                      }
                      _isError := true
                  }
                  owner, isError := _callOwner(tokenAddress)
              }
          }
          
          /**
           * @dev A gas efficient, and fallback-safe way to call the hasRole function on a token contract.
           *      This will check if the account `hasRole` if `hasRole` exists - and when the function is unimplemented, the
           *      presence of a fallback function will not result in halted execution.
           * 
           * @param tokenAddress  The address of the token collection to call hasRole on.
           * @param role          The role to check if the account has on the collection.
           * @param account       The address of the account to check if they have a specified role.
           * 
           * @return hasRole The owner of the token collection contract.
           * @return isError True if there was an error in retrieving the owner, false if the call was successful.
           */
          function _safeHasRole(
              address tokenAddress,
              bytes32 role,
              address account
          ) internal view returns(bool hasRole, bool isError) {
              assembly {
                  function _callHasRole(_tokenAddress, _role, _account) -> _hasRole, _isError {
                      let ptr := mload(0x40)
                      mstore(0x40, add(ptr, 0x60))
                      mstore(ptr, 0x91d14854)
                      mstore(add(0x20, ptr), _role)
                      mstore(add(0x40, ptr), _account)
                      if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _tokenAddress, add(ptr, 0x1C), 0x44, 0x00, 0x20)) {
                          _hasRole := mload(0x00)
                          leave
                      }
                      _isError := true
                  }
                  hasRole, isError := _callHasRole(tokenAddress, role, account)
              }
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /**
       * @dev Constant definitions for receiver constraints used by the transfer validator.
       */
      /// @dev No constraints on the receiver of a token.
      uint256 constant RECEIVER_CONSTRAINTS_NONE = 0;
      /// @dev Token receiver cannot have deployed code.
      uint256 constant RECEIVER_CONSTRAINTS_NO_CODE = 1;
      /// @dev Token receiver must be a verified EOA with the EOA Registry.
      uint256 constant RECEIVER_CONSTRAINTS_EOA = 2;
      /// @dev Token is a soulbound token and cannot be transferred.
      uint256 constant RECEIVER_CONSTRAINTS_SBT = 3;
      /**
       * @dev Constant definitions for caller constraints used by the transfer validator.
       */
      /// @dev No constraints on the caller of a token transfer.
      uint256 constant CALLER_CONSTRAINTS_NONE = 0;
      /// @dev Caller of a token transfer must not be on the blacklist unless it is an OTC transfer.
      uint256 constant CALLER_CONSTRAINTS_OPERATOR_BLACKLIST_ENABLE_OTC = 1;
      /// @dev Caller of a token transfer must be on the whitelist unless it is an OTC transfer.
      uint256 constant CALLER_CONSTRAINTS_OPERATOR_WHITELIST_ENABLE_OTC = 2;
      /// @dev Caller of a token transfer must be on the whitelist.
      uint256 constant CALLER_CONSTRAINTS_OPERATOR_WHITELIST_DISABLE_OTC = 3;
      /// @dev Token is a soulbound token and cannot be transferred.
      uint256 constant CALLER_CONSTRAINTS_SBT = 4;
      /**
       * @dev Constant definitions for transfer security levels used by the transfer validator
       *      to define what receiver and caller constraints are applied to a transfer.
       */
      /// @dev Recommend Security Level -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: None
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_RECOMMENDED = 0;
      /// @dev Security Level One -
      ///        Caller Constraints: None
      ///        Receiver Constraints: None
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_ONE = 1;
      /// @dev Security Level Two -
      ///        Caller Constraints: Operator Blacklist
      ///        Receiver Constraints: None
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_TWO = 2;
      /// @dev Security Level Three -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: None
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_THREE = 3;
      /// @dev Security Level Four -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: None
      ///        OTC: Not Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_FOUR = 4;
      /// @dev Security Level Five -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: No Code
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_FIVE = 5;
      /// @dev Security Level Six -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: Verified EOA
      ///        OTC: Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_SIX = 6;
      /// @dev Security Level Seven -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: No Code
      ///        OTC: Not Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_SEVEN = 7;
      /// @dev Security Level Eight -
      ///        Caller Constraints: Operator Whitelist
      ///        Receiver Constraints: Verified EOA
      ///        OTC: Not Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_EIGHT = 8;
      /// @dev Security Level Nine -
      ///        Soulbound Token, No Transfers Allowed
      uint8 constant TRANSFER_SECURITY_LEVEL_NINE = 9;
      /// @dev List type is a blacklist.
      uint8 constant LIST_TYPE_BLACKLIST = 0;
      /// @dev List type is a whitelist.
      uint8 constant LIST_TYPE_WHITELIST = 1;
      /// @dev List type is authorizers.
      uint8 constant LIST_TYPE_AUTHORIZERS = 2;
      /// @dev Constant value for the no error selector.
      bytes4 constant SELECTOR_NO_ERROR = bytes4(0x00000000);// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
      interface IEOARegistry is IERC165 {
          function isVerifiedEOA(address account) external view returns (bool);
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      interface ITransferValidator {
          function applyCollectionTransferPolicy(address caller, address from, address to) external view;
          function validateTransfer(address caller, address from, address to) external view;
          function validateTransfer(address caller, address from, address to, uint256 tokenId) external view;
          function validateTransfer(address caller, address from, address to, uint256 tokenId, uint256 amount) external;
          function beforeAuthorizedTransfer(address operator, address token, uint256 tokenId) external;
          function afterAuthorizedTransfer(address token, uint256 tokenId) external;
          function beforeAuthorizedTransfer(address operator, address token) external;
          function afterAuthorizedTransfer(address token) external;
          function beforeAuthorizedTransfer(address token, uint256 tokenId) external;
          function beforeAuthorizedTransferWithAmount(address token, uint256 tokenId, uint256 amount) external;
          function afterAuthorizedTransferWithAmount(address token, uint256 tokenId) external;
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /**
       * @dev Defines constraints for staking tokens in token wrapper contracts.
       */
      enum StakerConstraints {
          // 0: No constraints applied to staker.
          None,
          // 1: Transaction originator must be the address that will receive the wrapped tokens.
          CallerIsTxOrigin,
          // 2: Address that will receive the wrapped tokens must be a verified EOA.
          EOA
      }
      /**
       * @dev Defines the security policy for a token collection in Creator Token Standards V2.
       * 
       * @dev **transferSecurityLevel**: The transfer security level set for the collection.
       * @dev **listId**: The list id that contains the blacklist and whitelist to apply to the collection.
       * @dev **enableGraylisting**: If true, graylisting will be enabled for the collection.
       */
      struct CollectionSecurityPolicyV3 {
          bool disableAuthorizationMode;
          bool authorizersCannotSetWildcardOperators;
          uint8 transferSecurityLevel;
          uint120 listId;
          bool enableAccountFreezingMode;
          uint16 tokenType;
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.24;
      import "@openzeppelin/contracts/access/Ownable.sol";
      contract CreatorTokenTransferValidatorConfiguration is Ownable {
          error CreatorTokenTransferValidatorConfiguration__ConfigurationNotInitialized();
          bool configurationInitialized;
          uint256 private nativeValueToCheckPauseState;
          constructor(address defaultOwner) {
              _transferOwnership(defaultOwner);
          }
          function setNativeValueToCheckPauseState(uint256 _nativeValueToCheckPauseState) external onlyOwner {
              nativeValueToCheckPauseState = _nativeValueToCheckPauseState;
              configurationInitialized = true;
          }
          function getNativeValueToCheckPauseState() external view returns(uint256 _nativeValueToCheckPauseState) {
              if (!configurationInitialized) {
                  revert CreatorTokenTransferValidatorConfiguration__ConfigurationNotInitialized();
              }
              _nativeValueToCheckPauseState = nativeValueToCheckPauseState;
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.24;
      import "./Errors.sol";
      import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
      import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol";
      import {IERC1155} from "@openzeppelin/contracts/interfaces/IERC1155.sol";
      import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
      import {Ownable} from "./openzeppelin-optimized/Ownable.sol";
      import {EIP712} from "./openzeppelin-optimized/EIP712.sol";
      import {
          ZERO_BYTES32,
          ZERO, 
          ONE, 
          ORDER_STATE_OPEN,
          ORDER_STATE_FILLED,
          ORDER_STATE_CANCELLED,
          SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB,
          PERMIT_ORDER_ADVANCED_TYPEHASH_STUB,
          UPPER_BIT_MASK,
          TOKEN_TYPE_ERC1155,
          TOKEN_TYPE_ERC20,
          TOKEN_TYPE_ERC721,
          PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721,
          PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155,
          PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20,
          PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721,
          PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155,
          PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20,
          PAUSABLE_ORDER_TRANSFER_FROM_ERC1155,
          PAUSABLE_ORDER_TRANSFER_FROM_ERC20
      } from "./Constants.sol";
      import {PackedApproval, OrderFillAmounts} from "./DataTypes.sol";
      import {PermitHash} from './libraries/PermitHash.sol';
      import {IPermitC} from './interfaces/IPermitC.sol';
      import {CollateralizedPausableFlags} from './CollateralizedPausableFlags.sol';
      /*
                                                           @@@@@@@@@@@@@@             
                                                          @@@@@@@@@@@@@@@@@@(         
                                                         @@@@@@@@@@@@@@@@@@@@@        
                                                        @@@@@@@@@@@@@@@@@@@@@@@@      
                                                                 #@@@@@@@@@@@@@@      
                                                                     @@@@@@@@@@@@     
                                  @@@@@@@@@@@@@@*                    @@@@@@@@@@@@     
                                 @@@@@@@@@@@@@@@     @               @@@@@@@@@@@@     
                                @@@@@@@@@@@@@@@     @                @@@@@@@@@@@      
                               @@@@@@@@@@@@@@@     @@               @@@@@@@@@@@@      
                              @@@@@@@@@@@@@@@     #@@             @@@@@@@@@@@@/       
                              @@@@@@@@@@@@@@.     @@@@@@@@@@@@@@@@@@@@@@@@@@@         
                             @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@            
                            @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@             
                           @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                          @@@@@@@@@@@@@@@     @@@@@&%%%%%%%%&&@@@@@@@@@@@@@@          
                          @@@@@@@@@@@@@@      @@@@@               @@@@@@@@@@@         
                         @@@@@@@@@@@@@@@     @@@@@                 @@@@@@@@@@@        
                        @@@@@@@@@@@@@@@     @@@@@@                 @@@@@@@@@@@        
                       @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@        
                      @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@&        
                      @@@@@@@@@@@@@@     *@@@@@@@               (@@@@@@@@@@@@         
                     @@@@@@@@@@@@@@@     @@@@@@@@             @@@@@@@@@@@@@@          
                    @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                   @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            
                  @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              
                 .@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 
                 @@@@@@@@@@@@@@%     @@@@@@@@@@@@@@@@@@@@@@@@(                        
                @@@@@@@@@@@@@@@                                                       
               @@@@@@@@@@@@@@@                                                        
              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                         
             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                          
             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&                                          
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                           
       
      * @title PermitC
      * @custom:version 1.0.0
      * @author Limit Break, Inc.
      * @description Advanced approval management for ERC20, ERC721 and ERC1155 tokens
      *              allowing for single use permit transfers, time-bound approvals
      *              and order ID based transfers.
      */
      contract PermitC is Ownable, CollateralizedPausableFlags, EIP712, IPermitC {
          /**
           * @notice Map of approval details for the provided bytes32 hash to allow for multiple accessors
           *
           * @dev    keccak256(abi.encode(owner, tokenType, token, id, orderId, masterNonce)) => 
           * @dev        operator => (state, amount, expiration)
           * @dev    Utilized for stored approvals by an owner's direct call to `approve` and  
           * @dev    approvals by signature in `updateApprovalBySignature`. Both methods use a
           * @dev    bytes32(0) value for the `orderId`.
           */
          mapping(bytes32 => mapping(address => PackedApproval)) private _transferApprovals;
          /**
           * @notice Map of approval details for the provided bytes32 hash to allow for multiple accessors
           *
           * @dev    keccak256(abi.encode(owner, tokenType, token, id, orderId, masterNonce)) => 
           * @dev        operator => (state, amount, expiration)
           * @dev    Utilized for order approvals by `fillPermittedOrderERC20` and `fillPermittedOrderERC1155`
           * @dev    with the `orderId` provided by the sender.
           */
          mapping(bytes32 => mapping(address => PackedApproval)) private _orderApprovals;
          /**
           * @notice Map of registered additional data hashes for transfer permits.
           *
           * @dev    This is used to prevent someone from providing an invalid EIP712 envelope label
           * @dev    and tricking a user into signing a different message than they expect.
           */
          mapping(bytes32 => bool) private _registeredTransferHashes;
          /**
           * @notice Map of registered additional data hashes for order permits.
           *
           * @dev    This is used to prevent someone from providing an invalid EIP712 envelope label
           * @dev    and tricking a user into signing a different message than they expect.
           */
          mapping(bytes32 => bool) private _registeredOrderHashes;
          /// @dev Map of an address to a bitmap (slot => status)
          mapping(address => mapping(uint256 => uint256)) private _unorderedNonces;
          /**
           * @notice Master nonce used to invalidate all outstanding approvals for an owner
           *
           * @dev    owner => masterNonce
           * @dev    This is incremented when the owner calls lockdown()
           */
          mapping(address => uint256) private _masterNonces;
          constructor(
              string memory name,
              string memory version,
              address _defaultContractOwner,
              uint256 _nativeValueToCheckPauseState
          ) CollateralizedPausableFlags(_nativeValueToCheckPauseState) EIP712(name, version) {
              _transferOwnership(_defaultContractOwner);
          }
          /**
           * =================================================
           * ================= Modifiers =====================
           * =================================================
           */
          modifier onlyRegisteredTransferAdvancedTypeHash(bytes32 advancedPermitHash) {
              _requireTransferAdvancedPermitHashIsRegistered(advancedPermitHash);
              _;
          }
          modifier onlyRegisteredOrderAdvancedTypeHash(bytes32 advancedPermitHash) {
              _requireOrderAdvancedPermitHashIsRegistered(advancedPermitHash);
              _;
          }
          /**
           * =================================================
           * ============== Approval Transfers ===============
           * =================================================
           */
          /**
           * @notice Approve an operator to spend a specific token / ID combination
           * @notice This function is compatible with ERC20, ERC721 and ERC1155
           * @notice To give unlimited approval for ERC20 and ERC1155, set amount to type(uint200).max
           * @notice When approving an ERC721, you MUST set amount to `1`
           * @notice When approving an ERC20, you MUST set id to `0`
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Updates the approval for an operator to use an amount of a specific token / ID combination
           * @dev    2. If the expiration is 0, the approval is valid only in the context of the current block
           * @dev    3. If the expiration is not 0, the approval is valid until the expiration timestamp
           * @dev    4. If the provided amount is type(uint200).max, the approval is unlimited
           *
           * @param  tokenType  The type of token being approved - must be 20, 721 or 1155.
           * @param  token      The address of the token contract
           * @param  id         The token ID
           * @param  operator   The address of the operator
           * @param  amount     The amount of tokens to approve
           * @param  expiration The expiration timestamp of the approval
           */
          function approve(
              uint256 tokenType,
              address token, 
              uint256 id, 
              address operator, 
              uint200 amount, 
              uint48 expiration
          ) external {
              _requireValidTokenType(tokenType);
              _storeApproval(tokenType, token, id, amount, expiration, msg.sender, operator);
          }
          /**
           * @notice Use a signed permit to increase the allowance for a provided operator
           * @notice This function is compatible with ERC20, ERC721 and ERC1155
           * @notice To give unlimited approval for ERC20 and ERC1155, set amount to type(uint200).max
           * @notice When approving an ERC721, you MUST set amount to `1`
           * @notice When approving an ERC20, you MUST set id to `0`
           * @notice An `approvalExpiration` of zero is considered an atomic permit which will use the 
           * @notice current block time as the expiration time when storing the permit data.
           *
           * @dev    - Throws if the permit has expired
           * @dev    - Throws if the permit's nonce has already been used
           * @dev    - Throws if the permit signature is does not recover to the provided owner
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Updates the approval for an operator to use an amount of a specific token / ID combination
           * @dev    3. Sets the expiration of the approval to the expiration timestamp of the permit
           * @dev    4. If the provided amount is type(uint200).max, the approval is unlimited
           *
           * @param  tokenType            The type of token being approved - must be 20, 721 or 1155.
           * @param  token                Address of the token to approve
           * @param  id                   The token ID
           * @param  nonce                The nonce of the permit
           * @param  amount               The amount of tokens to approve
           * @param  operator             The address of the operator
           * @param  approvalExpiration   The expiration timestamp of the approval
           * @param  sigDeadline          The deadline timestamp for the permit signature
           * @param  owner                The owner of the tokens
           * @param  signedPermit         The permit signature, signed by the owner
           */
          function updateApprovalBySignature(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 nonce,
              uint200 amount,
              address operator,
              uint48 approvalExpiration,
              uint48 sigDeadline,
              address owner,
              bytes calldata signedPermit
          ) external {
              if (block.timestamp > sigDeadline) {
                  revert PermitC__ApprovalTransferPermitExpiredOrUnset();
              }
              _requireValidTokenType(tokenType);
              _checkAndInvalidateNonce(owner, nonce);
              _verifyPermitSignature(
                  _hashTypedDataV4(
                      PermitHash.hashOnChainApproval(
                          tokenType,
                          token,
                          id,
                          amount,
                          nonce,
                          operator,
                          approvalExpiration,
                          sigDeadline,
                          _masterNonces[owner]
                      )
                  ),
                  signedPermit, 
                  owner
              );
              // Expiration of zero is considered an atomic permit which is only valid in the 
              // current block.
              approvalExpiration = approvalExpiration == 0 ? uint48(block.timestamp) : approvalExpiration;
              _storeApproval(tokenType, token, id, amount, approvalExpiration, owner, operator);
          }
          /**
           * @notice Returns the amount of allowance an operator has and it's expiration for a specific token and id
           * @notice If the expiration on the allowance has expired, returns 0
           * @notice To retrieve allowance for ERC20, set id to `0`
           * 
           * @param  owner     The owner of the token
           * @param  operator  The operator of the token
           * @param  tokenType The type of token the allowance is for
           * @param  token     The address of the token contract
           * @param  id        The token ID
           *
           * @return allowedAmount The amount of allowance the operator has
           * @return expiration    The expiration timestamp of the allowance
           */
          function allowance(
              address owner, 
              address operator, 
              uint256 tokenType,
              address token, 
              uint256 id
          ) external view returns (uint256 allowedAmount, uint256 expiration) {
              return _allowance(_transferApprovals, owner, operator, tokenType, token, id, ZERO_BYTES32);
          }
          /**
           * =================================================
           * ================ Signed Transfers ===============
           * =================================================
           */
          /**
           * @notice Registers the combination of a provided string with the `SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB` 
           * @notice and `PERMIT_ORDER_ADVANCED_TYPEHASH_STUB` to create valid additional data hashes
           *
           * @dev    This function prevents malicious actors from changing the label of the EIP712 hash
           * @dev    to a value that would fool an external user into signing a different message.
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. The provided string is combined with the `SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB` string
           * @dev    2. The combined string is hashed using keccak256
           * @dev    3. The resulting hash is added to the `_registeredTransferHashes` mapping
           * @dev    4. The provided string is combined with the `PERMIT_ORDER_ADVANCED_TYPEHASH_STUB` string
           * @dev    5. The combined string is hashed using keccak256
           * @dev    6. The resulting hash is added to the `_registeredOrderHashes` mapping
           *
           * @param  additionalDataTypeString The string to register as a valid additional data hash
           */
          function registerAdditionalDataHash(string calldata additionalDataTypeString) external {
              _registeredTransferHashes[
                  keccak256(
                      bytes(
                          string.concat(
                              SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB, 
                              additionalDataTypeString
                          )
                      )
                  )
              ] = true;
              _registeredOrderHashes[
                  keccak256(
                      bytes(
                          string.concat(
                              PERMIT_ORDER_ADVANCED_TYPEHASH_STUB, 
                              additionalDataTypeString
                          )
                      )
                  )
              ] = true;
          }
          /**
           * @notice Transfer an ERC721 token from the owner to the recipient using a permit signature.
           *
           * @dev    Be advised that the permitted amount for ERC721 is always inferred to be 1, so signed permitted amount
           * @dev    MUST always be set to 1.
           *
           * @dev    - Throws if the permit is expired
           * @dev    - Throws if the nonce has already been used
           * @dev    - Throws if the permit is not signed by the owner
           * @dev    - Throws if the requested amount exceeds the permitted amount
           * @dev    - Throws if the provided token address does not implement ERC721 transferFrom function
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token from the owner to the recipient
           * @dev    2. The nonce of the permit is marked as used
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param token         The address of the token
           * @param id            The ID of the token
           * @param nonce         The nonce of the permit
           * @param expiration    The expiration timestamp of the permit
           * @param owner         The owner of the token
           * @param to            The address to transfer the tokens to
           * @param signedPermit  The permit signature, signed by the owner
           *
           * @return isError      True if the transfer failed, false otherwise
           */
          function permitTransferFromERC721(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 expiration,
              address owner,
              address to,
              bytes calldata signedPermit
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721);
              _checkPermitApproval(TOKEN_TYPE_ERC721, token, id, ONE, nonce, expiration, owner, ONE, signedPermit);
              isError = _transferFromERC721(owner, to, token, id);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Transfers an ERC721 token from the owner to the recipient using a permit signature
           * @notice This function includes additional data to verify on the signature, allowing
           * @notice protocols to extend the validation in one function call. NOTE: before calling this 
           * @notice function you MUST register the stub end of the additional data typestring using
           * @notice the `registerAdditionalDataHash` function.
           *
           * @dev    Be advised that the permitted amount for ERC721 is always inferred to be 1, so signed permitted amount
           * @dev    MUST always be set to 1.
           *
           * @dev    - Throws for any reason permitTransferFromERC721 would.
           * @dev    - Throws if the additional data does not match the signature
           * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
           * @dev    - Throws if the provided hash does not match the provided additional data
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token from the owner to the recipient
           * @dev    2. Performs any additional checks in the before and after hooks
           * @dev    3. The nonce of the permit is marked as used
           * 
           * @param  token                    The address of the token
           * @param  id                       The ID of the token
           * @param  nonce                    The nonce of the permit
           * @param  expiration               The expiration timestamp of the permit
           * @param  owner                    The owner of the token
           * @param  to                       The address to transfer the tokens to
           * @param  additionalData           The additional data to verify on the signature
           * @param  advancedPermitHash       The hash of the additional data
           * @param  signedPermit             The permit signature, signed by the owner
           *
           * @return isError                  True if the transfer failed, false otherwise
           */
          function permitTransferFromWithAdditionalDataERC721(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 expiration,
              address owner,
              address to,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
         ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721);
              _checkPermitApprovalWithAdditionalDataERC721(
                  token,
                  id,
                  ONE,
                  nonce,
                  expiration,
                  owner,
                  ONE,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
              isError = _transferFromERC721(owner, to, token, id);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Transfer an ERC1155 token from the owner to the recipient using a permit signature
           *
           * @dev    - Throws if the permit is expired
           * @dev    - Throws if the nonce has already been used
           * @dev    - Throws if the permit is not signed by the owner
           * @dev    - Throws if the requested amount exceeds the permitted amount
           * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. The nonce of the permit is marked as used
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param token           The address of the token
           * @param id              The ID of the token
           * @param nonce           The nonce of the permit
           * @param permitAmount    The amount of tokens permitted by the owner
           * @param expiration      The expiration timestamp of the permit
           * @param owner           The owner of the token
           * @param to              The address to transfer the tokens to
           * @param transferAmount  The amount of tokens to transfer
           * @param signedPermit    The permit signature, signed by the owner
           *
           * @return isError        True if the transfer failed, false otherwise
           */
          function permitTransferFromERC1155(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes calldata signedPermit
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155);
              _checkPermitApproval(TOKEN_TYPE_ERC1155, token, id, permitAmount, nonce, expiration, owner, transferAmount, signedPermit);
              isError = _transferFromERC1155(token, owner, to, id, transferAmount);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Transfers a token from the owner to the recipient using a permit signature
           * @notice This function includes additional data to verify on the signature, allowing
           * @notice protocols to extend the validation in one function call. NOTE: before calling this 
           * @notice function you MUST register the stub end of the additional data typestring using
           * @notice the `registerAdditionalDataHash` function.
           *
           * @dev    - Throws for any reason permitTransferFrom would.
           * @dev    - Throws if the additional data does not match the signature
           * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
           * @dev    - Throws if the provided hash does not match the provided additional data
           * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Performs any additional checks in the before and after hooks
           * @dev    3. The nonce of the permit is marked as used
           *
           * @param  token                    The address of the token
           * @param  id                       The ID of the token
           * @param  nonce                    The nonce of the permit
           * @param  permitAmount             The amount of tokens permitted by the owner
           * @param  expiration               The expiration timestamp of the permit
           * @param  owner                    The owner of the token
           * @param  to                       The address to transfer the tokens to
           * @param  transferAmount           The amount of tokens to transfer
           * @param  additionalData           The additional data to verify on the signature
           * @param  advancedPermitHash       The hash of the additional data
           * @param  signedPermit             The permit signature, signed by the owner
           *
           * @return isError                  True if the transfer failed, false otherwise
           */
          function permitTransferFromWithAdditionalDataERC1155(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
          ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155);
              _checkPermitApprovalWithAdditionalDataERC1155(
                  token,
                  id,
                  permitAmount,
                  nonce,
                  expiration,
                  owner,
                  transferAmount,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
              
              // copy id to top of stack to avoid stack too deep
              uint256 tmpId = id;
              isError = _transferFromERC1155(token, owner, to, tmpId, transferAmount);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Transfer an ERC20 token from the owner to the recipient using a permit signature.
           *
           * @dev    Be advised that the token ID for ERC20 is always inferred to be 0, so signed token ID
           * @dev    MUST always be set to 0.
           *
           * @dev    - Throws if the permit is expired
           * @dev    - Throws if the nonce has already been used
           * @dev    - Throws if the permit is not signed by the owner
           * @dev    - Throws if the requested amount exceeds the permitted amount
           * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token in the requested amount from the owner to the recipient
           * @dev    2. The nonce of the permit is marked as used
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param token         The address of the token
           * @param nonce         The nonce of the permit
           * @param permitAmount  The amount of tokens permitted by the owner
           * @param expiration    The expiration timestamp of the permit
           * @param owner         The owner of the token
           * @param to            The address to transfer the tokens to
           * @param signedPermit  The permit signature, signed by the owner
           *
           * @return isError      True if the transfer failed, false otherwise
           */
          function permitTransferFromERC20(
              address token,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes calldata signedPermit
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20);
              _checkPermitApproval(TOKEN_TYPE_ERC20, token, ZERO, permitAmount, nonce, expiration, owner, transferAmount, signedPermit);
              isError = _transferFromERC20(token, owner, to, ZERO, transferAmount);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Transfers an ERC20 token from the owner to the recipient using a permit signature
           * @notice This function includes additional data to verify on the signature, allowing
           * @notice protocols to extend the validation in one function call. NOTE: before calling this 
           * @notice function you MUST register the stub end of the additional data typestring using
           * @notice the `registerAdditionalDataHash` function.
           *
           * @dev    Be advised that the token ID for ERC20 is always inferred to be 0, so signed token ID
           * @dev    MUST always be set to 0.
           *
           * @dev    - Throws for any reason permitTransferFromERC20 would.
           * @dev    - Throws if the additional data does not match the signature
           * @dev    - Throws if the provided hash has not been registered as a valid additional data hash
           * @dev    - Throws if the provided hash does not match the provided additional data
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Performs any additional checks in the before and after hooks
           * @dev    3. The nonce of the permit is marked as used
           *
           * @param  token                    The address of the token
           * @param  nonce                    The nonce of the permit
           * @param  permitAmount             The amount of tokens permitted by the owner
           * @param  expiration               The expiration timestamp of the permit
           * @param  owner                    The owner of the token
           * @param  to                       The address to transfer the tokens to
           * @param  transferAmount           The amount of tokens to transfer
           * @param  additionalData           The additional data to verify on the signature
           * @param  advancedPermitHash       The hash of the additional data
           * @param  signedPermit             The permit signature, signed by the owner
           *
           * @return isError                  True if the transfer failed, false otherwise
           */
          function permitTransferFromWithAdditionalDataERC20(
              address token,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
          ) external onlyRegisteredTransferAdvancedTypeHash(advancedPermitHash) returns (bool isError) {
              _requireNotPaused(PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20);
              _checkPermitApprovalWithAdditionalDataERC20(
                  token,
                  ZERO,
                  permitAmount,
                  nonce,
                  expiration,
                  owner,
                  transferAmount,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
              isError = _transferFromERC20(token, owner, to, ZERO, transferAmount);
              if (isError) {
                  _restoreNonce(owner, nonce);
              }
          }
          /**
           * @notice Returns true if the provided hash has been registered as a valid additional data hash for transfers.
           *
           * @param  hash The hash to check
           *
           * @return isRegistered true if the hash is valid, false otherwise
           */
          function isRegisteredTransferAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered) {
              isRegistered = _registeredTransferHashes[hash];
          }
          /**
           * @notice Returns true if the provided hash has been registered as a valid additional data hash for orders.
           *
           * @param  hash The hash to check
           *
           * @return isRegistered true if the hash is valid, false otherwise
           */
          function isRegisteredOrderAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered) {
              isRegistered = _registeredOrderHashes[hash];
          }
          /**
           * =================================================
           * =============== Order Transfers =================
           * =================================================
           */
          /**
           * @notice Transfers an ERC1155 token from the owner to the recipient using a permit signature
           * @notice Order transfers are used to transfer a specific amount of a token from a specific order
           * @notice and allow for multiple uses of the same permit up to the allocated amount. NOTE: before calling this 
           * @notice function you MUST register the stub end of the additional data typestring using
           * @notice the `registerAdditionalDataHash` function.
           *
           * @dev    - Throws if the permit is expired
           * @dev    - Throws if the permit is not signed by the owner
           * @dev    - Throws if the requested amount + amount already filled exceeds the permitted amount
           * @dev    - Throws if the requested amount is less than the minimum fill amount
           * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
           * @dev    - Throws if the provided advanced permit hash has not been registered
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Updates the amount filled for the order ID
           * @dev    3. If completely filled, marks the order as filled
           * 
           * @param  signedPermit         The permit signature, signed by the owner
           * @param  orderFillAmounts     The amount of tokens to transfer
           * @param  token                The address of the token
           * @param  id                   The ID of the token
           * @param  owner                The owner of the token
           * @param  to                   The address to transfer the tokens to
           * @param  salt                 The salt of the permit
           * @param  expiration           The expiration timestamp of the permit
           * @param  orderId              The order ID
           * @param  advancedPermitHash   The hash of the additional data
           *
           * @return quantityFilled       The amount of tokens filled
           * @return isError              True if the transfer failed, false otherwise
           */
          function fillPermittedOrderERC1155(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              uint256 id,
              address owner,
              address to,
              uint256 salt,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) external onlyRegisteredOrderAdvancedTypeHash(advancedPermitHash) returns (uint256 quantityFilled, bool isError) {
              _requireNotPaused(PAUSABLE_ORDER_TRANSFER_FROM_ERC1155);
              PackedApproval storage orderStatus = _checkOrderTransferERC1155(
                  signedPermit,
                  orderFillAmounts,
                  token,
                  id,
                  owner,
                  salt,
                  expiration,
                  orderId,
                  advancedPermitHash
              );
              (
                  quantityFilled,
                  isError
              ) = _orderTransfer(
                      orderStatus,
                      orderFillAmounts,
                      token, 
                      id, 
                      owner, 
                      to, 
                      orderId,
                      _transferFromERC1155
              );
              if (isError) {
                  _restoreFillableItems(orderStatus, owner, orderId, quantityFilled, true);
              }
          }
          /**
           * @notice Transfers an ERC20 token from the owner to the recipient using a permit signature
           * @notice Order transfers are used to transfer a specific amount of a token from a specific order
           * @notice and allow for multiple uses of the same permit up to the allocated amount. NOTE: before calling this
           * @notice function you MUST register the stub end of the additional data typestring using
           * @notice the `registerAdditionalDataHash` function.
           *
           * @dev    - Throws if the permit is expired
           * @dev    - Throws if the permit is not signed by the owner
           * @dev    - Throws if the requested amount + amount already filled exceeds the permitted amount
           * @dev    - Throws if the requested amount is less than the minimum fill amount
           * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
           * @dev    - Throws if the provided advanced permit hash has not been registered
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Updates the amount filled for the order ID
           * @dev    3. If completely filled, marks the order as filled
           *
           * @param  signedPermit         The permit signature, signed by the owner
           * @param  orderFillAmounts     The amount of tokens to transfer
           * @param  token                The address of the token
           * @param  owner                The owner of the token
           * @param  to                   The address to transfer the tokens to
           * @param  salt                 The salt of the permit
           * @param  expiration           The expiration timestamp of the permit
           * @param  orderId              The order ID
           * @param  advancedPermitHash   The hash of the additional data
           *
           * @return quantityFilled       The amount of tokens filled
           * @return isError              True if the transfer failed, false otherwise
           */
          function fillPermittedOrderERC20(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              address owner,
              address to,
              uint256 salt,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) external onlyRegisteredOrderAdvancedTypeHash(advancedPermitHash) returns (uint256 quantityFilled, bool isError) {
              _requireNotPaused(PAUSABLE_ORDER_TRANSFER_FROM_ERC20);
              PackedApproval storage orderStatus = _checkOrderTransferERC20(
                  signedPermit,
                  orderFillAmounts,
                  token,
                  ZERO,
                  owner,
                  salt,
                  expiration,
                  orderId,
                  advancedPermitHash
              );
              (
                  quantityFilled,
                  isError
              ) = _orderTransfer(
                      orderStatus,
                      orderFillAmounts,
                      token, 
                      ZERO, 
                      owner, 
                      to, 
                      orderId,
                      _transferFromERC20
              );
              if (isError) {
                  _restoreFillableItems(orderStatus, owner, orderId, quantityFilled, true);
              }
          }
          /**
           * @notice Closes an outstanding order to prevent further execution of transfers.
           *
           * @dev    - Throws if the order is not in the open state
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Marks the order as cancelled
           * @dev    2. Sets the order amount to 0
           * @dev    3. Sets the order expiration to 0
           * @dev    4. Emits a OrderClosed event
           *
           * @param  owner      The owner of the token
           * @param  operator   The operator allowed to transfer the token
           * @param  tokenType  The type of token the order is for - must be 20, 721 or 1155.
           * @param  token      The address of the token contract
           * @param  id         The token ID
           * @param  orderId    The order ID
           */
          function closePermittedOrder(
              address owner,
              address operator,
              uint256 tokenType,
              address token,
              uint256 id,
              bytes32 orderId
          ) external {
              if(!(msg.sender == owner || msg.sender == operator)) {
                  revert PermitC__CallerMustBeOwnerOrOperator();
              }
              _requireValidTokenType(tokenType);
              PackedApproval storage orderStatus = _getPackedApprovalPtr(_orderApprovals, owner, tokenType, token, id, orderId, operator);
          
              if (orderStatus.state == ORDER_STATE_OPEN) {
                  orderStatus.state = ORDER_STATE_CANCELLED;
                  orderStatus.amount = 0;
                  orderStatus.expiration = 0;
                  emit OrderClosed(orderId, owner, operator, true);
              } else {
                  revert PermitC__OrderIsEitherCancelledOrFilled();
              }
          }
          /**
           * @notice Returns the amount of allowance an operator has for a specific token and id
           * @notice If the expiration on the allowance has expired, returns 0
           *
           * @dev    Overload of the on chain allowance function for approvals with a specified order ID
           * 
           * @param  owner    The owner of the token
           * @param  operator The operator of the token
           * @param  token    The address of the token contract
           * @param  id       The token ID
           *
           * @return allowedAmount The amount of allowance the operator has
           */
          function allowance(
              address owner, 
              address operator, 
              uint256 tokenType,
              address token, 
              uint256 id, 
              bytes32 orderId
          ) external view returns (uint256 allowedAmount, uint256 expiration) {
              return _allowance(_orderApprovals, owner, operator, tokenType, token, id, orderId);
          }
          /**
           * =================================================
           * ================ Nonce Management ===============
           * =================================================
           */
          /**
           * @notice Invalidates the provided nonce
           *
           * @dev    - Throws if the provided nonce has already been used
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Sets the provided nonce as used for the sender
           *
           * @param  nonce Nonce to invalidate
           */
          function invalidateUnorderedNonce(uint256 nonce) external {
              _checkAndInvalidateNonce(msg.sender, nonce);
          }
          /**
           * @notice Returns if the provided nonce has been used
           *
           * @param  owner The owner of the token
           * @param  nonce The nonce to check
           *
           * @return isValid true if the nonce is valid, false otherwise
           */
          function isValidUnorderedNonce(address owner, uint256 nonce) external view returns (bool isValid) {
              isValid = ((_unorderedNonces[owner][uint248(nonce >> 8)] >> uint8(nonce)) & ONE) == ZERO;
          }
          /**
           * @notice Revokes all outstanding approvals for the sender
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Increments the master nonce for the sender
           * @dev    2. All outstanding approvals for the sender are invalidated
           */
          function lockdown() external {
              unchecked {
                  _masterNonces[msg.sender]++;
              }
              emit Lockdown(msg.sender);
          }
          /**
           * @notice Returns the master nonce for the provided owner address
           *
           * @param  owner The owner address
           *
           * @return The master nonce
           */
          function masterNonce(address owner) external view returns (uint256) {
              return _masterNonces[owner];
          }
          /**
           * =================================================
           * ============== Transfer Functions ===============
           * =================================================
           */
          /**
           * @notice Transfer an ERC721 token from the owner to the recipient using on chain approvals
           *
           * @dev    Public transfer function overload for approval transfers
           * @dev    - Throws if the provided token address does not implement ERC721 transferFrom function
           * @dev    - Throws if the requested amount exceeds the approved amount
           * @dev    - Throws if the approval is expired
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Decrements the approval amount by the requested amount
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param  owner    The owner of the token
           * @param  to       The recipient of the token
           * @param  token    The address of the token
           * @param  id       The id of the token
           *
           * @return isError  True if the transfer failed, false otherwise
           */
          function transferFromERC721(
              address owner,
              address to,
              address token,
              uint256 id
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721);
              PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC721, token, id, ONE, true);
              isError = _transferFromERC721(owner, to, token, id);
              if (isError) {
                  _restoreFillableItems(approval, owner, ZERO_BYTES32, ONE, false);
              }
          }
          /**
           * @notice Transfer an ERC1155 token from the owner to the recipient using on chain approvals
           *
           * @dev    Public transfer function overload for approval transfers
           * @dev    - Throws if the provided token address does not implement ERC1155 safeTransferFrom function
           * @dev    - Throws if the requested amount exceeds the approved amount
           * @dev    - Throws if the approval is expired
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Decrements the approval amount by the requested amount
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param  owner     The owner of the token
           * @param  to       The recipient of the token
           * @param  amount   The amount of the token to transfer
           * @param  token    The address of the token
           * @param  id       The id of the token
           *
           * @return isError  True if the transfer failed, false otherwise
           */
          function transferFromERC1155(
              address owner,
              address to,
              address token,
              uint256 id,
              uint256 amount
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155);
              PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC1155, token, id, amount, false);
              isError = _transferFromERC1155(token, owner, to, id, amount);
              if (isError) {
                  _restoreFillableItems(approval, owner, ZERO_BYTES32, amount, false);
              }
          }
          /**
           * @notice Transfer an ERC20 token from the owner to the recipient using on chain approvals
           *
           * @dev    Public transfer function overload for approval transfers
           * @dev    - Throws if the provided token address does not implement ERC20 transferFrom function
           * @dev    - Throws if the requested amount exceeds the approved amount
           * @dev    - Throws if the approval is expired
           * @dev    - Returns `false` if the transfer fails
           *
           * @dev    <h4>Postconditions:</h4>
           * @dev    1. Transfers the token (in the requested amount) from the owner to the recipient
           * @dev    2. Decrements the approval amount by the requested amount
           * @dev    3. Performs any additional checks in the before and after hooks
           *
           * @param  owner     The owner of the token
           * @param  to       The recipient of the token
           * @param  amount   The amount of the token to transfer
           * @param  token    The address of the token
           *
           * @return isError  True if the transfer failed, false otherwise
           */
          function transferFromERC20(
              address owner,
              address to,
              address token,
              uint256 amount
          ) external returns (bool isError) {
              _requireNotPaused(PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20);
              PackedApproval storage approval = _checkAndUpdateApproval(owner, TOKEN_TYPE_ERC20, token, ZERO, amount, false);
              isError = _transferFromERC20(token, owner, to, ZERO, amount);
              if (isError) {
                  _restoreFillableItems(approval, owner, ZERO_BYTES32, amount, false);
              }
          }
          /**
           * @notice  Performs a transfer of an ERC721 token.
           * 
           * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
           * @dev     Will **NOT** revert if the transfer is unsucessful.
           * @dev     Invokers **MUST** check `isError` return value to determine success.
           * 
           * @param owner  The owner of the token being transferred
           * @param to     The address to transfer the token to
           * @param token  The token address of the token being transferred
           * @param id     The token id being transferred
           * 
           * @return isError True if the token was not transferred, false if token was transferred
           */
          function _transferFromERC721(
              address owner,
              address to,
              address token,
              uint256 id
          ) private returns (bool isError) {
              isError = _beforeTransferFrom(TOKEN_TYPE_ERC721, token, owner, to, id, ONE);
              if (!isError) {
                  try IERC721(token).transferFrom(owner, to, id) { } 
                  catch {
                      isError = true;
                  }
              }
          }
          /**
           * @notice  Performs a transfer of an ERC1155 token.
           * 
           * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
           * @dev     Will **NOT** revert if the transfer is unsucessful.
           * @dev     Invokers **MUST** check `isError` return value to determine success.
           * 
           * @param token  The token address of the token being transferred
           * @param owner  The owner of the token being transferred
           * @param to     The address to transfer the token to
           * @param id     The token id being transferred
           * @param amount The quantity of token id to transfer
           * 
           * @return isError True if the token was not transferred, false if token was transferred
           */
          function _transferFromERC1155(
              address token,
              address owner,
              address to,
              uint256 id,
              uint256 amount
          ) private returns (bool isError) {
              isError = _beforeTransferFrom(TOKEN_TYPE_ERC1155, token, owner, to, id, amount);
              if (!isError) {
                  try IERC1155(token).safeTransferFrom(owner, to, id, amount, "") { } catch {
                      isError = true;
                  }
              }
          }
          /**
           * @notice  Performs a transfer of an ERC20 token.
           * 
           * @dev     Will **NOT** attempt transfer if `_beforeTransferFrom` hook returns false.
           * @dev     Will **NOT** revert if the transfer is unsucessful.
           * @dev     Invokers **MUST** check `isError` return value to determine success.
           * 
           * @param token  The token address of the token being transferred
           * @param owner  The owner of the token being transferred
           * @param to     The address to transfer the token to
           * @param amount The quantity of token id to transfer
           * 
           * @return isError True if the token was not transferred, false if token was transferred
           */
          function _transferFromERC20(
              address token,
              address owner,
              address to,
              uint256 /*id*/,
              uint256 amount
            ) private returns (bool isError) {
              isError = _beforeTransferFrom(TOKEN_TYPE_ERC20, token, owner, to, ZERO, amount);
              if (!isError) {
                  (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, owner, to, amount));
                  if (!success) {
                      isError = true;
                  } else if (data.length > 0) {
                      isError = !abi.decode(data, (bool));
                  }
              }
          }
          /**
           * =================================================
           * ============ Signature Verification =============
           * =================================================
           */
          /**
           * @notice Returns the domain separator used in the permit signature
           *
           * @return domainSeparator The domain separator
           */
          function domainSeparatorV4() external view returns (bytes32 domainSeparator) {
              domainSeparator = _domainSeparatorV4();
          }
          /**
           * @notice  Verifies a permit signature based on the bytes length of the signature provided.
           * 
           * @dev     Throws when -
           * @dev         The bytes signature length is 64 or 65 bytes AND
           * @dev         The ECDSA recovered signer is not the owner AND
           * @dev         The owner's code length is zero OR the owner does not return a valid EIP-1271 response
           * @dev 
           * @dev         OR
           * @dev
           * @dev         The bytes signature length is not 64 or 65 bytes AND
           * @dev         The owner's code length is zero OR the owner does not return a valid EIP-1271 response
           */
          function _verifyPermitSignature(bytes32 digest, bytes calldata signature, address owner) private view {
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // Divide the signature in r, s and v variables
                  /// @solidity memory-safe-assembly
                  assembly {
                      r := calldataload(signature.offset)
                      s := calldataload(add(signature.offset, 32))
                      v := byte(0, calldataload(add(signature.offset, 64)))
                  }
                  (bool isError, address signer) = _ecdsaRecover(digest, v, r, s);
                  if (owner != signer || isError) {
                      _verifyEIP1271Signature(owner, digest, signature);
                  }
              } else if (signature.length == 64) {
                  bytes32 r;
                  bytes32 vs;
                  // Divide the signature in r and vs variables
                  /// @solidity memory-safe-assembly
                  assembly {
                      r := calldataload(signature.offset)
                      vs := calldataload(add(signature.offset, 32))
                  }
                  (bool isError, address signer) = _ecdsaRecover(digest, r, vs);
                  if (owner != signer || isError) {
                      _verifyEIP1271Signature(owner, digest, signature);
                  }
              } else {
                  _verifyEIP1271Signature(owner, digest, signature);
              }
          }
          /**
           * @notice Verifies an EIP-1271 signature.
           * 
           * @dev    Throws when `signer` code length is zero OR the EIP-1271 call does not
           * @dev    return the correct magic value.
           * 
           * @param signer     The signer address to verify a signature with
           * @param hash       The hash digest to verify with the signer
           * @param signature  The signature to verify
           */
          function _verifyEIP1271Signature(address signer, bytes32 hash, bytes calldata signature) private view {
              if(signer.code.length == 0) {
                  revert PermitC__SignatureTransferInvalidSignature();
              }
              if (!_safeIsValidSignature(signer, hash, signature)) {
                  revert PermitC__SignatureTransferInvalidSignature();
              }
          }
          /**
           * @notice  Overload of the `_ecdsaRecover` function to unpack the `v` and `s` values
           * 
           * @param digest    The hash digest that was signed
           * @param r         The `r` value of the signature
           * @param vs        The packed `v` and `s` values of the signature
           * 
           * @return isError  True if the ECDSA function is provided invalid inputs
           * @return signer   The recovered address from ECDSA
           */
          function _ecdsaRecover(bytes32 digest, bytes32 r, bytes32 vs) private pure returns (bool isError, address signer) {
              unchecked {
                  bytes32 s = vs & UPPER_BIT_MASK;
                  uint8 v = uint8(uint256(vs >> 255)) + 27;
                  (isError, signer) = _ecdsaRecover(digest, v, r, s);
              }
          }
          /**
           * @notice  Recovers the signer address using ECDSA
           * 
           * @dev     Does **NOT** revert if invalid input values are provided or `signer` is recovered as address(0)
           * @dev     Returns an `isError` value in those conditions that is handled upstream
           * 
           * @param digest    The hash digest that was signed
           * @param v         The `v` value of the signature
           * @param r         The `r` value of the signature
           * @param s         The `s` value of the signature
           * 
           * @return isError  True if the ECDSA function is provided invalid inputs
           * @return signer   The recovered address from ECDSA
           */
          function _ecdsaRecover(bytes32 digest, uint8 v, bytes32 r, bytes32 s) private pure returns (bool isError, address signer) {
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  // Invalid signature `s` value - return isError = true and signer = address(0) to check EIP-1271
                  return (true, address(0));
              }
              signer = ecrecover(digest, v, r, s);
              isError = (signer == address(0));
          }
          /**
           * @notice A gas efficient, and fallback-safe way to call the isValidSignature function for EIP-1271.
           *
           * @param signer     The EIP-1271 signer to call to check for a valid signature.
           * @param hash       The hash digest to verify with the EIP-1271 signer.
           * @param signature  The supplied signature to verify.
           * 
           * @return isValid   True if the EIP-1271 signer returns the EIP-1271 magic value.
           */
          function _safeIsValidSignature(
              address signer,
              bytes32 hash,
              bytes calldata signature
          ) private view returns(bool isValid) {
              assembly {
                  function _callIsValidSignature(_signer, _hash, _signatureOffset, _signatureLength) -> _isValid {
                      let ptr := mload(0x40)
                      // store isValidSignature(bytes32,bytes) selector
                      mstore(ptr, hex"1626ba7e")
                      // store bytes32 hash value in abi encoded location
                      mstore(add(ptr, 0x04), _hash)
                      // store abi encoded location of the bytes signature data
                      mstore(add(ptr, 0x24), 0x40)
                      // store bytes signature length
                      mstore(add(ptr, 0x44), _signatureLength)
                      // copy calldata bytes signature to memory
                      calldatacopy(add(ptr, 0x64), _signatureOffset, _signatureLength)
                      // calculate data length based on abi encoded data with rounded up signature length
                      let dataLength := add(0x64, and(add(_signatureLength, 0x1F), not(0x1F)))
                      // update free memory pointer
                      mstore(0x40, add(ptr, dataLength))
                      // static call _signer with abi encoded data
                      // skip return data check if call failed or return data size is not at least 32 bytes
                      if and(iszero(lt(returndatasize(), 0x20)), staticcall(gas(), _signer, ptr, dataLength, 0x00, 0x20)) {
                          // check if return data is equal to isValidSignature magic value
                          _isValid := eq(mload(0x00), hex"1626ba7e")
                          leave
                      }
                  }
                  isValid := _callIsValidSignature(signer, hash, signature.offset, signature.length)
              }
          }
          /**
           * =================================================
           * ===================== Hooks =====================
           * =================================================
           */
          /**
           * @dev    This function is empty by default. Override it to add additional logic after the approval transfer.
           * @dev    The function returns a boolean value instead of reverting to indicate if there is an error for more granular control in inheriting protocols.
           */
          function _beforeTransferFrom(uint256 tokenType, address token, address owner, address to, uint256 id, uint256 amount) internal virtual returns (bool isError) {}
          /**
           * =================================================
           * ==================== Internal ===================
           * =================================================
           */
          /**
           * @notice Checks if an advanced permit typehash has been registered with PermitC
           * 
           * @dev    Throws when the typehash has not been registered
           * 
           * @param advancedPermitHash  The permit typehash to check
           */
          function _requireTransferAdvancedPermitHashIsRegistered(bytes32 advancedPermitHash) private view {
              if (!_registeredTransferHashes[advancedPermitHash]) {
                  revert PermitC__SignatureTransferPermitHashNotRegistered();
              }
          }
          /**
           * @notice Checks if an advanced permit typehash has been registered with PermitC
           * 
           * @dev    Throws when the typehash has not been registered
           * 
           * @param advancedPermitHash  The permit typehash to check
           */
          function _requireOrderAdvancedPermitHashIsRegistered(bytes32 advancedPermitHash) private view {
              if (!_registeredOrderHashes[advancedPermitHash]) {
                  revert PermitC__SignatureTransferPermitHashNotRegistered();
              }
          }
          /**
           * @notice  Invalidates an account nonce if it has not been previously used
           * 
           * @dev     Throws when the nonce was previously used
           * 
           * @param account  The account to invalidate the nonce of
           * @param nonce    The nonce to invalidate
           */
          function _checkAndInvalidateNonce(address account, uint256 nonce) private {
              unchecked {
                  if (uint256(_unorderedNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) & 
                      (ONE << uint8(nonce)) == ZERO) {
                      revert PermitC__NonceAlreadyUsedOrRevoked();
                  }
              }
          }
          /**
           * @notice Checks an approval to ensure it is sufficient for the `amount` to send
           * 
           * @dev    Throws when the approval is expired
           * @dev    Throws when the approved amount is insufficient
           * 
           * @param owner            The owner of the token
           * @param tokenType        The type of token
           * @param token            The address of the token
           * @param id               The id of the token
           * @param amount           The amount to deduct from the approval
           * @param zeroOutApproval  True if the approval should be set to zero
           * 
           * @return approval  Storage pointer for the approval data
           */
          function _checkAndUpdateApproval(
              address owner,
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 amount,
              bool zeroOutApproval
          ) private returns (PackedApproval storage approval) {
              approval = _getPackedApprovalPtr(_transferApprovals, owner, tokenType, token, id, ZERO_BYTES32, msg.sender);
              
              if (approval.expiration < block.timestamp) {
                  revert PermitC__ApprovalTransferPermitExpiredOrUnset();
              }
              if (approval.amount < amount) {
                  revert PermitC__ApprovalTransferExceededPermittedAmount();
              }
              if(zeroOutApproval) {
                  approval.amount = 0;
              } else if (approval.amount < type(uint200).max) {
                  unchecked {
                      approval.amount -= uint200(amount);
                  }
              }
          }
          /**
           * @notice  Gets the storage pointer for an approval
           * 
           * @param _approvals  The mapping to retrieve the approval from
           * @param account     The account the approval is from
           * @param tokenType   The type of token the approval is for
           * @param token       The address of the token
           * @param id          The id of the token
           * @param orderId     The order id for the approval
           * @param operator    The operator for the approval
           * 
           * @return approval  Storage pointer for the approval data
           */
          function _getPackedApprovalPtr(
              mapping(bytes32 => mapping(address => PackedApproval)) storage _approvals,
              address account, 
              uint256 tokenType,
              address token, 
              uint256 id,
              bytes32 orderId,
              address operator
          ) private view returns (PackedApproval storage approval) {
              approval = _approvals[_getPackedApprovalKey(account, tokenType, token, id, orderId)][operator];
          }
          /**
           * @notice  Gets the storage key for the mapping for a specific approval
           * 
           * @param owner      The owner of the token
           * @param tokenType  The type of token
           * @param token      The address of the token
           * @param id         The id of the token
           * @param orderId    The order id of the approval
           * 
           * @return key  The key value to use to access the approval in the mapping
           */
          function _getPackedApprovalKey(address owner, uint256 tokenType, address token, uint256 id, bytes32 orderId) private view returns (bytes32 key) {
              key = keccak256(abi.encode(owner, tokenType, token, id, orderId, _masterNonces[owner]));
          }
          /**
           * @notice Checks the permit approval for a single use permit without additional data
           * 
           * @dev    Throws when the `nonce` has already been consumed
           * @dev    Throws when the permit amount is less than the transfer amount
           * @dev    Throws when the permit is expired
           * @dev    Throws when the signature is invalid
           * 
           * @param tokenType       The type of token
           * @param token           The address of the token
           * @param id              The id of the token
           * @param permitAmount    The amount authorized by the owner signature
           * @param nonce           The nonce of the permit
           * @param expiration      The time the permit expires
           * @param owner           The owner of the token
           * @param transferAmount  The amount of tokens requested to transfer
           * @param signedPermit    The signature for the permit
           */
          function _checkPermitApproval(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 permitAmount,
              uint256 nonce,
              uint256 expiration,
              address owner,
              uint256 transferAmount,
              bytes calldata signedPermit
          ) private {
              bytes32 digest = _hashTypedDataV4(
                  PermitHash.hashSingleUsePermit(
                      tokenType,
                      token,
                      id,
                      permitAmount,
                      nonce,
                      expiration,
                      _masterNonces[owner]
                  )
              );
              _checkPermitData(
                  nonce,
                  expiration,
                  transferAmount,
                  permitAmount,
                  owner,
                  digest,
                  signedPermit
              );
          }
          /**
           * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC1155
           * 
           * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC1155`
           * @dev     Throws when the `nonce` has already been consumed
           * @dev     Throws when the permit amount is less than the transfer amount
           * @dev     Throws when the permit is expired
           * @dev     Throws when the signature is invalid
           * 
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param permitAmount        The amount authorized by the owner signature
           * @param nonce               The nonce of the permit
           * @param expiration          The time the permit expires
           * @param owner               The owner of the token
           * @param transferAmount      The amount of tokens requested to transfer
           * @param signedPermit        The signature for the permit
           * @param additionalData      The additional data to validate with the permit signature
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           */
          function _checkPermitApprovalWithAdditionalDataERC1155(
              address token,
              uint256 id,
              uint256 permitAmount,
              uint256 nonce,
              uint256 expiration,
              address owner,
              uint256 transferAmount,
              bytes calldata signedPermit,
              bytes32 additionalData,
              bytes32 advancedPermitHash
          ) private {
              _checkPermitApprovalWithAdditionalData(
                  TOKEN_TYPE_ERC1155,
                  token,
                  id,
                  permitAmount,
                  nonce,
                  expiration,
                  owner,
                  transferAmount,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
          }
          /**
           * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC20
           * 
           * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC220`
           * @dev     Throws when the `nonce` has already been consumed
           * @dev     Throws when the permit amount is less than the transfer amount
           * @dev     Throws when the permit is expired
           * @dev     Throws when the signature is invalid
           * 
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param permitAmount        The amount authorized by the owner signature
           * @param nonce               The nonce of the permit
           * @param expiration          The time the permit expires
           * @param owner               The owner of the token
           * @param transferAmount      The amount of tokens requested to transfer
           * @param signedPermit        The signature for the permit
           * @param additionalData      The additional data to validate with the permit signature
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           */
          function _checkPermitApprovalWithAdditionalDataERC20(
              address token,
              uint256 id,
              uint256 permitAmount,
              uint256 nonce,
              uint256 expiration,
              address owner,
              uint256 transferAmount,
              bytes calldata signedPermit,
              bytes32 additionalData,
              bytes32 advancedPermitHash
          ) private {
              _checkPermitApprovalWithAdditionalData(
                  TOKEN_TYPE_ERC20,
                  token,
                  id,
                  permitAmount,
                  nonce,
                  expiration,
                  owner,
                  transferAmount,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
          }
          /**
           * @notice  Overload of `_checkPermitApprovalWithAdditionalData` to supply TOKEN_TYPE_ERC721
           * 
           * @dev     Prevents stack too deep in `permitTransferFromWithAdditionalDataERC721`
           * @dev     Throws when the `nonce` has already been consumed
           * @dev     Throws when the permit amount is less than the transfer amount
           * @dev     Throws when the permit is expired
           * @dev     Throws when the signature is invalid
           * 
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param permitAmount        The amount authorized by the owner signature
           * @param nonce               The nonce of the permit
           * @param expiration          The time the permit expires
           * @param owner               The owner of the token
           * @param transferAmount      The amount of tokens requested to transfer
           * @param signedPermit        The signature for the permit
           * @param additionalData      The additional data to validate with the permit signature
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           */
          function _checkPermitApprovalWithAdditionalDataERC721(
              address token,
              uint256 id,
              uint256 permitAmount,
              uint256 nonce,
              uint256 expiration,
              address owner,
              uint256 transferAmount,
              bytes calldata signedPermit,
              bytes32 additionalData,
              bytes32 advancedPermitHash
          ) private {
              _checkPermitApprovalWithAdditionalData(
                  TOKEN_TYPE_ERC721,
                  token,
                  id,
                  permitAmount,
                  nonce,
                  expiration,
                  owner,
                  transferAmount,
                  signedPermit,
                  additionalData,
                  advancedPermitHash
              );
          }
          /**
           * @notice Checks the permit approval for a single use permit with additional data
           * 
           * @dev    Throws when the `nonce` has already been consumed
           * @dev    Throws when the permit amount is less than the transfer amount
           * @dev    Throws when the permit is expired
           * @dev    Throws when the signature is invalid
           * 
           * @param tokenType           The type of token
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param permitAmount        The amount authorized by the owner signature
           * @param nonce               The nonce of the permit
           * @param expiration          The time the permit expires
           * @param owner               The owner of the token
           * @param transferAmount      The amount of tokens requested to transfer
           * @param signedPermit        The signature for the permit
           * @param additionalData      The additional data to validate with the permit signature
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           */
          function _checkPermitApprovalWithAdditionalData(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 permitAmount,
              uint256 nonce,
              uint256 expiration,
              address owner,
              uint256 transferAmount,
              bytes calldata signedPermit,
              bytes32 additionalData,
              bytes32 advancedPermitHash
          ) private {
              bytes32 digest = _getAdvancedTypedDataV4PermitHash(
                  tokenType,
                  token, 
                  id, 
                  permitAmount, 
                  owner,
                  nonce, 
                  expiration, 
                  additionalData, 
                  advancedPermitHash
              );        
              _checkPermitData(
                  nonce,
                  expiration,
                  transferAmount,
                  permitAmount,
                  owner,
                  digest,
                  signedPermit
              );
          }
          /**
           * @notice  Checks that a single use permit has not expired, was authorized for the amount
           * @notice  being transferred, has a valid nonce and has a valid signature.
           * 
           * @dev    Throws when the `nonce` has already been consumed
           * @dev    Throws when the permit amount is less than the transfer amount
           * @dev    Throws when the permit is expired
           * @dev    Throws when the signature is invalid
           * 
           * @param nonce           The nonce of the permit
           * @param expiration      The time the permit expires
           * @param transferAmount  The amount of tokens requested to transfer
           * @param permitAmount    The amount authorized by the owner signature
           * @param owner           The owner of the token
           * @param digest          The digest that was signed by the owner
           * @param signedPermit    The signature for the permit
           */
          function _checkPermitData(
              uint256 nonce,
              uint256 expiration, 
              uint256 transferAmount, 
              uint256 permitAmount, 
              address owner, 
              bytes32 digest,
              bytes calldata signedPermit
          ) private {
              if (block.timestamp > expiration) {
                  revert PermitC__SignatureTransferExceededPermitExpired();
              }
              if (transferAmount > permitAmount) {
                  revert PermitC__SignatureTransferExceededPermittedAmount();
              }
              _checkAndInvalidateNonce(owner, nonce);
              _verifyPermitSignature(digest, signedPermit, owner);
          }
          /**
           * @notice  Stores an approval for future use by `operator` to move tokens on behalf of `owner`
           * 
           * @param tokenType           The type of token
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param amount              The amount authorized by the owner
           * @param expiration          The time the permit expires
           * @param owner               The owner of the token
           * @param operator            The account allowed to transfer the tokens
           */
          function _storeApproval(
              uint256 tokenType,
              address token,
              uint256 id,
              uint200 amount,
              uint48 expiration,
              address owner,
              address operator
          ) private {
              PackedApproval storage approval = _getPackedApprovalPtr(_transferApprovals, owner, tokenType, token, id, ZERO_BYTES32, operator);
              
              approval.expiration = expiration;
              approval.amount = amount;
              emit Approval(owner, token, operator, id, amount, expiration);
          }
          /**
           * @notice  Overload of `_checkOrderTransfer` to supply TOKEN_TYPE_ERC1155
           * 
           * @dev     Prevents stack too deep in `fillPermittedOrderERC1155`
           * @dev     Throws when the order start amount is greater than type(uint200).max
           * @dev     Throws when the order status is not open
           * @dev     Throws when the signature is invalid
           * @dev     Throws when the permit is expired
           * 
           * @param signedPermit        The signature for the permit
           * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param owner               The owner of the token
           * @param salt                The salt value for the permit
           * @param expiration          The time the permit expires
           * @param orderId             The order id for the permit
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           * 
           * @return orderStatus  Storage pointer for the approval data
           */
          function _checkOrderTransferERC1155(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              uint256 id,
              address owner,
              uint256 salt,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) private returns (PackedApproval storage orderStatus) {
              orderStatus = _checkOrderTransfer(
                  signedPermit,
                  orderFillAmounts,
                  TOKEN_TYPE_ERC1155,
                  token,
                  id,
                  owner,
                  salt,
                  expiration,
                  orderId,
                  advancedPermitHash
              );
          }
          /**
           * @notice  Overload of `_checkOrderTransfer` to supply TOKEN_TYPE_ERC20
           * 
           * @dev     Prevents stack too deep in `fillPermittedOrderERC20`
           * @dev     Throws when the order start amount is greater than type(uint200).max
           * @dev     Throws when the order status is not open
           * @dev     Throws when the signature is invalid
           * @dev     Throws when the permit is expired
           * 
           * @param signedPermit        The signature for the permit
           * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param owner               The owner of the token
           * @param salt                The salt value for the permit
           * @param expiration          The time the permit expires
           * @param orderId             The order id for the permit
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           * 
           * @return orderStatus  Storage pointer for the approval data
           */
          function _checkOrderTransferERC20(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              uint256 id,
              address owner,
              uint256 salt,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) private returns (PackedApproval storage orderStatus) {
              orderStatus = _checkOrderTransfer(
                  signedPermit,
                  orderFillAmounts,
                  TOKEN_TYPE_ERC20,
                  token,
                  id,
                  owner,
                  salt,
                  expiration,
                  orderId,
                  advancedPermitHash
              );
          }
          /**
           * @notice  Validates an order transfer to check order start amount, status, signature if not previously
           * @notice  opened, and expiration.
           * 
           * @dev     Throws when the order start amount is greater than type(uint200).max
           * @dev     Throws when the order status is not open
           * @dev     Throws when the signature is invalid
           * @dev     Throws when the permit is expired
           * 
           * @param signedPermit        The signature for the permit
           * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
           * @param tokenType           The type of token
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param owner               The owner of the token
           * @param salt                The salt value for the permit
           * @param expiration          The time the permit expires
           * @param orderId             The order id for the permit
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           * 
           * @return orderStatus  Storage pointer for the approval data
           */
          function _checkOrderTransfer(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              uint256 tokenType,
              address token,
              uint256 id,
              address owner,
              uint256 salt,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) private returns (PackedApproval storage orderStatus) {
              if (orderFillAmounts.orderStartAmount > type(uint200).max) {
                  revert PermitC__AmountExceedsStorageMaximum();
              }
              orderStatus = _getPackedApprovalPtr(_orderApprovals, owner, tokenType, token, id, orderId, msg.sender);
              if (orderStatus.state == ORDER_STATE_OPEN) {
                  if (orderStatus.amount == 0) {
                      _verifyPermitSignature(
                          _getAdvancedTypedDataV4PermitHash(
                              tokenType,
                              token, 
                              id, 
                              orderFillAmounts.orderStartAmount,
                              owner,
                              salt, 
                              expiration, 
                              orderId, 
                              advancedPermitHash
                          ), 
                          signedPermit, 
                          owner
                      );
                      orderStatus.amount = uint200(orderFillAmounts.orderStartAmount);
                      orderStatus.expiration = expiration;   
                      emit OrderOpened(orderId, owner, msg.sender, orderFillAmounts.orderStartAmount);
                  }
                  if (block.timestamp > orderStatus.expiration) {
                      revert PermitC__SignatureTransferExceededPermitExpired();
                  }
              } else {
                  revert PermitC__OrderIsEitherCancelledOrFilled();
              }
          }
          /**
           * @notice  Checks the order fill amounts against approval data and transfers tokens, updates
           * @notice  approval if the fill results in the order being closed.
           * 
           * @dev     Throws when the amount to fill is less than the minimum fill amount
           * 
           * @param orderStatus         Storage pointer for the approval data
           * @param orderFillAmounts    A struct containing the order start, requested fill and minimum fill amounts
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param owner               The owner of the token
           * @param to                  The address to send the tokens to
           * @param orderId             The order id for the permit
           * @param _transferFrom       Function pointer of the transfer function to send tokens with
           * 
           * @return quantityFilled     The number of tokens filled in the order
           * @return isError            True if there was an error transferring tokens, false otherwise
           */
          function _orderTransfer(
              PackedApproval storage orderStatus,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              uint256 id,
              address owner,
              address to,
              bytes32 orderId,
              function (address, address, address, uint256, uint256) internal returns (bool) _transferFrom
          ) private returns (uint256 quantityFilled, bool isError) {
              quantityFilled = orderFillAmounts.requestedFillAmount;
              
              if (quantityFilled > orderStatus.amount) {
                  quantityFilled = orderStatus.amount;
              }
              if (quantityFilled < orderFillAmounts.minimumFillAmount) {
                  revert PermitC__UnableToFillMinimumRequestedQuantity();
              }
              unchecked {
                  orderStatus.amount -= uint200(quantityFilled);
                  emit OrderFilled(orderId, owner, msg.sender, quantityFilled);
              }
              if (orderStatus.amount == 0) {
                  orderStatus.state = ORDER_STATE_FILLED;
                  emit OrderClosed(orderId, owner, msg.sender, false);
              }
              isError = _transferFrom(token, owner, to, id, quantityFilled);
          }
          /**
           * @notice  Restores an account's nonce when a transfer was not successful
           * 
           * @dev     Throws when the nonce was not already consumed
           * 
           * @param account  The account to restore the nonce of
           * @param nonce    The nonce to restore
           */
          function _restoreNonce(address account, uint256 nonce) private {
              unchecked {
                  if (uint256(_unorderedNonces[account][uint248(nonce >> 8)] ^= (ONE << uint8(nonce))) & 
                      (ONE << uint8(nonce)) != ZERO) {
                      revert PermitC__NonceNotUsedOrRevoked();
                  }
              }
          }
          /**
           * @notice  Restores an approval amount when a transfer was not successful
           * 
           * @param approval        Storage pointer for the approval data
           * @param owner           The owner of the tokens
           * @param orderId         The order id to restore approval amount on
           * @param unfilledAmount  The amount that was not filled on the order
           * @param isOrderPermit   True if the fill restoration is for an permit order
           */
          function _restoreFillableItems(
              PackedApproval storage approval,
              address owner,
              bytes32 orderId,
              uint256 unfilledAmount,
              bool isOrderPermit
          ) private {
              if (unfilledAmount > 0) {
                  if (isOrderPermit) {
                      // Order permits always deduct amount and must be restored
                      unchecked {
                          approval.amount += uint200(unfilledAmount);
                      }
                      approval.state = ORDER_STATE_OPEN;
                      emit OrderRestored(orderId, owner, unfilledAmount);
                  } else if (approval.amount < type(uint200).max) {
                      // Stored approvals only deduct amount 
                      unchecked {
                          approval.amount += uint200(unfilledAmount);
                      }
                  }
              }
          }
          function _requireValidTokenType(uint256 tokenType) private pure {
              if(!(
                  tokenType == TOKEN_TYPE_ERC721 || 
                  tokenType == TOKEN_TYPE_ERC1155 || 
                  tokenType == TOKEN_TYPE_ERC20
                  )
              ) {
                  revert PermitC__InvalidTokenType();
              }
          }
          /**
           * @notice  Generates an EIP-712 digest for a permit
           * 
           * @param tokenType           The type of token
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param amount              The amount authorized by the owner signature
           * @param owner               The owner of the token
           * @param nonce               The nonce for the permit
           * @param expiration          The time the permit expires
           * @param additionalData      The additional data to validate with the permit signature
           * @param advancedPermitHash  The typehash of the permit to use for validating the signature
           * 
           * @return digest  The EIP-712 digest of the permit data
           */
          function _getAdvancedTypedDataV4PermitHash(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 amount,
              address owner,
              uint256 nonce,
              uint256 expiration,
              bytes32 additionalData,
              bytes32 advancedPermitHash
          ) private view returns (bytes32 digest) {
              // cache masterNonce on stack to avoid stack too deep
              uint256 masterNonce_ = _masterNonces[owner];
              digest = 
                  _hashTypedDataV4(
                      PermitHash.hashSingleUsePermitWithAdditionalData(
                          tokenType,
                          token, 
                          id, 
                          amount, 
                          nonce, 
                          expiration, 
                          additionalData, 
                          advancedPermitHash, 
                          masterNonce_
                      )
                  );
          }
          /**
           * @notice  Returns the current allowed amount and expiration for a stored permit
           * 
           * @dev     Returns zero allowed if the permit has expired
           * 
           * @param _approvals  The mapping to retrieve the approval from
           * @param owner       The account the approval is from
           * @param operator    The operator for the approval
           * @param tokenType   The type of token the approval is for
           * @param token       The address of the token
           * @param id          The id of the token
           * @param orderId     The order id for the approval
           * 
           * @return allowedAmount  The amount authorized by the approval, zero if the permit has expired
           * @return expiration     The expiration of the approval
           */
          function _allowance(
              mapping(bytes32 => mapping(address => PackedApproval)) storage _approvals,
              address owner, 
              address operator, 
              uint256 tokenType, 
              address token, 
              uint256 id, 
              bytes32 orderId
          ) private view returns (uint256 allowedAmount, uint256 expiration) {
              PackedApproval storage allowed = _getPackedApprovalPtr(_approvals, owner, tokenType, token, id, orderId, operator);
              allowedAmount = allowed.expiration < block.timestamp ? 0 : allowed.amount;
              expiration = allowed.expiration;
          }
          /**
           * @notice  Allows the owner of the PermitC contract to access pausable admin functions
           * 
           * @dev     May be overriden by an inheriting contract to provide alternative permission structure
           */
          function _requireCallerHasPausePermissions() internal view virtual override {
              _checkOwner();
          }
      }
      // 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.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
      pragma solidity ^0.8.24;
      contract Tstorish {
          // Declare a storage variable indicating if TSTORE support has been
          // activated post-deployment.
          bool private _tstoreSupport;
          /*
           * ------------------------------------------------------------------------+
           * Opcode      | Mnemonic         | Stack              | Memory            |
           * ------------------------------------------------------------------------|
           * 60 0x02     | PUSH1 0x02       | 0x02               |                   |
           * 60 0x1e     | PUSH1 0x1e       | 0x1e 0x02          |                   |
           * 61 0x3d5c   | PUSH2 0x3d5c     | 0x3d5c 0x1e 0x02   |                   |
           * 3d          | RETURNDATASIZE   | 0 0x3d5c 0x1e 0x02 |                   |
           *                                                                         |
           * :: store deployed bytecode in memory: (3d) RETURNDATASIZE (5c) TLOAD :: |
           * 52          | MSTORE           | 0x1e 0x02          | [0..0x20): 0x3d5c |
           * f3          | RETURN           |                    | [0..0x20): 0x3d5c |
           * ------------------------------------------------------------------------+
           */
          uint256 constant _TLOAD_TEST_PAYLOAD = 0x6002_601e_613d5c_3d_52_f3;
          uint256 constant _TLOAD_TEST_PAYLOAD_LENGTH = 0x0a;
          uint256 constant _TLOAD_TEST_PAYLOAD_OFFSET = 0x16;
          // Declare an immutable variable to store the initial TSTORE support status.
          bool private immutable _tstoreInitialSupport;
          // Declare an immutable variable to store the tstore test contract address.
          address private immutable _tloadTestContract;
          // Declare a few custom revert error types.
          error TStoreAlreadyActivated();
          error TStoreNotSupported();
          error TloadTestContractDeploymentFailed();
          error OnlyDirectCalls();
          /**
           * @dev Determine TSTORE availability during deployment. This involves
           *      attempting to deploy a contract that utilizes TLOAD as part of the
           *      contract construction bytecode, and configuring initial support for
           *      using TSTORE in place of SSTORE based on the result.
           */
          constructor() {
              // Deploy the contract testing TLOAD support and store the address.
              address tloadTestContract = _prepareTloadTest();
              // Ensure the deployment was successful.
              if (tloadTestContract == address(0)) {
                  revert TloadTestContractDeploymentFailed();
              }
              // Determine if TSTORE is supported.
              bool tstoreInitialSupport = _testTload(tloadTestContract);
              // Store the result as an immutable.
              _tstoreInitialSupport = tstoreInitialSupport;
              // Set the address of the deployed TLOAD test contract as an immutable.
              _tloadTestContract = tloadTestContract;
          }
          /**
           * @dev External function to activate TSTORE usage. Does not need to be
           *      called if TSTORE is supported from deployment, and only needs to be
           *      called once. Reverts if TSTORE has already been activated or if the
           *      opcode is not available. Note that this must be called directly from
           *      an externally-owned account to avoid potential reentrancy issues.
           */
          function __activateTstore() external {
              // Ensure this function is triggered from an externally-owned account.
              if (msg.sender != tx.origin) {
                  revert OnlyDirectCalls();
              }
              // Determine if TSTORE can potentially be activated.
              if (_tstoreInitialSupport || _tstoreSupport) {
                  revert TStoreAlreadyActivated();
              }
              // Determine if TSTORE can be activated and revert if not.
              if (!_testTload(_tloadTestContract)) {
                  revert TStoreNotSupported();
              }
              // Mark TSTORE as activated.
              _tstoreSupport = true;
          }
          /**
           * @dev Internal function to set a TSTORISH value.
           *
           * @param storageSlot The slot to write the TSTORISH value to.
           * @param value       The value to write to the given storage slot.
           */
          function _setTstorish(uint256 storageSlot, uint256 value) internal {
              if (_tstoreInitialSupport) {
                  assembly {
                      tstore(storageSlot, value)
                  }
              } else if (_tstoreSupport) {
                  assembly {
                      tstore(storageSlot, value)
                  }
              } else {
                  assembly {
                      sstore(storageSlot, value)
                  }
              }
          }
          /**
           * @dev Internal function to read a TSTORISH value.
           *
           * @param storageSlot The slot to read the TSTORISH value from.
           *
           * @return value The TSTORISH value at the given storage slot.
           */
          function _getTstorish(
              uint256 storageSlot
          ) internal view returns (uint256 value) {
              if (_tstoreInitialSupport) {
                  assembly {
                      value := tload(storageSlot)
                  }
              } else if (_tstoreSupport) {
                  assembly {
                      value := tload(storageSlot)
                  }
              } else {
                  assembly {
                      value := sload(storageSlot)
                  }
              }
          }
          /**
           * @dev Internal function to clear a TSTORISH value.
           *
           * @param storageSlot The slot to clear the TSTORISH value for.
           */
          function _clearTstorish(uint256 storageSlot) internal {
              if (_tstoreInitialSupport) {
                  assembly {
                      tstore(storageSlot, 0)
                  }
              } else if (_tstoreSupport) {
                  assembly {
                      tstore(storageSlot, 0)
                  }
              } else {
                  assembly {
                      sstore(storageSlot, 0)
                  }
              }
          }
          /**
           * @dev Private function to deploy a test contract that utilizes TLOAD as
           *      part of its fallback logic.
           */
          function _prepareTloadTest() private returns (address contractAddress) {
              // Utilize assembly to deploy a contract testing TLOAD support.
              assembly {
                  // Write the contract deployment code payload to scratch space.
                  mstore(0, _TLOAD_TEST_PAYLOAD)
                  // Deploy the contract.
                  contractAddress := create(
                      0,
                      _TLOAD_TEST_PAYLOAD_OFFSET,
                      _TLOAD_TEST_PAYLOAD_LENGTH
                  )
              }
          }
          /**
           * @dev Private view function to determine if TSTORE/TLOAD are supported by
           *      the current EVM implementation by attempting to call the test
           *      contract, which utilizes TLOAD as part of its fallback logic.
           */
          function _testTload(
              address tloadTestContract
          ) private view returns (bool ok) {
              // Call the test contract, which will perform a TLOAD test. If the call
              // does not revert, then TLOAD/TSTORE is supported. Do not forward all
              // available gas, as all forwarded gas will be consumed on revert.
              (ok, ) = tloadTestContract.staticcall{ gas: gasleft() / 10 }("");
          }
      }// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165 {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.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
      pragma solidity ^0.8.4;
      /// @dev Thrown when a stored approval exceeds type(uint200).max
      error PermitC__AmountExceedsStorageMaximum();
      /// @dev Thrown when a transfer amount requested exceeds the permitted amount
      error PermitC__ApprovalTransferExceededPermittedAmount();
      /// @dev Thrown when a transfer is requested after the permit has expired
      error PermitC__ApprovalTransferPermitExpiredOrUnset();
      /// @dev Thrown when attempting to close an order by an account that is not the owner or operator
      error PermitC__CallerMustBeOwnerOrOperator();
      /// @dev Thrown when attempting to approve a token type that is not valid for PermitC
      error PermitC__InvalidTokenType();
      /// @dev Thrown when attempting to invalidate a nonce that has already been used
      error PermitC__NonceAlreadyUsedOrRevoked();
      /// @dev Thrown when attempting to restore a nonce that has not been used
      error PermitC__NonceNotUsedOrRevoked();
      /// @dev Thrown when attempting to fill an order that has already been filled or cancelled
      error PermitC__OrderIsEitherCancelledOrFilled();
      /// @dev Thrown when a transfer amount requested exceeds the permitted amount
      error PermitC__SignatureTransferExceededPermittedAmount();
      /// @dev Thrown when a transfer is requested after the permit has expired
      error PermitC__SignatureTransferExceededPermitExpired();
      /// @dev Thrown when attempting to use an advanced permit typehash that is not registered
      error PermitC__SignatureTransferPermitHashNotRegistered();
      /// @dev Thrown when a permit signature is invalid
      error PermitC__SignatureTransferInvalidSignature();
      /// @dev Thrown when the remaining fill amount is less than the requested minimum fill
      error PermitC__UnableToFillMinimumRequestedQuantity();// SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
      pragma solidity ^0.8.0;
      import "../token/ERC20/IERC20.sol";
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol)
      pragma solidity ^0.8.0;
      import "../token/ERC721/IERC721.sol";
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155.sol)
      pragma solidity ^0.8.0;
      import "../token/ERC1155/IERC1155.sol";
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC1271 standard signature validation method for
       * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
       *
       * _Available since v4.1._
       */
      interface IERC1271 {
          /**
           * @dev Should return whether the signature provided is valid for the provided data
           * @param hash      Hash of the data to be signed
           * @param signature Signature byte array associated with _data
           */
          function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import {Context} from "@openzeppelin/contracts/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 {
          error Ownable__CallerIsNotOwner();
          error Ownable__NewOwnerIsZeroAddress();
          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 {
              if(owner() != _msgSender()) revert Ownable__CallerIsNotOwner();
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby disabling 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 {
              if(newOwner == address(0)) revert Ownable__NewOwnerIsZeroAddress();
              _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 (last updated v4.9.0) (utils/cryptography/EIP712.sol)
      pragma solidity ^0.8.8;
      import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
      /**
       * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
       *
       * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
       * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
       * they need in their contracts using a combination of `abi.encode` and `keccak256`.
       *
       * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
       * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
       * ({_hashTypedDataV4}).
       *
       * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
       * the chain id to protect against replay attacks on an eventual fork of the chain.
       *
       * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
       * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
       *
       * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
       * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
       * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
       *
       * _Available since v3.4._
       *
       * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
       */
      abstract contract EIP712 {
          bytes32 private constant _TYPE_HASH =
              keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
          // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
          // invalidate the cached domain separator if the chain id changes.
          bytes32 private immutable _cachedDomainSeparator;
          uint256 private immutable _cachedChainId;
          bytes32 private immutable _hashedName;
          bytes32 private immutable _hashedVersion;
          /**
           * @dev Initializes the domain separator and parameter caches.
           *
           * The meaning of `name` and `version` is specified in
           * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
           *
           * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
           * - `version`: the current major version of the signing domain.
           *
           * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
           * contract upgrade].
           */
          constructor(string memory name, string memory version) {
              _hashedName = keccak256(bytes(name));
              _hashedVersion = keccak256(bytes(version));
              _cachedChainId = block.chainid;
              _cachedDomainSeparator = _buildDomainSeparator();
          }
          /**
           * @dev Returns the domain separator for the current chain.
           */
          function _domainSeparatorV4() internal view returns (bytes32) {
              if (block.chainid == _cachedChainId) {
                  return _cachedDomainSeparator;
              } else {
                  return _buildDomainSeparator();
              }
          }
          function _buildDomainSeparator() private view returns (bytes32) {
              return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
          }
          /**
           * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
           * function returns the hash of the fully encoded EIP712 message for this domain.
           *
           * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
           *
           * ```solidity
           * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
           *     keccak256("Mail(address to,string contents)"),
           *     mailTo,
           *     keccak256(bytes(mailContents))
           * )));
           * address signer = ECDSA.recover(digest, signature);
           * ```
           */
          function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
              return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @dev Constant bytes32 value of 0x000...000
      bytes32 constant ZERO_BYTES32 = bytes32(0);
      /// @dev Constant value of 0
      uint256 constant ZERO = 0;
      /// @dev Constant value of 1
      uint256 constant ONE = 1;
      /// @dev Constant value representing an open order in storage
      uint8 constant ORDER_STATE_OPEN = 0;
      /// @dev Constant value representing a filled order in storage
      uint8 constant ORDER_STATE_FILLED = 1;
      /// @dev Constant value representing a cancelled order in storage
      uint8 constant ORDER_STATE_CANCELLED = 2;
      /// @dev Constant value representing the ERC721 token type for signatures and transfer hooks
      uint256 constant TOKEN_TYPE_ERC721 = 721;
      /// @dev Constant value representing the ERC1155 token type for signatures and transfer hooks
      uint256 constant TOKEN_TYPE_ERC1155 = 1155;
      /// @dev Constant value representing the ERC20 token type for signatures and transfer hooks
      uint256 constant TOKEN_TYPE_ERC20 = 20;
      /// @dev Constant value to mask the upper bits of a signature that uses a packed `vs` value to extract `s`
      bytes32 constant UPPER_BIT_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
      /// @dev EIP-712 typehash used for validating signature based stored approvals
      bytes32 constant UPDATE_APPROVAL_TYPEHASH =
          keccak256("UpdateApprovalBySignature(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 approvalExpiration,uint256 sigDeadline,uint256 masterNonce)");
      /// @dev EIP-712 typehash used for validating a single use permit without additional data
      bytes32 constant SINGLE_USE_PERMIT_TYPEHASH =
          keccak256("PermitTransferFrom(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 expiration,uint256 masterNonce)");
      /// @dev EIP-712 typehash used for validating a single use permit with additional data
      string constant SINGLE_USE_PERMIT_TRANSFER_ADVANCED_TYPEHASH_STUB =
          "PermitTransferFromWithAdditionalData(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 nonce,address operator,uint256 expiration,uint256 masterNonce,";
      /// @dev EIP-712 typehash used for validating an order permit that updates storage as it fills
      string constant PERMIT_ORDER_ADVANCED_TYPEHASH_STUB =
          "PermitOrderWithAdditionalData(uint256 tokenType,address token,uint256 id,uint256 amount,uint256 salt,address operator,uint256 expiration,uint256 masterNonce,";
      /// @dev Pausable flag for stored approval transfers of ERC721 assets
      uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC721 = 1 << 0;
      /// @dev Pausable flag for stored approval transfers of ERC1155 assets
      uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC1155 = 1 << 1;
      /// @dev Pausable flag for stored approval transfers of ERC20 assets
      uint256 constant PAUSABLE_APPROVAL_TRANSFER_FROM_ERC20 = 1 << 2;
      /// @dev Pausable flag for single use permit transfers of ERC721 assets
      uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC721 = 1 << 3;
      /// @dev Pausable flag for single use permit transfers of ERC1155 assets
      uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC1155 = 1 << 4;
      /// @dev Pausable flag for single use permit transfers of ERC20 assets
      uint256 constant PAUSABLE_PERMITTED_TRANSFER_FROM_ERC20 = 1 << 5;
      /// @dev Pausable flag for order fill transfers of ERC1155 assets
      uint256 constant PAUSABLE_ORDER_TRANSFER_FROM_ERC1155 = 1 << 6;
      /// @dev Pausable flag for order fill transfers of ERC20 assets
      uint256 constant PAUSABLE_ORDER_TRANSFER_FROM_ERC20 = 1 << 7;// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @dev Storage data struct for stored approvals and order approvals
      struct PackedApproval {
          // Only used for partial fill position 1155 transfers
          uint8 state;
          // Amount allowed
          uint200 amount;
          // Permission expiry
          uint48 expiration;
      }
      /// @dev Calldata data struct for order fill amounts
      struct OrderFillAmounts {
          uint256 orderStartAmount;
          uint256 requestedFillAmount;
          uint256 minimumFillAmount;
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.24;
      import {SINGLE_USE_PERMIT_TYPEHASH, UPDATE_APPROVAL_TYPEHASH} from "../Constants.sol";
      library PermitHash {
          /**
           * @notice  Hashes the permit data for a stored approval
           * 
           * @param tokenType           The type of token
           * @param token               The address of the token
           * @param id                  The id of the token
           * @param amount              The amount authorized by the owner signature
           * @param nonce               The nonce for the permit
           * @param operator            The account that is allowed to use the permit
           * @param approvalExpiration  The time the permit approval expires
           * @param sigDeadline         The deadline for submitting the permit onchain
           * @param masterNonce         The signers master nonce
           * 
           * @return hash  The hash of the permit data
           */
          function hashOnChainApproval(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 amount,
              uint256 nonce,
              address operator, 
              uint256 approvalExpiration,
              uint256 sigDeadline,
              uint256 masterNonce
          ) internal pure returns (bytes32 hash) {
              hash = keccak256(
                  abi.encode(
                      UPDATE_APPROVAL_TYPEHASH,
                      tokenType,
                      token,
                      id,
                      amount,
                      nonce,
                      operator,
                      approvalExpiration,
                      sigDeadline,
                      masterNonce
                  )
              );
          }
          /**
           * @notice  Hashes the permit data with the single user permit without additional data typehash
           * 
           * @param tokenType               The type of token
           * @param token                   The address of the token
           * @param id                      The id of the token
           * @param amount                  The amount authorized by the owner signature
           * @param nonce                   The nonce for the permit
           * @param expiration              The time the permit expires
           * @param masterNonce             The signers master nonce
           * 
           * @return hash  The hash of the permit data
           */
          function hashSingleUsePermit(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 amount,
              uint256 nonce,
              uint256 expiration,
              uint256 masterNonce
          ) internal view returns (bytes32 hash) {
              hash = keccak256(
                  abi.encode(
                      SINGLE_USE_PERMIT_TYPEHASH,
                      tokenType,
                      token,
                      id,
                      amount,
                      nonce,
                      msg.sender,
                      expiration,
                      masterNonce
                  )
              );
          }
          /**
           * @notice  Hashes the permit data with the supplied typehash
           * 
           * @param tokenType               The type of token
           * @param token                   The address of the token
           * @param id                      The id of the token
           * @param amount                  The amount authorized by the owner signature
           * @param nonce                   The nonce for the permit
           * @param expiration              The time the permit expires
           * @param additionalData          The additional data to validate with the permit signature
           * @param additionalDataTypeHash  The typehash of the permit to use for validating the signature
           * @param masterNonce             The signers master nonce
           * 
           * @return hash  The hash of the permit data with the supplied typehash
           */
          function hashSingleUsePermitWithAdditionalData(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 amount,
              uint256 nonce,
              uint256 expiration,
              bytes32 additionalData,
              bytes32 additionalDataTypeHash,
              uint256 masterNonce
          ) internal view returns (bytes32 hash) {
              hash = keccak256(
                  abi.encode(
                      additionalDataTypeHash,
                      tokenType,
                      token,
                      id,
                      amount,
                      nonce,
                      msg.sender,
                      expiration,
                      masterNonce,
                      additionalData
                  )
              );
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import {OrderFillAmounts} from "../DataTypes.sol";
      interface IPermitC {
          /**
           * =================================================
           * ==================== Events =====================
           * =================================================
           */
          /// @dev Emitted when an approval is stored
          event Approval(
              address indexed owner,
              address indexed token,
              address indexed operator,
              uint256 id,
              uint200 amount,
              uint48 expiration
          );
          /// @dev Emitted when a user increases their master nonce
          event Lockdown(address indexed owner);
          /// @dev Emitted when an order is opened
          event OrderOpened(
              bytes32 indexed orderId,
              address indexed owner,
              address indexed operator,
              uint256 fillableQuantity
          );
          /// @dev Emitted when an order has a fill
          event OrderFilled(
              bytes32 indexed orderId,
              address indexed owner,
              address indexed operator,
              uint256 amount
          );
          /// @dev Emitted when an order has been fully filled or cancelled
          event OrderClosed(
              bytes32 indexed orderId, 
              address indexed owner, 
              address indexed operator, 
              bool wasCancellation);
          /// @dev Emitted when an order has an amount restored due to a failed transfer
          event OrderRestored(
              bytes32 indexed orderId,
              address indexed owner,
              uint256 amountRestoredToOrder
          );
          /**
           * =================================================
           * ============== Approval Transfers ===============
           * =================================================
           */
          function approve(uint256 tokenType, address token, uint256 id, address operator, uint200 amount, uint48 expiration) external;
          function updateApprovalBySignature(
              uint256 tokenType,
              address token,
              uint256 id,
              uint256 nonce,
              uint200 amount,
              address operator,
              uint48 approvalExpiration,
              uint48 sigDeadline,
              address owner,
              bytes calldata signedPermit
          ) external;
          function allowance(
              address owner, 
              address operator, 
              uint256 tokenType,
              address token, 
              uint256 id
          ) external view returns (uint256 amount, uint256 expiration);
          /**
           * =================================================
           * ================ Signed Transfers ===============
           * =================================================
           */
          function registerAdditionalDataHash(string memory additionalDataTypeString) external;
          function permitTransferFromERC721(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 expiration,
              address owner,
              address to,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function permitTransferFromWithAdditionalDataERC721(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 expiration,
              address owner,
              address to,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function permitTransferFromERC1155(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function permitTransferFromWithAdditionalDataERC1155(
              address token,
              uint256 id,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function permitTransferFromERC20(
              address token,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function permitTransferFromWithAdditionalDataERC20(
              address token,
              uint256 nonce,
              uint256 permitAmount,
              uint256 expiration,
              address owner,
              address to,
              uint256 transferAmount,
              bytes32 additionalData,
              bytes32 advancedPermitHash,
              bytes calldata signedPermit
          ) external returns (bool isError);
          function isRegisteredTransferAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered);
          function isRegisteredOrderAdditionalDataHash(bytes32 hash) external view returns (bool isRegistered);
          /**
           * =================================================
           * =============== Order Transfers =================
           * =================================================
           */
          function fillPermittedOrderERC1155(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              uint256 id,
              address owner,
              address to,
              uint256 nonce,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) external returns (uint256 quantityFilled, bool isError);
          function fillPermittedOrderERC20(
              bytes calldata signedPermit,
              OrderFillAmounts calldata orderFillAmounts,
              address token,
              address owner,
              address to,
              uint256 nonce,
              uint48 expiration,
              bytes32 orderId,
              bytes32 advancedPermitHash
          ) external returns (uint256 quantityFilled, bool isError);
          function closePermittedOrder(
              address owner,
              address operator,
              uint256 tokenType,
              address token,
              uint256 id,
              bytes32 orderId
          ) external;
          function allowance(
              address owner, 
              address operator, 
              uint256 tokenType,
              address token, 
              uint256 id,
              bytes32 orderId
          ) external view returns (uint256 amount, uint256 expiration);
          /**
           * =================================================
           * ================ Nonce Management ===============
           * =================================================
           */
          function invalidateUnorderedNonce(uint256 nonce) external;
          function isValidUnorderedNonce(address owner, uint256 nonce) external view returns (bool isValid);
          function lockdown() external;
          function masterNonce(address owner) external view returns (uint256);
          /**
           * =================================================
           * ============== Transfer Functions ===============
           * =================================================
           */
          function transferFromERC721(
              address from,
              address to,
              address token,
              uint256 id
          ) external returns (bool isError);
          function transferFromERC1155(
              address from,
              address to,
              address token,
              uint256 id,
              uint256 amount
          ) external returns (bool isError);
          function transferFromERC20(
              address from,
              address to,
              address token,
              uint256 amount
          ) external returns (bool isError);
          /**
           * =================================================
           * ============ Signature Verification =============
           * =================================================
           */
          function domainSeparatorV4() external view returns (bytes32);
      }
      //SPDX-License-Identifier: MIT
      pragma solidity ^0.8.24;
      /*
                                                           @@@@@@@@@@@@@@             
                                                          @@@@@@@@@@@@@@@@@@(         
                                                         @@@@@@@@@@@@@@@@@@@@@        
                                                        @@@@@@@@@@@@@@@@@@@@@@@@      
                                                                 #@@@@@@@@@@@@@@      
                                                                     @@@@@@@@@@@@     
                                  @@@@@@@@@@@@@@*                    @@@@@@@@@@@@     
                                 @@@@@@@@@@@@@@@     @               @@@@@@@@@@@@     
                                @@@@@@@@@@@@@@@     @                @@@@@@@@@@@      
                               @@@@@@@@@@@@@@@     @@               @@@@@@@@@@@@      
                              @@@@@@@@@@@@@@@     #@@             @@@@@@@@@@@@/       
                              @@@@@@@@@@@@@@.     @@@@@@@@@@@@@@@@@@@@@@@@@@@         
                             @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@            
                            @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@             
                           @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                          @@@@@@@@@@@@@@@     @@@@@&%%%%%%%%&&@@@@@@@@@@@@@@          
                          @@@@@@@@@@@@@@      @@@@@               @@@@@@@@@@@         
                         @@@@@@@@@@@@@@@     @@@@@                 @@@@@@@@@@@        
                        @@@@@@@@@@@@@@@     @@@@@@                 @@@@@@@@@@@        
                       @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@        
                      @@@@@@@@@@@@@@@     @@@@@@@                 @@@@@@@@@@@&        
                      @@@@@@@@@@@@@@     *@@@@@@@               (@@@@@@@@@@@@         
                     @@@@@@@@@@@@@@@     @@@@@@@@             @@@@@@@@@@@@@@          
                    @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           
                   @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            
                  @@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              
                 .@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                 
                 @@@@@@@@@@@@@@%     @@@@@@@@@@@@@@@@@@@@@@@@(                        
                @@@@@@@@@@@@@@@                                                       
               @@@@@@@@@@@@@@@                                                        
              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                         
             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                          
             @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&                                          
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                           
       
      * @title CollateralizedPausableFlags
      * @custom:version 1.0.0
      * @author Limit Break, Inc.
      * @description Collateralized Pausable Flags is an extension for contracts
      *              that require features to be pausable in the event of potential
      *              or actual threats without incurring a storage read overhead cost
      *              during normal operations by using contract starting balance as
      *              a signal for checking the paused state.
      *
      *              Using contract balance to enable checking paused state creates an
      *              economic penalty for developers that deploy code that can be 
      *              exploited as well as an economic incentive (recovery of collateral)
      *              for them to mitigate the threat.
      *
      *              Developers implementing Collateralized Pausable Flags should consider
      *              their risk mitigation strategy and ensure funds are readily available
      *              for pausing if ever necessary by setting an appropriate threshold 
      *              value and considering use of an escrow contract that can initiate the
      *              pause with funds.
      *
      *              There is no restriction on the depositor as this can be easily 
      *              circumvented through a `SELFDESTRUCT` opcode.
      *
      *              Developers must be aware of potential outflows from the contract that
      *              could reduce collateral below the pausable check threshold and protect
      *              against those methods when pausing is required.
      */
      abstract contract CollateralizedPausableFlags {
          /// @dev Emitted when the pausable flags are updated
          event PausableFlagsUpdated(uint256 previousFlags, uint256 newFlags);
          /// @dev Thrown when an execution path requires a flag to not be paused but it is paused
          error CollateralizedPausableFlags__Paused();
          /// @dev Thrown when an executin path requires a flag to be paused but it is not paused
          error CollateralizedPausableFlags__NotPaused();
          /// @dev Thrown when a call to withdraw funds fails
          error CollateralizedPausableFlags__WithdrawFailed();
          /// @dev Immutable variable that defines the native funds threshold before flags are checked
          uint256 private immutable nativeValueToCheckPauseState;
          /// @dev Flags for current pausable state, each bit is considered a separate flag
          uint256 private pausableFlags;
          /// @dev Immutable pointer for the _requireNotPaused function to use based on value threshold
          function(uint256) internal view immutable _requireNotPaused;
          /// @dev Immutable pointer for the _requirePaused function to use based on value threshold
          function(uint256) internal view immutable _requirePaused;
          /// @dev Immutable pointer for the _getPausableFlags function to use based on value threshold
          function() internal view returns (uint256) immutable _getPausableFlags;
          constructor(uint256 _nativeValueToCheckPauseState) {
              // Optimizes value check at runtime by reducing the stored immutable 
              // value by 1 so that greater than can be used instead of greater 
              // than or equal while allowing the deployment parameter to reflect 
              // the value at which the deployer wants to trigger pause checking.
              // Example: 
              //     Constructed with a value of 1000
              //     Immutable value stored is 999
              //     State checking enabled at 1000 units deposited because
              //     1000 > 999 evaluates true
              if (_nativeValueToCheckPauseState > 0) {
                  unchecked {
                      _nativeValueToCheckPauseState -= 1;
                  }
                  _requireNotPaused = _requireNotPausedWithCollateralCheck;
                  _requirePaused = _requirePausedWithCollateralCheck;
                  _getPausableFlags = _getPausableFlagsWithCollateralCheck;
              } else {
                  _requireNotPaused = _requireNotPausedWithoutCollateralCheck;
                  _requirePaused = _requirePausedWithoutCollateralCheck;
                  _getPausableFlags = _getPausableFlagsWithoutCollateralCheck;
              }
              nativeValueToCheckPauseState = _nativeValueToCheckPauseState;
          }
          /**
           * @dev  Modifier to make a function callable only when the specified flags are not paused
           * @dev  Throws when any of the flags specified are paused
           * 
           * @param _flags  The flags to check for pause state
           */
          modifier whenNotPaused(uint256 _flags) {
              _requireNotPaused(_flags);
              _;
          }
          /**
           * @dev  Modifier to make a function callable only when the specified flags are paused
           * @dev  Throws when any of the flags specified are not paused
           * 
           * @param _flags  The flags to check for pause state
           */
          modifier whenPaused(uint256 _flags) {
              _requirePaused(_flags);
              _;
          }
          /**
           * @dev  Modifier to make a function callable only by a permissioned account
           * @dev  Throws when the caller does not have permission
           */
          modifier onlyPausePermissionedCaller() {
              _requireCallerHasPausePermissions();
              _;
          }
          /**
           * @notice  Updates the pausable flags settings
           * 
           * @dev     Throws when the caller does not have permission
           * @dev     **NOTE:** Pausable flag settings will only take effect if contract balance exceeds 
           * @dev     `nativeValueToPause`
           * 
           * @dev     <h4>Postconditions:</h4>
           * @dev     1. address(this).balance increases by msg.value
           * @dev     2. `pausableFlags` is set to the new value
           * @dev     3. Emits a PausableFlagsUpdated event
           * 
           * @param _pausableFlags  The new pausable flags to set
           */
          function pause(uint256 _pausableFlags) external payable onlyPausePermissionedCaller {
              _setPausableFlags(_pausableFlags);
          }
          /**
           * @notice  Allows any account to supply funds for enabling the pausable checks
           * 
           * @dev     **NOTE:** The threshold check for pausable collateral does not pause
           * @dev     any functions unless the associated pausable flag is set.
           */
          function pausableDepositCollateral() external payable {
              // thank you for your contribution to safety
          }
          /**
           * @notice  Resets all pausable flags to unpaused and withdraws funds
           * 
           * @dev     Throws when the caller does not have permission
           * 
           * @dev     <h4>Postconditions:</h4>
           * @dev     1. `pausableFlags` is set to zero
           * @dev     2. Emits a PausableFlagsUpdated event
           * @dev     3. Transfers `withdrawAmount` of native funds to `withdrawTo` if non-zero
           * 
           * @param withdrawTo      The address to withdraw the collateral to
           * @param withdrawAmount  The amount of collateral to withdraw
           */
          function unpause(address withdrawTo, uint256 withdrawAmount) external onlyPausePermissionedCaller {
              _setPausableFlags(0);
              if (withdrawAmount > 0) {
                  (bool success, ) = withdrawTo.call{value: withdrawAmount}("");
                  if(!success) revert CollateralizedPausableFlags__WithdrawFailed();
              }
          }
          /**
           * @notice  Returns collateralized pausable configuration information
           * 
           * @return _nativeValueToCheckPauseState  The collateral required to enable pause state checking
           * @return _pausableFlags                 The current pausable flags set, only checked when collateral met
           */
          function pausableConfigurationSettings() external view returns(
              uint256 _nativeValueToCheckPauseState, 
              uint256 _pausableFlags
          ) {
              unchecked {
                  _nativeValueToCheckPauseState = nativeValueToCheckPauseState + 1;
                  _pausableFlags = pausableFlags;
              }
          }
          /**
           * @notice  Updates the `pausableFlags` variable and emits a PausableFlagsUpdated event
           * 
           * @param _pausableFlags  The new pausable flags to set
           */
          function _setPausableFlags(uint256 _pausableFlags) internal {
              uint256 previousFlags = pausableFlags;
              pausableFlags = _pausableFlags;
              emit PausableFlagsUpdated(previousFlags, _pausableFlags);
          }
          /**
           * @notice  Checks the current pause state of the supplied flags and reverts if any are paused
           * 
           * @dev     *Should* be called prior to any transfers of native funds out of the contract for efficiency
           * @dev     Throws when the native funds balance is greater than the value to enable pausing AND
           * @dev     one or more of the supplied `_flags` is paused.
           * 
           * @param _flags  The flags to check for pause state
           */
          function _requireNotPausedWithCollateralCheck(uint256 _flags) private view {
              if (_nativeBalanceSubMsgValue() > nativeValueToCheckPauseState) {
                  if (pausableFlags & _flags > 0) {
                      revert CollateralizedPausableFlags__Paused();
                  }
              }
          }
          /**
           * @notice  Checks the current pause state of the supplied flags and reverts if any are paused
           * 
           * @dev     Throws when one or more of the supplied `_flags` is paused.
           * 
           * @param _flags  The flags to check for pause state
           */
          function _requireNotPausedWithoutCollateralCheck(uint256 _flags) private view {
              if (pausableFlags & _flags > 0) {
                  revert CollateralizedPausableFlags__Paused();
              }
          }
          /**
           * @notice  Checks the current pause state of the supplied flags and reverts if none are paused
           * 
           * @dev     *Should* be called prior to any transfers of native funds out of the contract for efficiency
           * @dev     Throws when the native funds balance is not greater than the value to enable pausing OR
           * @dev     none of the supplied `_flags` are paused.
           * 
           * @param _flags  The flags to check for pause state
           */
          function _requirePausedWithCollateralCheck(uint256 _flags) private view {
              if (_nativeBalanceSubMsgValue() <= nativeValueToCheckPauseState) {
                  revert CollateralizedPausableFlags__NotPaused();
              } else if (pausableFlags & _flags == 0) {
                  revert CollateralizedPausableFlags__NotPaused();
              }
          }
          /**
           * @notice  Checks the current pause state of the supplied flags and reverts if none are paused
           * 
           * @dev     Throws when none of the supplied `_flags` are paused.
           * 
           * @param _flags  The flags to check for pause state
           */
          function _requirePausedWithoutCollateralCheck(uint256 _flags) private view {
              if (pausableFlags & _flags == 0) {
                  revert CollateralizedPausableFlags__NotPaused();
              }
          }
          /**
           * @notice  Returns the current state of the pausable flags
           * 
           * @dev     Will return zero if the native funds balance is not greater than the value to enable pausing
           * 
           * @return _pausableFlags  The current state of the pausable flags
           */
          function _getPausableFlagsWithCollateralCheck() private view returns(uint256 _pausableFlags) {
              if (_nativeBalanceSubMsgValue() > nativeValueToCheckPauseState) {
                  _pausableFlags = pausableFlags;
              }
          }
          /**
           * @notice  Returns the current state of the pausable flags
           * 
           * @return _pausableFlags  The current state of the pausable flags
           */
          function _getPausableFlagsWithoutCollateralCheck() private view returns(uint256 _pausableFlags) {
              _pausableFlags = pausableFlags;
          }
          /**
           * @notice  Returns the current contract balance minus the value sent with the call
           * 
           * @dev     This is expected to be the contract balance at the beginning of a function call
           * @dev     to efficiently determine whether a contract has the necessary collateral to enable
           * @dev     the pausable flags checking for contracts that hold native token funds.
           * @dev     This should **NOT** be used in any way to determine current balance for contract logic
           * @dev     other than its intended purpose for pause state checking activation.
           */
          function _nativeBalanceSubMsgValue() private view returns (uint256 _value) {
              unchecked {
                  _value = address(this).balance - msg.value;
              }
          }
          /**
           * @dev  To be implemented by an inheriting contract for authorization to `pause` and `unpause` 
           * @dev  functions as well as any functions in the inheriting contract that utilize the
           * @dev  `onlyPausePermissionedCaller` modifier.
           * 
           * @dev  Implementing contract function **MUST** throw when the caller is not permissioned
           */
          function _requireCallerHasPausePermissions() internal view virtual;
      }// 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.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `from` to `to` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // 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
      // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)
      pragma solidity ^0.8.0;
      import "../../utils/introspection/IERC165.sol";
      /**
       * @dev Required interface of an ERC1155 compliant contract, as defined in the
       * https://eips.ethereum.org/EIPS/eip-1155[EIP].
       *
       * _Available since v3.1._
       */
      interface IERC1155 is IERC165 {
          /**
           * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
           */
          event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
          /**
           * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
           * transfers.
           */
          event TransferBatch(
              address indexed operator,
              address indexed from,
              address indexed to,
              uint256[] ids,
              uint256[] values
          );
          /**
           * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
           * `approved`.
           */
          event ApprovalForAll(address indexed account, address indexed operator, bool approved);
          /**
           * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
           *
           * If an {URI} event was emitted for `id`, the standard
           * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
           * returned by {IERC1155MetadataURI-uri}.
           */
          event URI(string value, uint256 indexed id);
          /**
           * @dev Returns the amount of tokens of token type `id` owned by `account`.
           *
           * Requirements:
           *
           * - `account` cannot be the zero address.
           */
          function balanceOf(address account, uint256 id) external view returns (uint256);
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
           *
           * Requirements:
           *
           * - `accounts` and `ids` must have the same length.
           */
          function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
              external
              view
              returns (uint256[] memory);
          /**
           * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
           *
           * Emits an {ApprovalForAll} event.
           *
           * Requirements:
           *
           * - `operator` cannot be the caller.
           */
          function setApprovalForAll(address operator, bool approved) external;
          /**
           * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address account, address operator) external view returns (bool);
          /**
           * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
           *
           * Emits a {TransferSingle} event.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
           * - `from` must have a balance of tokens of type `id` of at least `amount`.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
           * acceptance magic value.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 id,
              uint256 amount,
              bytes calldata data
          ) external;
          /**
           * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
           *
           * Emits a {TransferBatch} event.
           *
           * Requirements:
           *
           * - `ids` and `amounts` must have the same length.
           * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
           * acceptance magic value.
           */
          function safeBatchTransferFrom(
              address from,
              address to,
              uint256[] calldata ids,
              uint256[] calldata amounts,
              bytes calldata data
          ) external;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
      pragma solidity ^0.8.0;
      import "../Strings.sol";
      /**
       * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
       *
       * These functions can be used to verify that a message was signed by the holder
       * of the private keys of a given address.
       */
      library ECDSA {
          enum RecoverError {
              NoError,
              InvalidSignature,
              InvalidSignatureLength,
              InvalidSignatureS,
              InvalidSignatureV // Deprecated in v4.8
          }
          function _throwError(RecoverError error) private pure {
              if (error == RecoverError.NoError) {
                  return; // no error: do nothing
              } else if (error == RecoverError.InvalidSignature) {
                  revert("ECDSA: invalid signature");
              } else if (error == RecoverError.InvalidSignatureLength) {
                  revert("ECDSA: invalid signature length");
              } else if (error == RecoverError.InvalidSignatureS) {
                  revert("ECDSA: invalid signature 's' value");
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature` or error string. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           *
           * Documentation for signature generation:
           * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
           * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
           *
           * _Available since v4.3._
           */
          function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
              if (signature.length == 65) {
                  bytes32 r;
                  bytes32 s;
                  uint8 v;
                  // ecrecover takes the signature parameters, and the only way to get them
                  // currently is to use assembly.
                  /// @solidity memory-safe-assembly
                  assembly {
                      r := mload(add(signature, 0x20))
                      s := mload(add(signature, 0x40))
                      v := byte(0, mload(add(signature, 0x60)))
                  }
                  return tryRecover(hash, v, r, s);
              } else {
                  return (address(0), RecoverError.InvalidSignatureLength);
              }
          }
          /**
           * @dev Returns the address that signed a hashed message (`hash`) with
           * `signature`. This address can then be used for verification purposes.
           *
           * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
           * this function rejects them by requiring the `s` value to be in the lower
           * half order, and the `v` value to be either 27 or 28.
           *
           * IMPORTANT: `hash` _must_ be the result of a hash operation for the
           * verification to be secure: it is possible to craft signatures that
           * recover to arbitrary addresses for non-hashed data. A safe way to ensure
           * this is by receiving a hash of the original message (which may otherwise
           * be too long), and then calling {toEthSignedMessageHash} on it.
           */
          function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, signature);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
           *
           * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address, RecoverError) {
              bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
              uint8 v = uint8((uint256(vs) >> 255) + 27);
              return tryRecover(hash, v, r, s);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
           *
           * _Available since v4.2._
           */
          function recover(
              bytes32 hash,
              bytes32 r,
              bytes32 vs
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, r, vs);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
           * `r` and `s` signature fields separately.
           *
           * _Available since v4.3._
           */
          function tryRecover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address, RecoverError) {
              // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
              // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
              // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
              // signatures from current libraries generate a unique signature with an s-value in the lower half order.
              //
              // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
              // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
              // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
              // these malleable signatures as well.
              if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                  return (address(0), RecoverError.InvalidSignatureS);
              }
              // If the signature is valid (and not malleable), return the signer address
              address signer = ecrecover(hash, v, r, s);
              if (signer == address(0)) {
                  return (address(0), RecoverError.InvalidSignature);
              }
              return (signer, RecoverError.NoError);
          }
          /**
           * @dev Overload of {ECDSA-recover} that receives the `v`,
           * `r` and `s` signature fields separately.
           */
          function recover(
              bytes32 hash,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) internal pure returns (address) {
              (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
              _throwError(error);
              return recovered;
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from a `hash`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
              // 32 is the length in bytes of hash,
              // enforced by the type signature above
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      32", hash));
          }
          /**
           * @dev Returns an Ethereum Signed Message, created from `s`. This
           * produces hash corresponding to the one signed with the
           * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
           * JSON-RPC method as part of EIP-191.
           *
           * See {recover}.
           */
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
      ", Strings.toString(s.length), s));
          }
          /**
           * @dev Returns an Ethereum Signed Typed Data, created from a
           * `domainSeparator` and a `structHash`. This produces hash corresponding
           * to the one signed with the
           * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
           * JSON-RPC method as part of EIP-712.
           *
           * See {recover}.
           */
          function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
              return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
          }
      }
      // 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/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);
              }
          }
      }