ETH Price: $1,973.56 (+0.25%)

Transaction Decoder

Block:
24508931 at Feb-22-2026 12:49:59 AM +UTC
Transaction Fee:
0.000004511878924408 ETH $0.008904
Gas Used:
138,406 Gas / 0.032598868 Gwei

Emitted Events:

600 0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000009f645bb08bbf5fe431213d6fd5481b6a8bd9ba11, 0x00000000000000000000000000000000000000000000000000000000000004d8 )
601 0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000009f645bb08bbf5fe431213d6fd5481b6a8bd9ba11, 0x00000000000000000000000000000000000000000000000000000000000004d9 )
602 0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000009f645bb08bbf5fe431213d6fd5481b6a8bd9ba11, 0x00000000000000000000000000000000000000000000000000000000000004da )
603 0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000009f645bb08bbf5fe431213d6fd5481b6a8bd9ba11, 0x00000000000000000000000000000000000000000000000000000000000004db )
604 0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000009f645bb08bbf5fe431213d6fd5481b6a8bd9ba11, 0x00000000000000000000000000000000000000000000000000000000000004dc )

Account State Difference:

  Address   Before After State Difference Code
(quasarbuilder)
21.064507397911523923 Eth21.064507411752123923 Eth0.0000000138406
0x9f645Bb0...A8bd9bA11
0.000022134118806889 Eth
Nonce: 240
0.000017622239882481 Eth
Nonce: 241
0.000004511878924408
0xaC6FFd76...76a23187C

Execution Trace

0xac6ffd76087eeb7a3afb4a0f0c940aa76a23187c.4a21a2df( )
  • ArchetypeErc721a.mint( )
    • ArchetypeLogicErc721a.704c38fc( )
    • ArchetypeLogicErc721a.4c1b6fbc( )
    • ArchetypeLogicErc721a.05cdd44e( )
      File 1 of 2: ArchetypeErc721a
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC2981.sol)
      pragma solidity ^0.8.0;
      import "../utils/introspection/IERC165Upgradeable.sol";
      /**
       * @dev Interface for the NFT Royalty Standard.
       *
       * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
       * support for royalty payments across all NFT marketplaces and ecosystem participants.
       *
       * _Available since v4.5._
       */
      interface IERC2981Upgradeable is IERC165Upgradeable {
          /**
           * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
           * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
           */
          function royaltyInfo(
              uint256 tokenId,
              uint256 salePrice
          ) external view returns (address receiver, uint256 royaltyAmount);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.2;
      import "../../utils/AddressUpgradeable.sol";
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
       * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
       * case an upgrade adds a module that needs to be initialized.
       *
       * For example:
       *
       * [.hljs-theme-light.nopadding]
       * ```solidity
       * contract MyToken is ERC20Upgradeable {
       *     function initialize() initializer public {
       *         __ERC20_init("MyToken", "MTK");
       *     }
       * }
       *
       * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
       *     function initializeV2() reinitializer(2) public {
       *         __ERC20Permit_init("MyToken");
       *     }
       * }
       * ```
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       *
       * [CAUTION]
       * ====
       * Avoid leaving a contract uninitialized.
       *
       * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
       * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
       * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
       *
       * [.hljs-theme-light.nopadding]
       * ```
       * /// @custom:oz-upgrades-unsafe-allow constructor
       * constructor() {
       *     _disableInitializers();
       * }
       * ```
       * ====
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           * @custom:oz-retyped-from bool
           */
          uint8 private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint8 version);
          /**
           * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
           * `onlyInitializing` functions can be used to initialize parent contracts.
           *
           * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
           * constructor.
           *
           * Emits an {Initialized} event.
           */
          modifier initializer() {
              bool isTopLevelCall = !_initializing;
              require(
                  (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                  "Initializable: contract is already initialized"
              );
              _initialized = 1;
              if (isTopLevelCall) {
                  _initializing = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
                  emit Initialized(1);
              }
          }
          /**
           * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
           * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
           * used to initialize parent contracts.
           *
           * A reinitializer may be used after the original initialization step. This is essential to configure modules that
           * are added through upgrades and that require initialization.
           *
           * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
           * cannot be nested. If one is invoked in the context of another, execution will revert.
           *
           * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
           * a contract, executing them in the right order is up to the developer or operator.
           *
           * WARNING: setting the version to 255 will prevent any future reinitialization.
           *
           * Emits an {Initialized} event.
           */
          modifier reinitializer(uint8 version) {
              require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
              _initialized = version;
              _initializing = true;
              _;
              _initializing = false;
              emit Initialized(version);
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} and {reinitializer} modifiers, directly or indirectly.
           */
          modifier onlyInitializing() {
              require(_initializing, "Initializable: contract is not initializing");
              _;
          }
          /**
           * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
           * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
           * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
           * through proxies.
           *
           * Emits an {Initialized} event the first time it is successfully executed.
           */
          function _disableInitializers() internal virtual {
              require(!_initializing, "Initializable: contract is initializing");
              if (_initialized != type(uint8).max) {
                  _initialized = type(uint8).max;
                  emit Initialized(type(uint8).max);
              }
          }
          /**
           * @dev Returns the highest version that has been initialized. See {reinitializer}.
           */
          function _getInitializedVersion() internal view returns (uint8) {
              return _initialized;
          }
          /**
           * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
           */
          function _isInitializing() internal view returns (bool) {
              return _initializing;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (token/common/ERC2981.sol)
      pragma solidity ^0.8.0;
      import "../../interfaces/IERC2981Upgradeable.sol";
      import "../../utils/introspection/ERC165Upgradeable.sol";
      import {Initializable} from "../../proxy/utils/Initializable.sol";
      /**
       * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information.
       *
       * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for
       * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first.
       *
       * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the
       * fee is specified in basis points by default.
       *
       * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
       * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
       * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
       *
       * _Available since v4.5._
       */
      abstract contract ERC2981Upgradeable is Initializable, IERC2981Upgradeable, ERC165Upgradeable {
          struct RoyaltyInfo {
              address receiver;
              uint96 royaltyFraction;
          }
          RoyaltyInfo private _defaultRoyaltyInfo;
          mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;
          function __ERC2981_init() internal onlyInitializing {
          }
          function __ERC2981_init_unchained() internal onlyInitializing {
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC165Upgradeable) returns (bool) {
              return interfaceId == type(IERC2981Upgradeable).interfaceId || super.supportsInterface(interfaceId);
          }
          /**
           * @inheritdoc IERC2981Upgradeable
           */
          function royaltyInfo(uint256 tokenId, uint256 salePrice) public view virtual override returns (address, uint256) {
              RoyaltyInfo memory royalty = _tokenRoyaltyInfo[tokenId];
              if (royalty.receiver == address(0)) {
                  royalty = _defaultRoyaltyInfo;
              }
              uint256 royaltyAmount = (salePrice * royalty.royaltyFraction) / _feeDenominator();
              return (royalty.receiver, royaltyAmount);
          }
          /**
           * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
           * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
           * override.
           */
          function _feeDenominator() internal pure virtual returns (uint96) {
              return 10000;
          }
          /**
           * @dev Sets the royalty information that all ids in this contract will default to.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: invalid receiver");
              _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Removes default royalty information.
           */
          function _deleteDefaultRoyalty() internal virtual {
              delete _defaultRoyaltyInfo;
          }
          /**
           * @dev Sets the royalty information for a specific token id, overriding the global default.
           *
           * Requirements:
           *
           * - `receiver` cannot be the zero address.
           * - `feeNumerator` cannot be greater than the fee denominator.
           */
          function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual {
              require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
              require(receiver != address(0), "ERC2981: Invalid parameters");
              _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
          }
          /**
           * @dev Resets royalty information for the token id back to the global default.
           */
          function _resetTokenRoyalty(uint256 tokenId) internal virtual {
              delete _tokenRoyaltyInfo[tokenId];
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[48] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
      pragma solidity ^0.8.1;
      /**
       * @dev Collection of functions related to the address type
       */
      library AddressUpgradeable {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           *
           * Furthermore, `isContract` will also return true if the target contract within
           * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
           * which only has an effect at the end of a transaction.
           * ====
           *
           * [IMPORTANT]
           * ====
           * You shouldn't rely on `isContract` to protect against flash loan attacks!
           *
           * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
           * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
           * constructor.
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize/address.code.length, which returns 0
              // for contracts in construction, since the code is only stored at the end
              // of the constructor execution.
              return account.code.length > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              (bool success, ) = recipient.call{value: amount}("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain `call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
           * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
           *
           * _Available since v4.8._
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              if (success) {
                  if (returndata.length == 0) {
                      // only check isContract if the call was successful and the return data is empty
                      // otherwise we already know that it was a contract
                      require(isContract(target), "Address: call to non-contract");
                  }
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason or using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  _revert(returndata, errorMessage);
              }
          }
          function _revert(bytes memory returndata, string memory errorMessage) private pure {
              // Look for revert reason and bubble it up if present
              if (returndata.length > 0) {
                  // The easiest way to bubble the revert reason is using memory via assembly
                  /// @solidity memory-safe-assembly
                  assembly {
                      let returndata_size := mload(returndata)
                      revert(add(32, returndata), returndata_size)
                  }
              } else {
                  revert(errorMessage);
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
      pragma solidity ^0.8.0;
      import "./IERC165Upgradeable.sol";
      import {Initializable} from "../../proxy/utils/Initializable.sol";
      /**
       * @dev Implementation of the {IERC165} interface.
       *
       * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
       * for the additional interface id that will be supported. For example:
       *
       * ```solidity
       * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
       *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
       * }
       * ```
       *
       * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
       */
      abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
          function __ERC165_init() internal onlyInitializing {
          }
          function __ERC165_init_unchained() internal onlyInitializing {
          }
          /**
           * @dev See {IERC165-supportsInterface}.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              return interfaceId == type(IERC165Upgradeable).interfaceId;
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC165 standard, as defined in the
       * https://eips.ethereum.org/EIPS/eip-165[EIP].
       *
       * Implementers can declare support of contract interfaces, which can then be
       * queried by others ({ERC165Checker}).
       *
       * For an implementation, see {ERC165}.
       */
      interface IERC165Upgradeable {
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30 000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.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.9.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 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
      // ArchetypePayouts v0.7.0
      //
      //        d8888                 888               888
      //       d88888                 888               888
      //      d88P888                 888               888
      //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
      //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
      //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
      //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
      // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
      //                                                            888 888
      //                                                       Y8b d88P 888
      //
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      error InvalidLength();
      error InvalidSplitShares();
      error TransferFailed();
      error BalanceEmpty();
      error NotApprovedToWithdraw();
      contract ArchetypePayouts {
        event Withdrawal(address indexed src, address token, uint256 wad);
        event FundsAdded(address indexed recipient, address token, uint256 amount);
        mapping(address => mapping(address => uint256)) private _balance;
        mapping(address => mapping(address => bool)) private _approvals;
        function updateBalances(
          uint256 totalAmount,
          address token,
          address[] calldata recipients,
          uint16[] calldata splits
        ) public payable {
          if (recipients.length != splits.length) {
            revert InvalidLength();
          }
          uint256 totalShares = 0;
          for (uint256 i = 0; i < splits.length; i++) {
            totalShares += splits[i];
          }
          if (totalShares != 10000) {
            revert InvalidSplitShares();
          }
          if (token == address(0)) {
            // ETH payments
            uint256 totalReceived = msg.value;
            for (uint256 i = 0; i < recipients.length; i++) {
              if (splits[i] > 0) {
                uint256 amountToAdd = (totalReceived * splits[i]) / 10000;
                _balance[recipients[i]][token] += amountToAdd;
                emit FundsAdded(recipients[i], token, amountToAdd);
              }
            }
          } else {
            // ERC20 payments
            IERC20 paymentToken = IERC20(token);
            bool success = paymentToken.transferFrom(msg.sender, address(this), totalAmount);
            if (!success) {
              revert TransferFailed();
            }
            for (uint256 i = 0; i < recipients.length; i++) {
              if (splits[i] > 0) {
                uint256 amountToAdd = (totalAmount * splits[i]) / 10000;
                _balance[recipients[i]][token] += amountToAdd;
                emit FundsAdded(recipients[i], token, amountToAdd);
              }
            }
          }
        }
        function withdraw() external {
          address msgSender = msg.sender;
          _withdraw(msgSender, msgSender, address(0));
        }
        function withdrawTokens(address[] memory tokens) external {
          address msgSender = msg.sender;
          for (uint256 i = 0; i < tokens.length; i++) {
            _withdraw(msgSender, msgSender, tokens[i]);
          }
        }
        function withdrawFrom(address from, address to) public {
          if (from != msg.sender && !_approvals[from][to]) {
            revert NotApprovedToWithdraw();
          }
          _withdraw(from, to, address(0));
        }
        function withdrawTokensFrom(
          address from,
          address to,
          address[] memory tokens
        ) public {
          if (from != msg.sender && !_approvals[from][to]) {
            revert NotApprovedToWithdraw();
          }
          for (uint256 i = 0; i < tokens.length; i++) {
            _withdraw(from, to, tokens[i]);
          }
        }
        function _withdraw(
          address from,
          address to,
          address token
        ) internal {
          uint256 wad;
          wad = _balance[from][token];
          _balance[from][token] = 0;
          if (wad == 0) {
            revert BalanceEmpty();
          }
          if (token == address(0)) {
            bool success = false;
            (success, ) = to.call{ value: wad }("");
            if (!success) {
              revert TransferFailed();
            }
          } else {
            IERC20 erc20Token = IERC20(token);
            bool success = erc20Token.transfer(to, wad);
            if (!success) {
              revert TransferFailed();
            }
          }
          emit Withdrawal(from, token, wad);
        }
        function approveWithdrawal(address delegate, bool approved) external {
          _approvals[msg.sender][delegate] = approved;
        }
        function isApproved(address from, address delegate) external view returns (bool) {
          return _approvals[from][delegate];
        }
        function balance(address recipient) external view returns (uint256) {
          return _balance[recipient][address(0)];
        }
        function balanceToken(address recipient, address token) external view returns (uint256) {
          return _balance[recipient][token];
        }
      }
      // SPDX-License-Identifier: MIT
      // Archetype v0.8.0
      //
      //        d8888                 888               888
      //       d88888                 888               888
      //      d88P888                 888               888
      //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
      //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
      //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
      //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
      // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
      //                                                            888 888
      //                                                       Y8b d88P 888
      //                                                        "Y88P"  888
      pragma solidity ^0.8.20;
      import "./ArchetypeLogicErc721a.sol";
      import "erc721a-upgradeable/contracts/ERC721AUpgradeable.sol";
      import "erc721a-upgradeable/contracts/ERC721A__Initializable.sol";
      import "erc721a-upgradeable/contracts/extensions/ERC721AQueryableUpgradeable.sol";
      import "./ERC721A__OwnableUpgradeable.sol";
      import "solady/src/utils/LibString.sol";
      import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
      contract ArchetypeErc721a is
        ERC721A__Initializable,
        ERC721AUpgradeable,
        ERC721A__OwnableUpgradeable,
        ERC2981Upgradeable,
        ERC721AQueryableUpgradeable
      {
        //
        // EVENTS
        //
        event Invited(bytes32 indexed key, bytes32 indexed cid);
        event BurnInvited(bytes32 indexed key, bytes32 indexed cid);
        event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
        event Withdrawal(address indexed src, address token, uint128 wad);
        //
        // VARIABLES
        //
        mapping(bytes32 => AdvancedInvite) public invites;
        mapping(bytes32 => uint256) public packedBonusDiscounts;
        mapping(bytes32 => BurnInvite) public burnInvites;
        mapping(address => mapping(bytes32 => uint256)) private _minted;
        mapping(bytes32 => uint256) private _listSupply;
        mapping(address => uint128) private _ownerBalance;
        mapping(address => mapping(address => uint128)) private _affiliateBalance;
        Config public config;
        PayoutConfig public payoutConfig;
        Options public options;
        //
        // METHODS
        //
        function initialize(
          string memory name,
          string memory symbol,
          Config calldata config_,
          PayoutConfig calldata payoutConfig_,
          address _receiver
        ) external initializerERC721A {
          __ERC721A_init(name, symbol);
          // check max bps not reached and min platform fee.
          if (
            config_.affiliateFee > MAXBPS ||
            config_.affiliateDiscount > MAXBPS ||
            config_.affiliateSigner == address(0) ||
            config_.maxBatchSize == 0
          ) {
            revert InvalidConfig();
          }
          config = config_;
          __Ownable_init();
          uint256 totalShares = payoutConfig_.ownerBps +
            payoutConfig_.platformBps +
            payoutConfig_.partnerBps +
            payoutConfig_.superAffiliateBps;
          if (payoutConfig_.platformBps < 250 || totalShares != 10000) {
            revert InvalidSplitShares();
          }
          payoutConfig = payoutConfig_;
          setDefaultRoyalty(_receiver, config.defaultRoyalty);
        }
        //
        // PUBLIC
        //
        function mint(
          Auth calldata auth,
          uint256 quantity,
          address affiliate,
          bytes calldata signature
        ) external payable {
          mintTo(auth, quantity, _msgSender(), affiliate, signature);
        }
        function batchMintTo(
          Auth calldata auth,
          address[] calldata toList,
          uint256[] calldata quantityList,
          address affiliate,
          bytes calldata signature
        ) external payable {
          if (quantityList.length != toList.length) {
            revert InvalidConfig();
          }
          AdvancedInvite storage invite = invites[auth.key];
          uint256 packedDiscount = packedBonusDiscounts[auth.key];
          uint256 curSupply = _totalMinted();
          
          uint256 totalQuantity;
          uint256 totalBonusMints;
          for (uint256 i; i < toList.length; ) {
            uint256 quantityToAdd;
            if (invite.unitSize > 1) {
              quantityToAdd = quantityList[i] * invite.unitSize;
            } else {
              quantityToAdd = quantityList[i];
            }
            uint256 numBonusMints = ArchetypeLogicErc721a.bonusMintsAwarded(quantityToAdd, packedDiscount);
            _mint(toList[i], quantityToAdd + numBonusMints);
            totalQuantity += quantityToAdd;
            totalBonusMints += numBonusMints;
            unchecked {
              ++i;
            }
          }
          validateAndCreditMint(invite, auth, totalQuantity, totalBonusMints, curSupply, affiliate, signature);
        }
        function mintTo(
          Auth calldata auth,
          uint256 quantity,
          address to,
          address affiliate,
          bytes calldata signature
        ) public payable {
          AdvancedInvite storage invite = invites[auth.key];
          uint256 packedDiscount = packedBonusDiscounts[auth.key];
          if (invite.unitSize > 1) {
            quantity = quantity * invite.unitSize;
          }
          uint256 curSupply = _totalMinted();
          uint256 numBonusMints = ArchetypeLogicErc721a.bonusMintsAwarded(quantity, packedDiscount);
          _mint(to, quantity + numBonusMints);
          validateAndCreditMint(invite, auth, quantity, numBonusMints, curSupply, affiliate, signature);
        }
        function validateAndCreditMint(
          AdvancedInvite storage invite,
          Auth calldata auth,
          uint256 quantity,
          uint256 numBonusMints,
          uint256 curSupply,
          address affiliate,
          bytes calldata signature
        ) internal {
          uint256 totalQuantity = quantity + numBonusMints;
          ValidationArgs memory args;
          {
            args = ValidationArgs({
              owner: owner(),
              affiliate: affiliate,
              quantity: totalQuantity,
              curSupply: curSupply,
              listSupply: _listSupply[auth.key]
            });
          }
          uint128 cost = uint128(
            ArchetypeLogicErc721a.computePrice(
              invite,
              config.affiliateDiscount,
              quantity,
              args.listSupply,
              args.affiliate != address(0)
            )
          );
          ArchetypeLogicErc721a.validateMint(invite, config, auth, _minted, signature, args, cost);
          if (invite.limit < invite.maxSupply) {
            _minted[_msgSender()][auth.key] += totalQuantity;
          }
          if (invite.maxSupply < UINT32_MAX) {
            _listSupply[auth.key] += totalQuantity;
          }
          ArchetypeLogicErc721a.updateBalances(
            invite.tokenAddress,
            config,
            _ownerBalance,
            _affiliateBalance,
            affiliate,
            quantity,
            cost
          );
          if (msg.value > cost) {
            _refund(_msgSender(), msg.value - cost);
          }
        }
        function burnToMint(Auth calldata auth, uint256[] calldata tokenIds) external payable {
          BurnInvite storage burnInvite = burnInvites[auth.key];
          uint256 curSupply = _totalMinted();
          uint128 cost = burnInvite.price;
          ArchetypeLogicErc721a.validateBurnToMint(burnInvite, config, auth, tokenIds, curSupply, _minted, cost);
          address msgSender = _msgSender();
          for (uint256 i; i < tokenIds.length; ) {
            address burnAddress = burnInvite.burnAddress != address(0)
              ? burnInvite.burnAddress
              : address(0x000000000000000000000000000000000000dEaD);
            burnInvite.burnErc721.transferFrom(msgSender, burnAddress, tokenIds[i]);
            unchecked {
              ++i;
            }
          }
          uint256 quantity = burnInvite.reversed
            ? tokenIds.length * burnInvite.ratio
            : tokenIds.length / burnInvite.ratio;
          _mint(msgSender, quantity);
          if (burnInvite.limit < config.maxSupply) {
            _minted[msgSender][keccak256(abi.encodePacked("burn", auth.key))] += quantity;
          }
          ArchetypeLogicErc721a.updateBalances(
            burnInvite.tokenAddress,
            config,
            _ownerBalance,
            _affiliateBalance,
            address(0), // burn to mint does not support affiliates
            quantity,
            cost
          );
          if (msg.value > cost) {
            _refund(_msgSender(), msg.value - cost);
          }
        }
        function tokenURI(uint256 tokenId) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) {
          if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
          return
            bytes(config.baseUri).length != 0
              ? string(abi.encodePacked(config.baseUri, LibString.toString(tokenId)))
              : "";
        }
        function withdraw() external {
          address[] memory tokens = new address[](1);
          tokens[0] = address(0);
          withdrawTokens(tokens);
        }
        function withdrawTokens(address[] memory tokens) public {
          ArchetypeLogicErc721a.withdrawTokens(payoutConfig, _ownerBalance, owner(), tokens);
        }
        function withdrawAffiliate() external {
          address[] memory tokens = new address[](1);
          tokens[0] = address(0);
          withdrawTokensAffiliate(tokens);
        }
        function withdrawTokensAffiliate(address[] memory tokens) public {
          ArchetypeLogicErc721a.withdrawTokensAffiliate(_affiliateBalance, tokens);
        }
        function ownerBalance() external view returns (uint128) {
          return _ownerBalance[address(0)];
        }
        function ownerBalanceToken(address token) external view returns (uint128) {
          return _ownerBalance[token];
        }
        function affiliateBalance(address affiliate) external view returns (uint128) {
          return _affiliateBalance[affiliate][address(0)];
        }
        function affiliateBalanceToken(address affiliate, address token) external view returns (uint128) {
          return _affiliateBalance[affiliate][token];
        }
        function minted(address minter, bytes32 key) external view returns (uint256) {
          return _minted[minter][key];
        }
        function listSupply(bytes32 key) external view returns (uint256) {
          return _listSupply[key];
        }
        function platform() external pure returns (address) {
          return PLATFORM;
        }
        function computePrice(
          bytes32 key,
          uint256 quantity,
          bool affiliateUsed
        ) external view returns (uint256) {
          AdvancedInvite storage i = invites[key];
          uint256 listSupply_ = _listSupply[key];
          return ArchetypeLogicErc721a.computePrice(i, config.affiliateDiscount, quantity, listSupply_, affiliateUsed);
        }
        //
        // OWNER ONLY
        //
        function setBaseURI(string memory baseUri) external _onlyOwner {
          if (options.uriLocked) {
            revert LockedForever();
          }
          config.baseUri = baseUri;
        }
        /// @notice the password is "forever"
        function lockURI(string memory password) external _onlyOwner {
          if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
            revert WrongPassword();
          }
          options.uriLocked = true;
        }
        /// @notice the password is "forever"
        // max supply cannot subceed total supply. Be careful changing.
        function setMaxSupply(uint32 maxSupply, string memory password) external _onlyOwner {
          if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
            revert WrongPassword();
          }
          if (options.maxSupplyLocked) {
            revert LockedForever();
          }
          if (maxSupply < _totalMinted()) {
            revert MaxSupplyExceeded();
          }
          config.maxSupply = maxSupply;
        }
        /// @notice the password is "forever"
        function lockMaxSupply(string memory password) external _onlyOwner {
          if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
            revert WrongPassword();
          }
          options.maxSupplyLocked = true;
        }
        function setAffiliateFee(uint16 affiliateFee) external _onlyOwner {
          if (options.affiliateFeeLocked) {
            revert LockedForever();
          }
          if (affiliateFee > MAXBPS) {
            revert InvalidConfig();
          }
          config.affiliateFee = affiliateFee;
        }
        function setAffiliateDiscount(uint16 affiliateDiscount) external _onlyOwner {
          if (options.affiliateFeeLocked) {
            revert LockedForever();
          }
          if (affiliateDiscount > MAXBPS) {
            revert InvalidConfig();
          }
          config.affiliateDiscount = affiliateDiscount;
        }
        /// @notice the password is "forever"
        function lockAffiliateFee(string memory password) external _onlyOwner {
          if (keccak256(abi.encodePacked(password)) != keccak256(abi.encodePacked("forever"))) {
            revert WrongPassword();
          }
          options.affiliateFeeLocked = true;
        }
        function setOwnerAltPayout(address ownerAltPayout) external _onlyOwner {
          if (options.ownerAltPayoutLocked) {
            revert LockedForever();
          }
          payoutConfig.ownerAltPayout = ownerAltPayout;
        }
        function lockOwnerAltPayout() external _onlyOwner {
          options.ownerAltPayoutLocked = true;
        }
        function setMaxBatchSize(uint32 maxBatchSize) external _onlyOwner {
          config.maxBatchSize = maxBatchSize;
        }
        function setBonusDiscounts(bytes32 _key, BonusDiscount[] calldata _bonusDiscounts) public onlyOwner {
          if(_bonusDiscounts.length > 8) {
            revert InvalidConfig();
          }
          uint256 packed;
          for (uint8 i = 0; i < _bonusDiscounts.length; i++) {
              if (i > 0 && _bonusDiscounts[i].numMints >= _bonusDiscounts[i - 1].numMints) {
                  revert InvalidConfig();
              }
              uint32 discount = (uint32(_bonusDiscounts[i].numMints) << 16) | uint32(_bonusDiscounts[i].numBonusMints);
              packed |= uint256(discount) << (32 * i);
          }
          packedBonusDiscounts[_key] = packed;
        }
        function setBonusInvite(
          bytes32 _key,
          bytes32 _cid,
          AdvancedInvite calldata _advancedInvite,
          BonusDiscount[] calldata _bonusDiscount
        ) external _onlyOwner {
          setBonusDiscounts(_key, _bonusDiscount);
          setAdvancedInvite(_key, _cid, _advancedInvite);
        }
        function setInvite(
          bytes32 _key,
          bytes32 _cid,
          Invite calldata _invite
        ) external _onlyOwner {
          setAdvancedInvite(_key, _cid, AdvancedInvite({
            price: _invite.price,
            reservePrice: _invite.price,
            delta: 0,
            start: _invite.start,
            end: _invite.end,
            limit: _invite.limit,
            maxSupply: _invite.maxSupply,
            interval: 0,
            unitSize: _invite.unitSize,
            tokenAddress: _invite.tokenAddress,
            isBlacklist: _invite.isBlacklist
          }));
        }
        function setAdvancedInvite(
          bytes32 _key,
          bytes32 _cid,
          AdvancedInvite memory _AdvancedInvite
        ) public _onlyOwner {
          // approve token for withdrawals if erc20 list
          if (_AdvancedInvite.tokenAddress != address(0)) {
            bool success = IERC20(_AdvancedInvite.tokenAddress).approve(PAYOUTS, 2**256 - 1);
            if (!success) {
              revert NotApprovedToTransfer();
            }
          }
          if (_AdvancedInvite.start < block.timestamp) {
            _AdvancedInvite.start = uint32(block.timestamp);
          }
          invites[_key] = _AdvancedInvite;
          emit Invited(_key, _cid);
        }
        function setBurnInvite(
          bytes32 _key,
          bytes32 _cid,
          BurnInvite memory _burnInvite
        ) external _onlyOwner {
          if (_burnInvite.start < block.timestamp) {
            _burnInvite.start = uint32(block.timestamp);
          }
          burnInvites[_key] = _burnInvite;
          emit BurnInvited(_key, _cid);
        }
        //
        // INTERNAL
        //
        function _startTokenId() internal view virtual override returns (uint256) {
          return 1;
        }
        function _msgSender() internal view returns (address) {
          return msg.sender == BATCH ? tx.origin : msg.sender;
        }
        modifier _onlyPlatform() {
          if (_msgSender() != PLATFORM) {
            revert NotPlatform();
          }
          _;
        }
        modifier _onlyOwner() {
          if (_msgSender() != owner()) {
            revert NotOwner();
          }
          _;
        }
        function _refund(address to, uint256 refund) internal {
          (bool success, ) = payable(to).call{ value: refund }("");
          if (!success) {
            revert TransferFailed();
          }
        }
        //ERC2981 ROYALTY
        function supportsInterface(bytes4 interfaceId)
          public
          view
          virtual
          override(IERC721AUpgradeable, ERC721AUpgradeable, ERC2981Upgradeable)
          returns (bool)
        {
          // Supports the following `interfaceId`s:
          // - IERC165: 0x01ffc9a7
          // - IERC721: 0x80ac58cd
          // - IERC721Metadata: 0x5b5e139f
          // - IERC2981: 0x2a55205a
          return
            ERC721AUpgradeable.supportsInterface(interfaceId) ||
            ERC2981Upgradeable.supportsInterface(interfaceId);
        }
        function setDefaultRoyalty(address receiver, uint16 feeNumerator) public _onlyOwner {
          config.defaultRoyalty = feeNumerator;
          _setDefaultRoyalty(receiver, feeNumerator);
        }
      }
      // SPDX-License-Identifier: MIT
      // ArchetypeLogic v0.8.0
      //
      //        d8888                 888               888
      //       d88888                 888               888
      //      d88P888                 888               888
      //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
      //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
      //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
      //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
      // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
      //                                                            888 888
      //                                                       Y8b d88P 888
      //                                                        "Y88P"  888
      pragma solidity ^0.8.20;
      import "../ArchetypePayouts.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "solady/src/utils/MerkleProofLib.sol";
      import "solady/src/utils/ECDSA.sol";
      error InvalidConfig();
      error MintNotYetStarted();
      error MintEnded();
      error WalletUnauthorizedToMint();
      error InsufficientEthSent();
      error ExcessiveEthSent();
      error Erc20BalanceTooLow();
      error MaxSupplyExceeded();
      error ListMaxSupplyExceeded();
      error NumberOfMintsExceeded();
      error MintingPaused();
      error InvalidReferral();
      error InvalidSignature();
      error MaxBatchSizeExceeded();
      error BurnToMintDisabled();
      error NotTokenOwner();
      error NotPlatform();
      error NotOwner();
      error NotShareholder();
      error NotApprovedToTransfer();
      error InvalidAmountOfTokens();
      error WrongPassword();
      error LockedForever();
      error Blacklisted();
      //
      // STRUCTS
      //
      struct Auth {
        bytes32 key;
        bytes32[] proof;
      }
      struct BonusDiscount {
        uint16 numMints;
        uint16 numBonusMints;
      }
      struct Config {
        string baseUri;
        address affiliateSigner;
        uint32 maxSupply;
        uint32 maxBatchSize;
        uint16 affiliateFee; //BPS
        uint16 affiliateDiscount; //BPS
        uint16 defaultRoyalty; //BPS
      }
      // allocation splits for withdrawn owner funds, must sum to 100%
      struct PayoutConfig {
        uint16 ownerBps;
        uint16 platformBps;
        uint16 partnerBps;
        uint16 superAffiliateBps;
        address partner;
        address superAffiliate;
        address ownerAltPayout;
      }
      struct Options {
        bool uriLocked;
        bool maxSupplyLocked;
        bool affiliateFeeLocked;
        bool ownerAltPayoutLocked;
      }
      struct AdvancedInvite {
        uint128 price;
        uint128 reservePrice;
        uint128 delta;
        uint32 start;
        uint32 end;
        uint32 limit;
        uint32 maxSupply;
        uint32 interval;
        uint32 unitSize; // mint 1 get x
        address tokenAddress;
        bool isBlacklist;
      }
      struct Invite {
        uint128 price;
        uint32 start;
        uint32 end;
        uint32 limit;
        uint32 maxSupply;
        uint32 unitSize; // mint 1 get x
        address tokenAddress;
        bool isBlacklist;
      }
      struct BurnInvite {
        IERC721 burnErc721;
        address burnAddress;
        address tokenAddress;
        uint128 price; // flat price - does not support discounts
        bool reversed; // side of the ratio (false=burn {ratio} get 1, true=burn 1 get {ratio})
        uint16 ratio;
        uint32 start;
        uint32 end;
        uint64 limit;
      }
      struct ValidationArgs {
        address owner;
        address affiliate;
        uint256 quantity;
        uint256 curSupply;
        uint256 listSupply;
      }
      // UPDATE CONSTANTS BEFORE DEPLOY
      address constant PLATFORM = 0x8952caF7E5bf1fe63ebe94148ca802F3eF127C98;
      address constant BATCH = 0x6Bc558A6DC48dEfa0e7022713c23D65Ab26e4Fa7;
      address constant PAYOUTS = 0xaAfdfA4a935d8511bF285af11A0544ce7e4a1199;
      uint16 constant MAXBPS = 5000; // max fee or discount is 50%
      uint32 constant UINT32_MAX = 2**32 - 1;
      library ArchetypeLogicErc721a {
        //
        // EVENTS
        //
        event Invited(bytes32 indexed key, bytes32 indexed cid);
        event BurnInvited(bytes32 indexed key, bytes32 indexed cid);
        event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
        event Withdrawal(address indexed src, address token, uint128 wad);
        // calculate price based on affiliate usage and mint discounts
        function computePrice(
          AdvancedInvite storage invite,
          uint16 affiliateDiscount,
          uint256 numTokens,
          uint256 listSupply,
          bool affiliateUsed
        ) public view returns (uint256) {
          uint256 price = invite.price;
          uint256 cost;
          if (invite.interval > 0 && invite.delta > 0) {
            // Apply dutch pricing
            uint256 diff = (((block.timestamp - invite.start) / invite.interval) * invite.delta);
            if (price > invite.reservePrice) {
              if (diff > price - invite.reservePrice) {
                price = invite.reservePrice;
              } else {
                price = price - diff;
              }
            } else if (price < invite.reservePrice) {
              if (diff > invite.reservePrice - price) {
                price = invite.reservePrice;
              } else {
                price = price + diff;
              }
            }
            cost = price * numTokens;
          } else if (invite.interval == 0 && invite.delta > 0) {
            // Apply linear curve
            uint256 lastPrice = price + invite.delta * listSupply;
            cost = lastPrice * numTokens + (invite.delta * numTokens * (numTokens - 1)) / 2;
          } else {
            cost = price * numTokens;
          }
          if (affiliateUsed) {
            cost = cost - ((cost * affiliateDiscount) / 10000);
          }
          return cost;
        }
        function bonusMintsAwarded(uint256 numNfts, uint256 packedDiscount) internal pure returns (uint256) {
          for (uint8 i = 0; i < 8; i++) {
              uint32 discount = uint32((packedDiscount >> (32 * i)) & 0xFFFFFFFF);
              uint16 tierNumMints = uint16(discount >> 16);
              uint16 tierBonusMints = uint16(discount);
              if (tierNumMints == 0) {
                  break; // End of valid discounts
              }
              if (numNfts >= tierNumMints) {
                  return (numNfts / tierNumMints) * tierBonusMints;
              }
          }
          return 0;
        }
        function validateMint(
          AdvancedInvite storage i,
          Config storage config,
          Auth calldata auth,
          mapping(address => mapping(bytes32 => uint256)) storage minted,
          bytes calldata signature,
          ValidationArgs memory args,
          uint128 cost
        ) public view {
          address msgSender = _msgSender();
          if (args.affiliate != address(0)) {
            if (
              args.affiliate == PLATFORM || args.affiliate == args.owner || args.affiliate == msgSender
            ) {
              revert InvalidReferral();
            }
            validateAffiliate(args.affiliate, signature, config.affiliateSigner);
          }
          if (i.limit == 0) {
            revert MintingPaused();
          }
          if (!i.isBlacklist) {
            if (!verify(auth, i.tokenAddress, msgSender)) {
              revert WalletUnauthorizedToMint();
            }
          } else {
            if (verify(auth, i.tokenAddress, msgSender)) {
              revert Blacklisted();
            }
          }
          if (block.timestamp < i.start) {
            revert MintNotYetStarted();
          }
          if (i.end > i.start && block.timestamp > i.end) {
            revert MintEnded();
          }
          if (i.limit < i.maxSupply) {
            uint256 totalAfterMint = minted[msgSender][auth.key] + args.quantity;
            if (totalAfterMint > i.limit) {
              revert NumberOfMintsExceeded();
            }
          }
          if (i.maxSupply < config.maxSupply) {
            uint256 totalAfterMint = args.listSupply + args.quantity;
            if (totalAfterMint > i.maxSupply) {
              revert ListMaxSupplyExceeded();
            }
          }
          if (args.quantity > config.maxBatchSize) {
            revert MaxBatchSizeExceeded();
          }
          if ((args.curSupply + args.quantity) > config.maxSupply) {
            revert MaxSupplyExceeded();
          }
          if (i.tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(i.tokenAddress);
            if (erc20Token.allowance(msgSender, address(this)) < cost) {
              revert NotApprovedToTransfer();
            }
            if (erc20Token.balanceOf(msgSender) < cost) {
              revert Erc20BalanceTooLow();
            }
            if (msg.value != 0) {
              revert ExcessiveEthSent();
            }
          } else {
            if (msg.value < cost) {
              revert InsufficientEthSent();
            }
          }
        }
        function validateBurnToMint(
          BurnInvite storage burnInvite,
          Config storage config,
          Auth calldata auth,
          uint256[] calldata tokenIds,
          uint256 curSupply,
          mapping(address => mapping(bytes32 => uint256)) storage minted,
          uint128 cost
        ) public view {
          if (burnInvite.limit == 0) {
            revert MintingPaused();
          }
          if (block.timestamp < burnInvite.start) {
            revert MintNotYetStarted();
          }
          if (burnInvite.end > burnInvite.start && block.timestamp > burnInvite.end) {
            revert MintEnded();
          }
          // check if msgSender owns tokens and has correct approvals
          address msgSender = _msgSender();
          for (uint256 i; i < tokenIds.length; ) {
            if (burnInvite.burnErc721.ownerOf(tokenIds[i]) != msgSender) {
              revert NotTokenOwner();
            }
            unchecked {
              ++i;
            }
          }
          if (!verify(auth, burnInvite.tokenAddress, msgSender)) {
            revert WalletUnauthorizedToMint();
          }
          if (!burnInvite.burnErc721.isApprovedForAll(msgSender, address(this))) {
            revert NotApprovedToTransfer();
          }
          uint256 quantity;
          if (burnInvite.reversed) {
            quantity = tokenIds.length * burnInvite.ratio;
          } else {
            if (tokenIds.length % burnInvite.ratio != 0) {
              revert InvalidAmountOfTokens();
            }
            quantity = tokenIds.length / burnInvite.ratio;
          }
          if (quantity > config.maxBatchSize) {
            revert MaxBatchSizeExceeded();
          }
          if (burnInvite.limit < config.maxSupply) {
            uint256 totalAfterMint = minted[msgSender][keccak256(abi.encodePacked("burn", auth.key))] +
              quantity;
            if (totalAfterMint > burnInvite.limit) {
              revert NumberOfMintsExceeded();
            }
          }
          if ((curSupply + quantity) > config.maxSupply) {
            revert MaxSupplyExceeded();
          }
          if (burnInvite.tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(burnInvite.tokenAddress);
            if (erc20Token.allowance(msgSender, address(this)) < cost) {
              revert NotApprovedToTransfer();
            }
            if (erc20Token.balanceOf(msgSender) < cost) {
              revert Erc20BalanceTooLow();
            }
            if (msg.value != 0) {
              revert ExcessiveEthSent();
            }
          } else {
            if (msg.value < cost) {
              revert InsufficientEthSent();
            }
          }
        }
        function updateBalances(
          address tokenAddress,
          Config storage config,
          mapping(address => uint128) storage _ownerBalance,
          mapping(address => mapping(address => uint128)) storage _affiliateBalance,
          address affiliate,
          uint256 quantity,
          uint128 value
        ) public {
          uint128 affiliateWad;
          if (affiliate != address(0)) {
            affiliateWad = (value * config.affiliateFee) / 10000;
            _affiliateBalance[affiliate][tokenAddress] += affiliateWad;
            emit Referral(affiliate, tokenAddress, affiliateWad, quantity);
          }
          uint128 balance = _ownerBalance[tokenAddress];
          uint128 ownerWad = value - affiliateWad;
          _ownerBalance[tokenAddress] = balance + ownerWad;
          if (tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(tokenAddress);
            bool success = erc20Token.transferFrom(_msgSender(), address(this), value);
            if (!success) {
              revert TransferFailed();
            }
          }
        }
        function withdrawTokensAffiliate(
          mapping(address => mapping(address => uint128)) storage _affiliateBalance,
          address[] calldata tokens
        ) public {
          address msgSender = _msgSender();
          for (uint256 i; i < tokens.length; i++) {
            address tokenAddress = tokens[i];
            uint128 wad = _affiliateBalance[msgSender][tokenAddress];
            _affiliateBalance[msgSender][tokenAddress] = 0;
            if (wad == 0) {
              revert BalanceEmpty();
            }
            if (tokenAddress == address(0)) {
              bool success = false;
              (success, ) = msgSender.call{ value: wad }("");
              if (!success) {
                revert TransferFailed();
              }
            } else {
              IERC20 erc20Token = IERC20(tokenAddress);
              bool success = erc20Token.transfer(msgSender, wad);
              if (!success) {
                revert TransferFailed();
              }
            }
            emit Withdrawal(msgSender, tokenAddress, wad);
          }
        }
        function withdrawTokens(
          PayoutConfig storage payoutConfig,
          mapping(address => uint128) storage _ownerBalance,
          address owner,
          address[] calldata tokens
        ) public {
          address msgSender = _msgSender();
          for (uint256 i; i < tokens.length; i++) {
            address tokenAddress = tokens[i];
            uint128 wad;
            if (
              msgSender == owner ||
              msgSender == PLATFORM ||
              msgSender == payoutConfig.partner ||
              msgSender == payoutConfig.superAffiliate ||
              msgSender == payoutConfig.ownerAltPayout
            ) {
              wad = _ownerBalance[tokenAddress];
              _ownerBalance[tokenAddress] = 0;
            } else {
              revert NotShareholder();
            }
            if (wad == 0) {
              revert BalanceEmpty();
            }
            if (payoutConfig.ownerAltPayout == address(0)) {
              address[] memory recipients = new address[](4);
              recipients[0] = owner;
              recipients[1] = PLATFORM;
              recipients[2] = payoutConfig.partner;
              recipients[3] = payoutConfig.superAffiliate;
              uint16[] memory splits = new uint16[](4);
              splits[0] = payoutConfig.ownerBps;
              splits[1] = payoutConfig.platformBps;
              splits[2] = payoutConfig.partnerBps;
              splits[3] = payoutConfig.superAffiliateBps;
              if (tokenAddress == address(0)) {
                ArchetypePayouts(PAYOUTS).updateBalances{ value: wad }(
                  wad,
                  tokenAddress,
                  recipients,
                  splits
                );
              } else {
                ArchetypePayouts(PAYOUTS).updateBalances(wad, tokenAddress, recipients, splits);
              }
            } else {
              uint256 ownerShare = (uint256(wad) * payoutConfig.ownerBps) / 10000;
              uint256 remainingShare = wad - ownerShare;
              if (tokenAddress == address(0)) {
                (bool success, ) = payable(payoutConfig.ownerAltPayout).call{ value: ownerShare }("");
                if (!success) revert TransferFailed();
              } else {
                IERC20(tokenAddress).transfer(payoutConfig.ownerAltPayout, ownerShare);
              }
              address[] memory recipients = new address[](3);
              recipients[0] = PLATFORM;
              recipients[1] = payoutConfig.partner;
              recipients[2] = payoutConfig.superAffiliate;
              uint16[] memory splits = new uint16[](3);
              uint16 remainingBps = 10000 - payoutConfig.ownerBps;
              splits[1] = uint16((uint256(payoutConfig.partnerBps) * 10000) / remainingBps);
              splits[2] = uint16((uint256(payoutConfig.superAffiliateBps) * 10000) / remainingBps);
              splits[0] = 10000 - splits[1] - splits[2];
              if (tokenAddress == address(0)) {
                ArchetypePayouts(PAYOUTS).updateBalances{ value: remainingShare }(
                  remainingShare,
                  tokenAddress,
                  recipients,
                  splits
                );
              } else {
                ArchetypePayouts(PAYOUTS).updateBalances(
                  remainingShare,
                  tokenAddress,
                  recipients,
                  splits
                );
              }
            }
            emit Withdrawal(msgSender, tokenAddress, wad);
          }
        }
        function validateAffiliate(
          address affiliate,
          bytes calldata signature,
          address affiliateSigner
        ) public view {
          bytes32 signedMessagehash = ECDSA.toEthSignedMessageHash(
            keccak256(abi.encodePacked(affiliate))
          );
          address signer = ECDSA.recover(signedMessagehash, signature);
          if (signer != affiliateSigner) {
            revert InvalidSignature();
          }
        }
        function verify(
          Auth calldata auth,
          address tokenAddress,
          address account
        ) public pure returns (bool) {
          // keys 0-255 and tokenAddress are public
          if (uint256(auth.key) <= 0xff || auth.key == keccak256(abi.encodePacked(tokenAddress))) {
            return true;
          }
          return MerkleProofLib.verify(auth.proof, auth.key, keccak256(abi.encodePacked(account)));
        }
        function _msgSender() internal view returns (address) {
          return msg.sender == BATCH ? tx.origin : msg.sender;
        }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
      import 'erc721a-upgradeable/contracts/ERC721A__Initializable.sol';
      import 'erc721a-upgradeable/contracts/ERC721AUpgradeable.sol';
      pragma solidity ^0.8.4;
      /**
       * @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 ERC721A__OwnableUpgradeable is ERC721A__Initializable, ERC721AUpgradeable {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function __Ownable_init() internal onlyInitializingERC721A {
              __Ownable_init_unchained();
          }
          function __Ownable_init_unchained() internal onlyInitializingERC721A {
              _transferOwnership(_msgSenderERC721A());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _isOwner();
              _;
          }
          function _isOwner() internal view {
              require(owner() == _msgSenderERC721A(), "Ownable: caller is not the owner");
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
          /**
           * @dev This empty reserved space is put in place to allow future versions to add new
           * variables without shifting down storage in the inheritance chain.
           * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
           */
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev This is a base contract to aid in writing upgradeable diamond facet 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.
       *
       * 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.
       */
      import {ERC721A__InitializableStorage} from './ERC721A__InitializableStorage.sol';
      abstract contract ERC721A__Initializable {
          using ERC721A__InitializableStorage for ERC721A__InitializableStorage.Layout;
          /**
           * @dev Modifier to protect an initializer function from being invoked twice.
           */
          modifier initializerERC721A() {
              // If the contract is initializing we ignore whether _initialized is set in order to support multiple
              // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
              // contract may have been reentered.
              require(
                  ERC721A__InitializableStorage.layout()._initializing
                      ? _isConstructor()
                      : !ERC721A__InitializableStorage.layout()._initialized,
                  'ERC721A__Initializable: contract is already initialized'
              );
              bool isTopLevelCall = !ERC721A__InitializableStorage.layout()._initializing;
              if (isTopLevelCall) {
                  ERC721A__InitializableStorage.layout()._initializing = true;
                  ERC721A__InitializableStorage.layout()._initialized = true;
              }
              _;
              if (isTopLevelCall) {
                  ERC721A__InitializableStorage.layout()._initializing = false;
              }
          }
          /**
           * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
           * {initializer} modifier, directly or indirectly.
           */
          modifier onlyInitializingERC721A() {
              require(
                  ERC721A__InitializableStorage.layout()._initializing,
                  'ERC721A__Initializable: contract is not initializing'
              );
              _;
          }
          /// @dev Returns true if and only if the function is running in the constructor
          function _isConstructor() private view returns (bool) {
              // extcodesize checks the size of the code stored in an address, and
              // address returns the current address. Since the code is still not
              // deployed when running a constructor, any checks on its code size will
              // yield zero, making it an effective way to detect if a contract is
              // under construction or not.
              address self = address(this);
              uint256 cs;
              assembly {
                  cs := extcodesize(self)
              }
              return cs == 0;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev This is a base storage for the  initialization function for upgradeable diamond facet contracts
       **/
      library ERC721A__InitializableStorage {
          struct Layout {
              /*
               * Indicates that the contract has been initialized.
               */
              bool _initialized;
              /*
               * Indicates that the contract is in the process of being initialized.
               */
              bool _initializing;
          }
          bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.initializable.facet');
          function layout() internal pure returns (Layout storage l) {
              bytes32 slot = STORAGE_SLOT;
              assembly {
                  l.slot := slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      library ERC721AStorage {
          // Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
          struct TokenApprovalRef {
              address value;
          }
          struct Layout {
              // =============================================================
              //                            STORAGE
              // =============================================================
              // The next token ID to be minted.
              uint256 _currentIndex;
              // The number of tokens burned.
              uint256 _burnCounter;
              // Token name
              string _name;
              // Token symbol
              string _symbol;
              // Mapping from token ID to ownership details
              // An empty struct value does not necessarily mean the token is unowned.
              // See {_packedOwnershipOf} implementation for details.
              //
              // Bits Layout:
              // - [0..159]   `addr`
              // - [160..223] `startTimestamp`
              // - [224]      `burned`
              // - [225]      `nextInitialized`
              // - [232..255] `extraData`
              mapping(uint256 => uint256) _packedOwnerships;
              // Mapping owner address to address data.
              //
              // Bits Layout:
              // - [0..63]    `balance`
              // - [64..127]  `numberMinted`
              // - [128..191] `numberBurned`
              // - [192..255] `aux`
              mapping(address => uint256) _packedAddressData;
              // Mapping from token ID to approved address.
              mapping(uint256 => ERC721AStorage.TokenApprovalRef) _tokenApprovals;
              // Mapping from owner to operator approvals
              mapping(address => mapping(address => bool)) _operatorApprovals;
              // The amount of tokens minted above `_sequentialUpTo()`.
              // We call these spot mints (i.e. non-sequential mints).
              uint256 _spotMinted;
          }
          bytes32 internal constant STORAGE_SLOT = keccak256('ERC721A.contracts.storage.ERC721A');
          function layout() internal pure returns (Layout storage l) {
              bytes32 slot = STORAGE_SLOT;
              assembly {
                  l.slot := slot
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // ERC721A Contracts v4.3.0
      // Creator: Chiru Labs
      pragma solidity ^0.8.4;
      import './IERC721AUpgradeable.sol';
      import {ERC721AStorage} from './ERC721AStorage.sol';
      import './ERC721A__Initializable.sol';
      /**
       * @dev Interface of ERC721 token receiver.
       */
      interface ERC721A__IERC721ReceiverUpgradeable {
          function onERC721Received(
              address operator,
              address from,
              uint256 tokenId,
              bytes calldata data
          ) external returns (bytes4);
      }
      /**
       * @title ERC721A
       *
       * @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
       * Non-Fungible Token Standard, including the Metadata extension.
       * Optimized for lower gas during batch mints.
       *
       * Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
       * starting from `_startTokenId()`.
       *
       * The `_sequentialUpTo()` function can be overriden to enable spot mints
       * (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
       *
       * Assumptions:
       *
       * - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
       * - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
       */
      contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable {
          using ERC721AStorage for ERC721AStorage.Layout;
          // =============================================================
          //                           CONSTANTS
          // =============================================================
          // Mask of an entry in packed address data.
          uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
          // The bit position of `numberMinted` in packed address data.
          uint256 private constant _BITPOS_NUMBER_MINTED = 64;
          // The bit position of `numberBurned` in packed address data.
          uint256 private constant _BITPOS_NUMBER_BURNED = 128;
          // The bit position of `aux` in packed address data.
          uint256 private constant _BITPOS_AUX = 192;
          // Mask of all 256 bits in packed address data except the 64 bits for `aux`.
          uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
          // The bit position of `startTimestamp` in packed ownership.
          uint256 private constant _BITPOS_START_TIMESTAMP = 160;
          // The bit mask of the `burned` bit in packed ownership.
          uint256 private constant _BITMASK_BURNED = 1 << 224;
          // The bit position of the `nextInitialized` bit in packed ownership.
          uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
          // The bit mask of the `nextInitialized` bit in packed ownership.
          uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
          // The bit position of `extraData` in packed ownership.
          uint256 private constant _BITPOS_EXTRA_DATA = 232;
          // Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
          uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
          // The mask of the lower 160 bits for addresses.
          uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
          // The maximum `quantity` that can be minted with {_mintERC2309}.
          // This limit is to prevent overflows on the address data entries.
          // For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
          // is required to cause an overflow, which is unrealistic.
          uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
          // The `Transfer` event signature is given by:
          // `keccak256(bytes("Transfer(address,address,uint256)"))`.
          bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
              0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
          // =============================================================
          //                          CONSTRUCTOR
          // =============================================================
          function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
              __ERC721A_init_unchained(name_, symbol_);
          }
          function __ERC721A_init_unchained(string memory name_, string memory symbol_) internal onlyInitializingERC721A {
              ERC721AStorage.layout()._name = name_;
              ERC721AStorage.layout()._symbol = symbol_;
              ERC721AStorage.layout()._currentIndex = _startTokenId();
              if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
          }
          // =============================================================
          //                   TOKEN COUNTING OPERATIONS
          // =============================================================
          /**
           * @dev Returns the starting token ID for sequential mints.
           *
           * Override this function to change the starting token ID for sequential mints.
           *
           * Note: The value returned must never change after any tokens have been minted.
           */
          function _startTokenId() internal view virtual returns (uint256) {
              return 0;
          }
          /**
           * @dev Returns the maximum token ID (inclusive) for sequential mints.
           *
           * Override this function to return a value less than 2**256 - 1,
           * but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
           *
           * Note: The value returned must never change after any tokens have been minted.
           */
          function _sequentialUpTo() internal view virtual returns (uint256) {
              return type(uint256).max;
          }
          /**
           * @dev Returns the next token ID to be minted.
           */
          function _nextTokenId() internal view virtual returns (uint256) {
              return ERC721AStorage.layout()._currentIndex;
          }
          /**
           * @dev Returns the total number of tokens in existence.
           * Burned tokens will reduce the count.
           * To get the total number of tokens minted, please see {_totalMinted}.
           */
          function totalSupply() public view virtual override returns (uint256 result) {
              // Counter underflow is impossible as `_burnCounter` cannot be incremented
              // more than `_currentIndex + _spotMinted - _startTokenId()` times.
              unchecked {
                  // With spot minting, the intermediate `result` can be temporarily negative,
                  // and the computation must be unchecked.
                  result = ERC721AStorage.layout()._currentIndex - ERC721AStorage.layout()._burnCounter - _startTokenId();
                  if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
              }
          }
          /**
           * @dev Returns the total amount of tokens minted in the contract.
           */
          function _totalMinted() internal view virtual returns (uint256 result) {
              // Counter underflow is impossible as `_currentIndex` does not decrement,
              // and it is initialized to `_startTokenId()`.
              unchecked {
                  result = ERC721AStorage.layout()._currentIndex - _startTokenId();
                  if (_sequentialUpTo() != type(uint256).max) result += ERC721AStorage.layout()._spotMinted;
              }
          }
          /**
           * @dev Returns the total number of tokens burned.
           */
          function _totalBurned() internal view virtual returns (uint256) {
              return ERC721AStorage.layout()._burnCounter;
          }
          /**
           * @dev Returns the total number of tokens that are spot-minted.
           */
          function _totalSpotMinted() internal view virtual returns (uint256) {
              return ERC721AStorage.layout()._spotMinted;
          }
          // =============================================================
          //                    ADDRESS DATA OPERATIONS
          // =============================================================
          /**
           * @dev Returns the number of tokens in `owner`'s account.
           */
          function balanceOf(address owner) public view virtual override returns (uint256) {
              if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
              return ERC721AStorage.layout()._packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
          }
          /**
           * Returns the number of tokens minted by `owner`.
           */
          function _numberMinted(address owner) internal view returns (uint256) {
              return
                  (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
          }
          /**
           * Returns the number of tokens burned by or on behalf of `owner`.
           */
          function _numberBurned(address owner) internal view returns (uint256) {
              return
                  (ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
          }
          /**
           * Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
           */
          function _getAux(address owner) internal view returns (uint64) {
              return uint64(ERC721AStorage.layout()._packedAddressData[owner] >> _BITPOS_AUX);
          }
          /**
           * Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
           * If there are multiple variables, please pack them into a uint64.
           */
          function _setAux(address owner, uint64 aux) internal virtual {
              uint256 packed = ERC721AStorage.layout()._packedAddressData[owner];
              uint256 auxCasted;
              // Cast `aux` with assembly to avoid redundant masking.
              assembly {
                  auxCasted := aux
              }
              packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
              ERC721AStorage.layout()._packedAddressData[owner] = packed;
          }
          // =============================================================
          //                            IERC165
          // =============================================================
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30000 gas.
           */
          function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
              // The interface IDs are constants representing the first 4 bytes
              // of the XOR of all function selectors in the interface.
              // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
              // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
              return
                  interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
                  interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
                  interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
          }
          // =============================================================
          //                        IERC721Metadata
          // =============================================================
          /**
           * @dev Returns the token collection name.
           */
          function name() public view virtual override returns (string memory) {
              return ERC721AStorage.layout()._name;
          }
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() public view virtual override returns (string memory) {
              return ERC721AStorage.layout()._symbol;
          }
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
              if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
              string memory baseURI = _baseURI();
              return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
          }
          /**
           * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
           * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
           * by default, it can be overridden in child contracts.
           */
          function _baseURI() internal view virtual returns (string memory) {
              return '';
          }
          // =============================================================
          //                     OWNERSHIPS OPERATIONS
          // =============================================================
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) public view virtual override returns (address) {
              return address(uint160(_packedOwnershipOf(tokenId)));
          }
          /**
           * @dev Gas spent here starts off proportional to the maximum mint batch size.
           * It gradually moves to O(1) as tokens get transferred around over time.
           */
          function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
              return _unpackedOwnership(_packedOwnershipOf(tokenId));
          }
          /**
           * @dev Returns the unpacked `TokenOwnership` struct at `index`.
           */
          function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
              return _unpackedOwnership(ERC721AStorage.layout()._packedOwnerships[index]);
          }
          /**
           * @dev Returns whether the ownership slot at `index` is initialized.
           * An uninitialized slot does not necessarily mean that the slot has no owner.
           */
          function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
              return ERC721AStorage.layout()._packedOwnerships[index] != 0;
          }
          /**
           * @dev Initializes the ownership slot minted at `index` for efficiency purposes.
           */
          function _initializeOwnershipAt(uint256 index) internal virtual {
              if (ERC721AStorage.layout()._packedOwnerships[index] == 0) {
                  ERC721AStorage.layout()._packedOwnerships[index] = _packedOwnershipOf(index);
              }
          }
          /**
           * @dev Returns the packed ownership data of `tokenId`.
           */
          function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
              if (_startTokenId() <= tokenId) {
                  packed = ERC721AStorage.layout()._packedOwnerships[tokenId];
                  if (tokenId > _sequentialUpTo()) {
                      if (_packedOwnershipExists(packed)) return packed;
                      _revert(OwnerQueryForNonexistentToken.selector);
                  }
                  // If the data at the starting slot does not exist, start the scan.
                  if (packed == 0) {
                      if (tokenId >= ERC721AStorage.layout()._currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
                      // Invariant:
                      // There will always be an initialized ownership slot
                      // (i.e. `ownership.addr != address(0) && ownership.burned == false`)
                      // before an unintialized ownership slot
                      // (i.e. `ownership.addr == address(0) && ownership.burned == false`)
                      // Hence, `tokenId` will not underflow.
                      //
                      // We can directly compare the packed value.
                      // If the address is zero, packed will be zero.
                      for (;;) {
                          unchecked {
                              packed = ERC721AStorage.layout()._packedOwnerships[--tokenId];
                          }
                          if (packed == 0) continue;
                          if (packed & _BITMASK_BURNED == 0) return packed;
                          // Otherwise, the token is burned, and we must revert.
                          // This handles the case of batch burned tokens, where only the burned bit
                          // of the starting slot is set, and remaining slots are left uninitialized.
                          _revert(OwnerQueryForNonexistentToken.selector);
                      }
                  }
                  // Otherwise, the data exists and we can skip the scan.
                  // This is possible because we have already achieved the target condition.
                  // This saves 2143 gas on transfers of initialized tokens.
                  // If the token is not burned, return `packed`. Otherwise, revert.
                  if (packed & _BITMASK_BURNED == 0) return packed;
              }
              _revert(OwnerQueryForNonexistentToken.selector);
          }
          /**
           * @dev Returns the unpacked `TokenOwnership` struct from `packed`.
           */
          function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
              ownership.addr = address(uint160(packed));
              ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
              ownership.burned = packed & _BITMASK_BURNED != 0;
              ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
          }
          /**
           * @dev Packs ownership data into a single uint256.
           */
          function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
              assembly {
                  // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                  owner := and(owner, _BITMASK_ADDRESS)
                  // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
                  result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
              }
          }
          /**
           * @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
           */
          function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
              // For branchless setting of the `nextInitialized` flag.
              assembly {
                  // `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
                  result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
              }
          }
          // =============================================================
          //                      APPROVAL OPERATIONS
          // =============================================================
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           */
          function approve(address to, uint256 tokenId) public payable virtual override {
              _approve(to, tokenId, true);
          }
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) public view virtual override returns (address) {
              if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
              return ERC721AStorage.layout()._tokenApprovals[tokenId].value;
          }
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom}
           * for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool approved) public virtual override {
              ERC721AStorage.layout()._operatorApprovals[_msgSenderERC721A()][operator] = approved;
              emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
          }
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
              return ERC721AStorage.layout()._operatorApprovals[owner][operator];
          }
          /**
           * @dev Returns whether `tokenId` exists.
           *
           * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
           *
           * Tokens start existing when they are minted. See {_mint}.
           */
          function _exists(uint256 tokenId) internal view virtual returns (bool result) {
              if (_startTokenId() <= tokenId) {
                  if (tokenId > _sequentialUpTo())
                      return _packedOwnershipExists(ERC721AStorage.layout()._packedOwnerships[tokenId]);
                  if (tokenId < ERC721AStorage.layout()._currentIndex) {
                      uint256 packed;
                      while ((packed = ERC721AStorage.layout()._packedOwnerships[tokenId]) == 0) --tokenId;
                      result = packed & _BITMASK_BURNED == 0;
                  }
              }
          }
          /**
           * @dev Returns whether `packed` represents a token that exists.
           */
          function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
              assembly {
                  // The following is equivalent to `owner != address(0) && burned == false`.
                  // Symbolically tested.
                  result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
              }
          }
          /**
           * @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
           */
          function _isSenderApprovedOrOwner(
              address approvedAddress,
              address owner,
              address msgSender
          ) private pure returns (bool result) {
              assembly {
                  // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
                  owner := and(owner, _BITMASK_ADDRESS)
                  // Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
                  msgSender := and(msgSender, _BITMASK_ADDRESS)
                  // `msgSender == owner || msgSender == approvedAddress`.
                  result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
              }
          }
          /**
           * @dev Returns the storage slot and value for the approved address of `tokenId`.
           */
          function _getApprovedSlotAndAddress(uint256 tokenId)
              private
              view
              returns (uint256 approvedAddressSlot, address approvedAddress)
          {
              ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._tokenApprovals[tokenId];
              // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
              assembly {
                  approvedAddressSlot := tokenApproval.slot
                  approvedAddress := sload(approvedAddressSlot)
              }
          }
          // =============================================================
          //                      TRANSFER OPERATIONS
          // =============================================================
          /**
           * @dev Transfers `tokenId` from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token
           * by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) public payable virtual override {
              uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
              // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
              from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
              if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
              (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
              // The nested ifs save around 20+ gas over a compound boolean condition.
              if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                  if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
              _beforeTokenTransfers(from, to, tokenId, 1);
              // Clear approvals from the previous owner.
              assembly {
                  if approvedAddress {
                      // This is equivalent to `delete _tokenApprovals[tokenId]`.
                      sstore(approvedAddressSlot, 0)
                  }
              }
              // Underflow of the sender's balance is impossible because we check for
              // ownership above and the recipient's balance can't realistically overflow.
              // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
              unchecked {
                  // We can directly increment and decrement the balances.
                  --ERC721AStorage.layout()._packedAddressData[from]; // Updates: `balance -= 1`.
                  ++ERC721AStorage.layout()._packedAddressData[to]; // Updates: `balance += 1`.
                  // Updates:
                  // - `address` to the next owner.
                  // - `startTimestamp` to the timestamp of transfering.
                  // - `burned` to `false`.
                  // - `nextInitialized` to `true`.
                  ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                      to,
                      _BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
                  );
                  // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                  if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                      uint256 nextTokenId = tokenId + 1;
                      // If the next slot's address is zero and not burned (i.e. packed value is zero).
                      if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                          // If the next slot is within bounds.
                          if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                              // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                              ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                          }
                      }
                  }
              }
              // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
              uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
              assembly {
                  // Emit the `Transfer` event.
                  log4(
                      0, // Start of data (0, since no data).
                      0, // End of data (0, since no data).
                      _TRANSFER_EVENT_SIGNATURE, // Signature.
                      from, // `from`.
                      toMasked, // `to`.
                      tokenId // `tokenId`.
                  )
              }
              if (toMasked == 0) _revert(TransferToZeroAddress.selector);
              _afterTokenTransfers(from, to, tokenId, 1);
          }
          /**
           * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) public payable virtual override {
              safeTransferFrom(from, to, tokenId, '');
          }
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token
           * by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement
           * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes memory _data
          ) public payable virtual override {
              transferFrom(from, to, tokenId);
              if (to.code.length != 0)
                  if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
                      _revert(TransferToNonERC721ReceiverImplementer.selector);
                  }
          }
          /**
           * @dev Hook that is called before a set of serially-ordered token IDs
           * are about to be transferred. This includes minting.
           * And also called before burning one token.
           *
           * `startTokenId` - the first token ID to be transferred.
           * `quantity` - the amount to be transferred.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
           * transferred to `to`.
           * - When `from` is zero, `tokenId` will be minted for `to`.
           * - When `to` is zero, `tokenId` will be burned by `from`.
           * - `from` and `to` are never both zero.
           */
          function _beforeTokenTransfers(
              address from,
              address to,
              uint256 startTokenId,
              uint256 quantity
          ) internal virtual {}
          /**
           * @dev Hook that is called after a set of serially-ordered token IDs
           * have been transferred. This includes minting.
           * And also called after one token has been burned.
           *
           * `startTokenId` - the first token ID to be transferred.
           * `quantity` - the amount to be transferred.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
           * transferred to `to`.
           * - When `from` is zero, `tokenId` has been minted for `to`.
           * - When `to` is zero, `tokenId` has been burned by `from`.
           * - `from` and `to` are never both zero.
           */
          function _afterTokenTransfers(
              address from,
              address to,
              uint256 startTokenId,
              uint256 quantity
          ) internal virtual {}
          /**
           * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
           *
           * `from` - Previous owner of the given token ID.
           * `to` - Target address that will receive the token.
           * `tokenId` - Token ID to be transferred.
           * `_data` - Optional data to send along with the call.
           *
           * Returns whether the call correctly returned the expected magic value.
           */
          function _checkContractOnERC721Received(
              address from,
              address to,
              uint256 tokenId,
              bytes memory _data
          ) private returns (bool) {
              try
                  ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data)
              returns (bytes4 retval) {
                  return retval == ERC721A__IERC721ReceiverUpgradeable(to).onERC721Received.selector;
              } catch (bytes memory reason) {
                  if (reason.length == 0) {
                      _revert(TransferToNonERC721ReceiverImplementer.selector);
                  }
                  assembly {
                      revert(add(32, reason), mload(reason))
                  }
              }
          }
          // =============================================================
          //                        MINT OPERATIONS
          // =============================================================
          /**
           * @dev Mints `quantity` tokens and transfers them to `to`.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `quantity` must be greater than 0.
           *
           * Emits a {Transfer} event for each mint.
           */
          function _mint(address to, uint256 quantity) internal virtual {
              uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
              if (quantity == 0) _revert(MintZeroQuantity.selector);
              _beforeTokenTransfers(address(0), to, startTokenId, quantity);
              // Overflows are incredibly unrealistic.
              // `balance` and `numberMinted` have a maximum limit of 2**64.
              // `tokenId` has a maximum limit of 2**256.
              unchecked {
                  // Updates:
                  // - `address` to the owner.
                  // - `startTimestamp` to the timestamp of minting.
                  // - `burned` to `false`.
                  // - `nextInitialized` to `quantity == 1`.
                  ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                      to,
                      _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                  );
                  // Updates:
                  // - `balance += quantity`.
                  // - `numberMinted += quantity`.
                  //
                  // We can directly add to the `balance` and `numberMinted`.
                  ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                  // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                  uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
                  if (toMasked == 0) _revert(MintToZeroAddress.selector);
                  uint256 end = startTokenId + quantity;
                  uint256 tokenId = startTokenId;
                  if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
                  do {
                      assembly {
                          // Emit the `Transfer` event.
                          log4(
                              0, // Start of data (0, since no data).
                              0, // End of data (0, since no data).
                              _TRANSFER_EVENT_SIGNATURE, // Signature.
                              0, // `address(0)`.
                              toMasked, // `to`.
                              tokenId // `tokenId`.
                          )
                      }
                      // The `!=` check ensures that large values of `quantity`
                      // that overflows uint256 will make the loop run out of gas.
                  } while (++tokenId != end);
                  ERC721AStorage.layout()._currentIndex = end;
              }
              _afterTokenTransfers(address(0), to, startTokenId, quantity);
          }
          /**
           * @dev Mints `quantity` tokens and transfers them to `to`.
           *
           * This function is intended for efficient minting only during contract creation.
           *
           * It emits only one {ConsecutiveTransfer} as defined in
           * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
           * instead of a sequence of {Transfer} event(s).
           *
           * Calling this function outside of contract creation WILL make your contract
           * non-compliant with the ERC721 standard.
           * For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
           * {ConsecutiveTransfer} event is only permissible during contract creation.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `quantity` must be greater than 0.
           *
           * Emits a {ConsecutiveTransfer} event.
           */
          function _mintERC2309(address to, uint256 quantity) internal virtual {
              uint256 startTokenId = ERC721AStorage.layout()._currentIndex;
              if (to == address(0)) _revert(MintToZeroAddress.selector);
              if (quantity == 0) _revert(MintZeroQuantity.selector);
              if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
              _beforeTokenTransfers(address(0), to, startTokenId, quantity);
              // Overflows are unrealistic due to the above check for `quantity` to be below the limit.
              unchecked {
                  // Updates:
                  // - `balance += quantity`.
                  // - `numberMinted += quantity`.
                  //
                  // We can directly add to the `balance` and `numberMinted`.
                  ERC721AStorage.layout()._packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
                  // Updates:
                  // - `address` to the owner.
                  // - `startTimestamp` to the timestamp of minting.
                  // - `burned` to `false`.
                  // - `nextInitialized` to `quantity == 1`.
                  ERC721AStorage.layout()._packedOwnerships[startTokenId] = _packOwnershipData(
                      to,
                      _nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
                  );
                  if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
                  emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
                  ERC721AStorage.layout()._currentIndex = startTokenId + quantity;
              }
              _afterTokenTransfers(address(0), to, startTokenId, quantity);
          }
          /**
           * @dev Safely mints `quantity` tokens and transfers them to `to`.
           *
           * Requirements:
           *
           * - If `to` refers to a smart contract, it must implement
           * {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
           * - `quantity` must be greater than 0.
           *
           * See {_mint}.
           *
           * Emits a {Transfer} event for each mint.
           */
          function _safeMint(
              address to,
              uint256 quantity,
              bytes memory _data
          ) internal virtual {
              _mint(to, quantity);
              unchecked {
                  if (to.code.length != 0) {
                      uint256 end = ERC721AStorage.layout()._currentIndex;
                      uint256 index = end - quantity;
                      do {
                          if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
                              _revert(TransferToNonERC721ReceiverImplementer.selector);
                          }
                      } while (index < end);
                      // This prevents reentrancy to `_safeMint`.
                      // It does not prevent reentrancy to `_safeMintSpot`.
                      if (ERC721AStorage.layout()._currentIndex != end) revert();
                  }
              }
          }
          /**
           * @dev Equivalent to `_safeMint(to, quantity, '')`.
           */
          function _safeMint(address to, uint256 quantity) internal virtual {
              _safeMint(to, quantity, '');
          }
          /**
           * @dev Mints a single token at `tokenId`.
           *
           * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - `tokenId` must be greater than `_sequentialUpTo()`.
           * - `tokenId` must not exist.
           *
           * Emits a {Transfer} event for each mint.
           */
          function _mintSpot(address to, uint256 tokenId) internal virtual {
              if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
              uint256 prevOwnershipPacked = ERC721AStorage.layout()._packedOwnerships[tokenId];
              if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
              _beforeTokenTransfers(address(0), to, tokenId, 1);
              // Overflows are incredibly unrealistic.
              // The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
              // `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
              unchecked {
                  // Updates:
                  // - `address` to the owner.
                  // - `startTimestamp` to the timestamp of minting.
                  // - `burned` to `false`.
                  // - `nextInitialized` to `true` (as `quantity == 1`).
                  ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                      to,
                      _nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
                  );
                  // Updates:
                  // - `balance += 1`.
                  // - `numberMinted += 1`.
                  //
                  // We can directly add to the `balance` and `numberMinted`.
                  ERC721AStorage.layout()._packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
                  // Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
                  uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
                  if (toMasked == 0) _revert(MintToZeroAddress.selector);
                  assembly {
                      // Emit the `Transfer` event.
                      log4(
                          0, // Start of data (0, since no data).
                          0, // End of data (0, since no data).
                          _TRANSFER_EVENT_SIGNATURE, // Signature.
                          0, // `address(0)`.
                          toMasked, // `to`.
                          tokenId // `tokenId`.
                      )
                  }
                  ++ERC721AStorage.layout()._spotMinted;
              }
              _afterTokenTransfers(address(0), to, tokenId, 1);
          }
          /**
           * @dev Safely mints a single token at `tokenId`.
           *
           * Note: A spot-minted `tokenId` that has been burned can be re-minted again.
           *
           * Requirements:
           *
           * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
           * - `tokenId` must be greater than `_sequentialUpTo()`.
           * - `tokenId` must not exist.
           *
           * See {_mintSpot}.
           *
           * Emits a {Transfer} event.
           */
          function _safeMintSpot(
              address to,
              uint256 tokenId,
              bytes memory _data
          ) internal virtual {
              _mintSpot(to, tokenId);
              unchecked {
                  if (to.code.length != 0) {
                      uint256 currentSpotMinted = ERC721AStorage.layout()._spotMinted;
                      if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
                          _revert(TransferToNonERC721ReceiverImplementer.selector);
                      }
                      // This prevents reentrancy to `_safeMintSpot`.
                      // It does not prevent reentrancy to `_safeMint`.
                      if (ERC721AStorage.layout()._spotMinted != currentSpotMinted) revert();
                  }
              }
          }
          /**
           * @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
           */
          function _safeMintSpot(address to, uint256 tokenId) internal virtual {
              _safeMintSpot(to, tokenId, '');
          }
          // =============================================================
          //                       APPROVAL OPERATIONS
          // =============================================================
          /**
           * @dev Equivalent to `_approve(to, tokenId, false)`.
           */
          function _approve(address to, uint256 tokenId) internal virtual {
              _approve(to, tokenId, false);
          }
          /**
           * @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:
           *
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function _approve(
              address to,
              uint256 tokenId,
              bool approvalCheck
          ) internal virtual {
              address owner = ownerOf(tokenId);
              if (approvalCheck && _msgSenderERC721A() != owner)
                  if (!isApprovedForAll(owner, _msgSenderERC721A())) {
                      _revert(ApprovalCallerNotOwnerNorApproved.selector);
                  }
              ERC721AStorage.layout()._tokenApprovals[tokenId].value = to;
              emit Approval(owner, to, tokenId);
          }
          // =============================================================
          //                        BURN OPERATIONS
          // =============================================================
          /**
           * @dev Equivalent to `_burn(tokenId, false)`.
           */
          function _burn(uint256 tokenId) internal virtual {
              _burn(tokenId, false);
          }
          /**
           * @dev Destroys `tokenId`.
           * The approval is cleared when the token is burned.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           *
           * Emits a {Transfer} event.
           */
          function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
              uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
              address from = address(uint160(prevOwnershipPacked));
              (uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
              if (approvalCheck) {
                  // The nested ifs save around 20+ gas over a compound boolean condition.
                  if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
                      if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
              }
              _beforeTokenTransfers(from, address(0), tokenId, 1);
              // Clear approvals from the previous owner.
              assembly {
                  if approvedAddress {
                      // This is equivalent to `delete _tokenApprovals[tokenId]`.
                      sstore(approvedAddressSlot, 0)
                  }
              }
              // Underflow of the sender's balance is impossible because we check for
              // ownership above and the recipient's balance can't realistically overflow.
              // Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
              unchecked {
                  // Updates:
                  // - `balance -= 1`.
                  // - `numberBurned += 1`.
                  //
                  // We can directly decrement the balance, and increment the number burned.
                  // This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
                  ERC721AStorage.layout()._packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
                  // Updates:
                  // - `address` to the last owner.
                  // - `startTimestamp` to the timestamp of burning.
                  // - `burned` to `true`.
                  // - `nextInitialized` to `true`.
                  ERC721AStorage.layout()._packedOwnerships[tokenId] = _packOwnershipData(
                      from,
                      (_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
                  );
                  // If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
                  if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
                      uint256 nextTokenId = tokenId + 1;
                      // If the next slot's address is zero and not burned (i.e. packed value is zero).
                      if (ERC721AStorage.layout()._packedOwnerships[nextTokenId] == 0) {
                          // If the next slot is within bounds.
                          if (nextTokenId != ERC721AStorage.layout()._currentIndex) {
                              // Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
                              ERC721AStorage.layout()._packedOwnerships[nextTokenId] = prevOwnershipPacked;
                          }
                      }
                  }
              }
              emit Transfer(from, address(0), tokenId);
              _afterTokenTransfers(from, address(0), tokenId, 1);
              // Overflow not possible, as `_burnCounter` cannot be exceed `_currentIndex + _spotMinted` times.
              unchecked {
                  ERC721AStorage.layout()._burnCounter++;
              }
          }
          // =============================================================
          //                     EXTRA DATA OPERATIONS
          // =============================================================
          /**
           * @dev Directly sets the extra data for the ownership data `index`.
           */
          function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
              uint256 packed = ERC721AStorage.layout()._packedOwnerships[index];
              if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
              uint256 extraDataCasted;
              // Cast `extraData` with assembly to avoid redundant masking.
              assembly {
                  extraDataCasted := extraData
              }
              packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
              ERC721AStorage.layout()._packedOwnerships[index] = packed;
          }
          /**
           * @dev Called during each token transfer to set the 24bit `extraData` field.
           * Intended to be overridden by the cosumer contract.
           *
           * `previousExtraData` - the value of `extraData` before transfer.
           *
           * Calling conditions:
           *
           * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
           * transferred to `to`.
           * - When `from` is zero, `tokenId` will be minted for `to`.
           * - When `to` is zero, `tokenId` will be burned by `from`.
           * - `from` and `to` are never both zero.
           */
          function _extraData(
              address from,
              address to,
              uint24 previousExtraData
          ) internal view virtual returns (uint24) {}
          /**
           * @dev Returns the next extra data for the packed ownership data.
           * The returned result is shifted into position.
           */
          function _nextExtraData(
              address from,
              address to,
              uint256 prevOwnershipPacked
          ) private view returns (uint256) {
              uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
              return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
          }
          // =============================================================
          //                       OTHER OPERATIONS
          // =============================================================
          /**
           * @dev Returns the message sender (defaults to `msg.sender`).
           *
           * If you are writing GSN compatible contracts, you need to override this function.
           */
          function _msgSenderERC721A() internal view virtual returns (address) {
              return msg.sender;
          }
          /**
           * @dev Converts a uint256 to its ASCII string decimal representation.
           */
          function _toString(uint256 value) internal pure virtual returns (string memory str) {
              assembly {
                  // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                  // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                  // We will need 1 word for the trailing zeros padding, 1 word for the length,
                  // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
                  let m := add(mload(0x40), 0xa0)
                  // Update the free memory pointer to allocate.
                  mstore(0x40, m)
                  // Assign the `str` to the end.
                  str := sub(m, 0x20)
                  // Zeroize the slot after the string.
                  mstore(str, 0)
                  // Cache the end of the memory to calculate the length later.
                  let end := str
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  // prettier-ignore
                  for { let temp := value } 1 {} {
                      str := sub(str, 1)
                      // Write the character to the pointer.
                      // The ASCII index of the '0' character is 48.
                      mstore8(str, add(48, mod(temp, 10)))
                      // Keep dividing `temp` until zero.
                      temp := div(temp, 10)
                      // prettier-ignore
                      if iszero(temp) { break }
                  }
                  let length := sub(end, str)
                  // Move the pointer 32 bytes leftwards to make room for the length.
                  str := sub(str, 0x20)
                  // Store the length.
                  mstore(str, length)
              }
          }
          /**
           * @dev For more efficient reverts.
           */
          function _revert(bytes4 errorSelector) internal pure {
              assembly {
                  mstore(0x00, errorSelector)
                  revert(0x00, 0x04)
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // ERC721A Contracts v4.3.0
      // Creator: Chiru Labs
      pragma solidity ^0.8.4;
      import './IERC721AQueryableUpgradeable.sol';
      import '../ERC721AUpgradeable.sol';
      import '../ERC721A__Initializable.sol';
      /**
       * @title ERC721AQueryable.
       *
       * @dev ERC721A subclass with convenience query functions.
       */
      abstract contract ERC721AQueryableUpgradeable is
          ERC721A__Initializable,
          ERC721AUpgradeable,
          IERC721AQueryableUpgradeable
      {
          function __ERC721AQueryable_init() internal onlyInitializingERC721A {
              __ERC721AQueryable_init_unchained();
          }
          function __ERC721AQueryable_init_unchained() internal onlyInitializingERC721A {}
          /**
           * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
           *
           * If the `tokenId` is out of bounds:
           *
           * - `addr = address(0)`
           * - `startTimestamp = 0`
           * - `burned = false`
           * - `extraData = 0`
           *
           * If the `tokenId` is burned:
           *
           * - `addr = <Address of owner before token was burned>`
           * - `startTimestamp = <Timestamp when token was burned>`
           * - `burned = true`
           * - `extraData = <Extra data when token was burned>`
           *
           * Otherwise:
           *
           * - `addr = <Address of owner>`
           * - `startTimestamp = <Timestamp of start of ownership>`
           * - `burned = false`
           * - `extraData = <Extra data at start of ownership>`
           */
          function explicitOwnershipOf(uint256 tokenId)
              public
              view
              virtual
              override
              returns (TokenOwnership memory ownership)
          {
              unchecked {
                  if (tokenId >= _startTokenId()) {
                      if (tokenId > _sequentialUpTo()) return _ownershipAt(tokenId);
                      if (tokenId < _nextTokenId()) {
                          // If the `tokenId` is within bounds,
                          // scan backwards for the initialized ownership slot.
                          while (!_ownershipIsInitialized(tokenId)) --tokenId;
                          return _ownershipAt(tokenId);
                      }
                  }
              }
          }
          /**
           * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
           * See {ERC721AQueryable-explicitOwnershipOf}
           */
          function explicitOwnershipsOf(uint256[] calldata tokenIds)
              external
              view
              virtual
              override
              returns (TokenOwnership[] memory)
          {
              TokenOwnership[] memory ownerships;
              uint256 i = tokenIds.length;
              assembly {
                  // Grab the free memory pointer.
                  ownerships := mload(0x40)
                  // Store the length.
                  mstore(ownerships, i)
                  // Allocate one word for the length,
                  // `tokenIds.length` words for the pointers.
                  i := shl(5, i) // Multiply `i` by 32.
                  mstore(0x40, add(add(ownerships, 0x20), i))
              }
              while (i != 0) {
                  uint256 tokenId;
                  assembly {
                      i := sub(i, 0x20)
                      tokenId := calldataload(add(tokenIds.offset, i))
                  }
                  TokenOwnership memory ownership = explicitOwnershipOf(tokenId);
                  assembly {
                      // Store the pointer of `ownership` in the `ownerships` array.
                      mstore(add(add(ownerships, 0x20), i), ownership)
                  }
              }
              return ownerships;
          }
          /**
           * @dev Returns an array of token IDs owned by `owner`,
           * in the range [`start`, `stop`)
           * (i.e. `start <= tokenId < stop`).
           *
           * This function allows for tokens to be queried if the collection
           * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
           *
           * Requirements:
           *
           * - `start < stop`
           */
          function tokensOfOwnerIn(
              address owner,
              uint256 start,
              uint256 stop
          ) external view virtual override returns (uint256[] memory) {
              return _tokensOfOwnerIn(owner, start, stop);
          }
          /**
           * @dev Returns an array of token IDs owned by `owner`.
           *
           * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
           * It is meant to be called off-chain.
           *
           * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
           * multiple smaller scans if the collection is large enough to cause
           * an out-of-gas error (10K collections should be fine).
           */
          function tokensOfOwner(address owner) external view virtual override returns (uint256[] memory) {
              // If spot mints are enabled, full-range scan is disabled.
              if (_sequentialUpTo() != type(uint256).max) _revert(NotCompatibleWithSpotMints.selector);
              uint256 start = _startTokenId();
              uint256 stop = _nextTokenId();
              uint256[] memory tokenIds;
              if (start != stop) tokenIds = _tokensOfOwnerIn(owner, start, stop);
              return tokenIds;
          }
          /**
           * @dev Helper function for returning an array of token IDs owned by `owner`.
           *
           * Note that this function is optimized for smaller bytecode size over runtime gas,
           * since it is meant to be called off-chain.
           */
          function _tokensOfOwnerIn(
              address owner,
              uint256 start,
              uint256 stop
          ) private view returns (uint256[] memory tokenIds) {
              unchecked {
                  if (start >= stop) _revert(InvalidQueryRange.selector);
                  // Set `start = max(start, _startTokenId())`.
                  if (start < _startTokenId()) start = _startTokenId();
                  uint256 nextTokenId = _nextTokenId();
                  // If spot mints are enabled, scan all the way until the specified `stop`.
                  uint256 stopLimit = _sequentialUpTo() != type(uint256).max ? stop : nextTokenId;
                  // Set `stop = min(stop, stopLimit)`.
                  if (stop >= stopLimit) stop = stopLimit;
                  // Number of tokens to scan.
                  uint256 tokenIdsMaxLength = balanceOf(owner);
                  // Set `tokenIdsMaxLength` to zero if the range contains no tokens.
                  if (start >= stop) tokenIdsMaxLength = 0;
                  // If there are one or more tokens to scan.
                  if (tokenIdsMaxLength != 0) {
                      // Set `tokenIdsMaxLength = min(balanceOf(owner), tokenIdsMaxLength)`.
                      if (stop - start <= tokenIdsMaxLength) tokenIdsMaxLength = stop - start;
                      uint256 m; // Start of available memory.
                      assembly {
                          // Grab the free memory pointer.
                          tokenIds := mload(0x40)
                          // Allocate one word for the length, and `tokenIdsMaxLength` words
                          // for the data. `shl(5, x)` is equivalent to `mul(32, x)`.
                          m := add(tokenIds, shl(5, add(tokenIdsMaxLength, 1)))
                          mstore(0x40, m)
                      }
                      // We need to call `explicitOwnershipOf(start)`,
                      // because the slot at `start` may not be initialized.
                      TokenOwnership memory ownership = explicitOwnershipOf(start);
                      address currOwnershipAddr;
                      // If the starting slot exists (i.e. not burned),
                      // initialize `currOwnershipAddr`.
                      // `ownership.address` will not be zero,
                      // as `start` is clamped to the valid token ID range.
                      if (!ownership.burned) currOwnershipAddr = ownership.addr;
                      uint256 tokenIdsIdx;
                      // Use a do-while, which is slightly more efficient for this case,
                      // as the array will at least contain one element.
                      do {
                          if (_sequentialUpTo() != type(uint256).max) {
                              // Skip the remaining unused sequential slots.
                              if (start == nextTokenId) start = _sequentialUpTo() + 1;
                              // Reset `currOwnershipAddr`, as each spot-minted token is a batch of one.
                              if (start > _sequentialUpTo()) currOwnershipAddr = address(0);
                          }
                          ownership = _ownershipAt(start); // This implicitly allocates memory.
                          assembly {
                              switch mload(add(ownership, 0x40))
                              // if `ownership.burned == false`.
                              case 0 {
                                  // if `ownership.addr != address(0)`.
                                  // The `addr` already has it's upper 96 bits clearned,
                                  // since it is written to memory with regular Solidity.
                                  if mload(ownership) {
                                      currOwnershipAddr := mload(ownership)
                                  }
                                  // if `currOwnershipAddr == owner`.
                                  // The `shl(96, x)` is to make the comparison agnostic to any
                                  // dirty upper 96 bits in `owner`.
                                  if iszero(shl(96, xor(currOwnershipAddr, owner))) {
                                      tokenIdsIdx := add(tokenIdsIdx, 1)
                                      mstore(add(tokenIds, shl(5, tokenIdsIdx)), start)
                                  }
                              }
                              // Otherwise, reset `currOwnershipAddr`.
                              // This handles the case of batch burned tokens
                              // (burned bit of first slot set, remaining slots left uninitialized).
                              default {
                                  currOwnershipAddr := 0
                              }
                              start := add(start, 1)
                              // Free temporary memory implicitly allocated for ownership
                              // to avoid quadratic memory expansion costs.
                              mstore(0x40, m)
                          }
                      } while (!(start == stop || tokenIdsIdx == tokenIdsMaxLength));
                      // Store the length of the array.
                      assembly {
                          mstore(tokenIds, tokenIdsIdx)
                      }
                  }
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // ERC721A Contracts v4.3.0
      // Creator: Chiru Labs
      pragma solidity ^0.8.4;
      import '../IERC721AUpgradeable.sol';
      /**
       * @dev Interface of ERC721AQueryable.
       */
      interface IERC721AQueryableUpgradeable is IERC721AUpgradeable {
          /**
           * Invalid query range (`start` >= `stop`).
           */
          error InvalidQueryRange();
          /**
           * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
           *
           * If the `tokenId` is out of bounds:
           *
           * - `addr = address(0)`
           * - `startTimestamp = 0`
           * - `burned = false`
           * - `extraData = 0`
           *
           * If the `tokenId` is burned:
           *
           * - `addr = <Address of owner before token was burned>`
           * - `startTimestamp = <Timestamp when token was burned>`
           * - `burned = true`
           * - `extraData = <Extra data when token was burned>`
           *
           * Otherwise:
           *
           * - `addr = <Address of owner>`
           * - `startTimestamp = <Timestamp of start of ownership>`
           * - `burned = false`
           * - `extraData = <Extra data at start of ownership>`
           */
          function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
          /**
           * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
           * See {ERC721AQueryable-explicitOwnershipOf}
           */
          function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
          /**
           * @dev Returns an array of token IDs owned by `owner`,
           * in the range [`start`, `stop`)
           * (i.e. `start <= tokenId < stop`).
           *
           * This function allows for tokens to be queried if the collection
           * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
           *
           * Requirements:
           *
           * - `start < stop`
           */
          function tokensOfOwnerIn(
              address owner,
              uint256 start,
              uint256 stop
          ) external view returns (uint256[] memory);
          /**
           * @dev Returns an array of token IDs owned by `owner`.
           *
           * This function scans the ownership mapping and is O(`totalSupply`) in complexity.
           * It is meant to be called off-chain.
           *
           * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
           * multiple smaller scans if the collection is large enough to cause
           * an out-of-gas error (10K collections should be fine).
           */
          function tokensOfOwner(address owner) external view returns (uint256[] memory);
      }
      // SPDX-License-Identifier: MIT
      // ERC721A Contracts v4.3.0
      // Creator: Chiru Labs
      pragma solidity ^0.8.4;
      /**
       * @dev Interface of ERC721A.
       */
      interface IERC721AUpgradeable {
          /**
           * The caller must own the token or be an approved operator.
           */
          error ApprovalCallerNotOwnerNorApproved();
          /**
           * The token does not exist.
           */
          error ApprovalQueryForNonexistentToken();
          /**
           * Cannot query the balance for the zero address.
           */
          error BalanceQueryForZeroAddress();
          /**
           * Cannot mint to the zero address.
           */
          error MintToZeroAddress();
          /**
           * The quantity of tokens minted must be more than zero.
           */
          error MintZeroQuantity();
          /**
           * The token does not exist.
           */
          error OwnerQueryForNonexistentToken();
          /**
           * The caller must own the token or be an approved operator.
           */
          error TransferCallerNotOwnerNorApproved();
          /**
           * The token must be owned by `from`.
           */
          error TransferFromIncorrectOwner();
          /**
           * Cannot safely transfer to a contract that does not implement the
           * ERC721Receiver interface.
           */
          error TransferToNonERC721ReceiverImplementer();
          /**
           * Cannot transfer to the zero address.
           */
          error TransferToZeroAddress();
          /**
           * The token does not exist.
           */
          error URIQueryForNonexistentToken();
          /**
           * The `quantity` minted with ERC2309 exceeds the safety limit.
           */
          error MintERC2309QuantityExceedsLimit();
          /**
           * The `extraData` cannot be set on an unintialized ownership slot.
           */
          error OwnershipNotInitializedForExtraData();
          /**
           * `_sequentialUpTo()` must be greater than `_startTokenId()`.
           */
          error SequentialUpToTooSmall();
          /**
           * The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
           */
          error SequentialMintExceedsLimit();
          /**
           * Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
           */
          error SpotMintTokenIdTooSmall();
          /**
           * Cannot mint over a token that already exists.
           */
          error TokenAlreadyExists();
          /**
           * The feature is not compatible with spot mints.
           */
          error NotCompatibleWithSpotMints();
          // =============================================================
          //                            STRUCTS
          // =============================================================
          struct TokenOwnership {
              // The address of the owner.
              address addr;
              // Stores the start time of ownership with minimal overhead for tokenomics.
              uint64 startTimestamp;
              // Whether the token has been burned.
              bool burned;
              // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
              uint24 extraData;
          }
          // =============================================================
          //                         TOKEN COUNTERS
          // =============================================================
          /**
           * @dev Returns the total number of tokens in existence.
           * Burned tokens will reduce the count.
           * To get the total number of tokens minted, please see {_totalMinted}.
           */
          function totalSupply() external view returns (uint256);
          // =============================================================
          //                            IERC165
          // =============================================================
          /**
           * @dev Returns true if this contract implements the interface defined by
           * `interfaceId`. See the corresponding
           * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
           * to learn more about how these ids are created.
           *
           * This function call must use less than 30000 gas.
           */
          function supportsInterface(bytes4 interfaceId) external view returns (bool);
          // =============================================================
          //                            IERC721
          // =============================================================
          /**
           * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
           */
          event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
           */
          event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
          /**
           * @dev Emitted when `owner` enables or disables
           * (`approved`) `operator` to manage all of its assets.
           */
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          /**
           * @dev Returns the number of tokens in `owner`'s account.
           */
          function balanceOf(address owner) external view returns (uint256 balance);
          /**
           * @dev Returns the owner of the `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function ownerOf(uint256 tokenId) external view returns (address owner);
          /**
           * @dev Safely transfers `tokenId` token from `from` to `to`,
           * checking first that contract recipients are aware of the ERC721 protocol
           * to prevent tokens from being forever locked.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must exist and be owned by `from`.
           * - If the caller is not `from`, it must be have been allowed to move
           * this token by either {approve} or {setApprovalForAll}.
           * - If `to` refers to a smart contract, it must implement
           * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
           *
           * Emits a {Transfer} event.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId,
              bytes calldata data
          ) external payable;
          /**
           * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
           */
          function safeTransferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external payable;
          /**
           * @dev Transfers `tokenId` from `from` to `to`.
           *
           * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
           * whenever possible.
           *
           * Requirements:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `tokenId` token must be owned by `from`.
           * - If the caller is not `from`, it must be approved to move this token
           * by either {approve} or {setApprovalForAll}.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address from,
              address to,
              uint256 tokenId
          ) external payable;
          /**
           * @dev Gives permission to `to` to transfer `tokenId` token to another account.
           * The approval is cleared when the token is transferred.
           *
           * Only a single account can be approved at a time, so approving the
           * zero address clears previous approvals.
           *
           * Requirements:
           *
           * - The caller must own the token or be an approved operator.
           * - `tokenId` must exist.
           *
           * Emits an {Approval} event.
           */
          function approve(address to, uint256 tokenId) external payable;
          /**
           * @dev Approve or remove `operator` as an operator for the caller.
           * Operators can call {transferFrom} or {safeTransferFrom}
           * for any token owned by the caller.
           *
           * Requirements:
           *
           * - The `operator` cannot be the caller.
           *
           * Emits an {ApprovalForAll} event.
           */
          function setApprovalForAll(address operator, bool _approved) external;
          /**
           * @dev Returns the account approved for `tokenId` token.
           *
           * Requirements:
           *
           * - `tokenId` must exist.
           */
          function getApproved(uint256 tokenId) external view returns (address operator);
          /**
           * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
           *
           * See {setApprovalForAll}.
           */
          function isApprovedForAll(address owner, address operator) external view returns (bool);
          // =============================================================
          //                        IERC721Metadata
          // =============================================================
          /**
           * @dev Returns the token collection name.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the token collection symbol.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
           */
          function tokenURI(uint256 tokenId) external view returns (string memory);
          // =============================================================
          //                           IERC2309
          // =============================================================
          /**
           * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
           * (inclusive) is transferred from `from` to `to`, as defined in the
           * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
           *
           * See {_mintERC2309} for more details.
           */
          event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Gas optimized ECDSA wrapper.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
      ///
      /// @dev Note:
      /// - The recovery functions use the ecrecover precompile (0x1).
      /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
      ///   This is for more safety by default.
      ///   Use the `tryRecover` variants if you need to get the zero address back
      ///   upon recovery failure instead.
      /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
      ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
      ///   See: https://eips.ethereum.org/EIPS/eip-2098
      ///   This is for calldata efficiency on smart accounts prevalent on L2s.
      ///
      /// WARNING! Do NOT directly use signatures as unique identifiers:
      /// - The recovery operations do NOT check if a signature is non-malleable.
      /// - Use a nonce in the digest to prevent replay attacks on the same contract.
      /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
      ///   EIP-712 also enables readable signing of typed data for better user safety.
      /// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
      library ECDSA {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         CONSTANTS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The order of the secp256k1 elliptic curve.
          uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
          /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
          uint256 private constant _HALF_N_PLUS_1 =
              0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                        CUSTOM ERRORS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The signature is invalid.
          error InvalidSignature();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                    RECOVERY OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function recoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the EIP-2098 short form signature defined by `r` and `vs`.
          function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                  mstore(0x40, r)
                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the signature defined by `v`, `r`, `s`.
          function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, and(v, 0xff))
                  mstore(0x40, r)
                  mstore(0x60, s)
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   TRY-RECOVER OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // WARNING!
          // These functions will NOT revert upon recovery failure.
          // Instead, they will return the zero address upon recovery failure.
          // It is critical that the returned address is NEVER compared against
          // a zero address (e.g. an uninitialized address variable).
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function tryRecover(bytes32 hash, bytes memory signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the EIP-2098 short form signature defined by `r` and `vs`.
          function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                  mstore(0x40, r)
                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          1, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the signature defined by `v`, `r`, `s`.
          function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, and(v, 0xff))
                  mstore(0x40, r)
                  mstore(0x60, s)
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          1, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     HASHING OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an Ethereum Signed Message, created from a `hash`.
          /// This produces a hash corresponding to the one signed with the
          /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
          /// JSON-RPC method as part of EIP-191.
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, hash) // Store into scratch space for keccak256.
                  mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
      32") // 28 bytes.
                  result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
              }
          }
          /// @dev Returns an Ethereum Signed Message, created from `s`.
          /// This produces a hash corresponding to the one signed with the
          /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
          /// JSON-RPC method as part of EIP-191.
          /// Note: Supports lengths of `s` up to 999999 bytes.
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let sLength := mload(s)
                  let o := 0x20
                  mstore(o, "\\x19Ethereum Signed Message:\
      ") // 26 bytes, zero-right-padded.
                  mstore(0x00, 0x00)
                  // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                  for { let temp := sLength } 1 {} {
                      o := sub(o, 1)
                      mstore8(o, add(48, mod(temp, 10)))
                      temp := div(temp, 10)
                      if iszero(temp) { break }
                  }
                  let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                  // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                  returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                  mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                  result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                  mstore(s, sLength) // Restore the length.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  CANONICAL HASH FUNCTIONS                  */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // The following functions returns the hash of the signature in it's canonicalized format,
          // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
          // If `s` is greater than `N / 2` then it will be converted to `N - s`
          // and the `v` value will be flipped.
          // If the signature has an invalid length, or if `v` is invalid,
          // a uniquely corrupt hash will be returned.
          // These functions are useful for "poor-mans-VRF".
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  let l := mload(signature)
                  for {} 1 {} {
                      mstore(0x00, mload(add(signature, 0x20))) // `r`.
                      let s := mload(add(signature, 0x40))
                      let v := mload(add(signature, 0x41))
                      if eq(l, 64) {
                          v := add(shr(255, s), 27)
                          s := shr(1, shl(1, s))
                      }
                      if iszero(lt(s, _HALF_N_PLUS_1)) {
                          v := xor(v, 7)
                          s := sub(N, s)
                      }
                      mstore(0x21, v)
                      mstore(0x20, s)
                      result := keccak256(0x00, 0x41)
                      mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                      break
                  }
                  // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                  if iszero(lt(sub(l, 64), 2)) {
                      // `bytes4(keccak256("InvalidSignatureLength"))`.
                      result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
                  }
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHashCalldata(bytes calldata signature)
              internal
              pure
              returns (bytes32 result)
          {
              // @solidity memory-safe-assembly
              assembly {
                  let l := signature.length
                  for {} 1 {} {
                      mstore(0x00, calldataload(signature.offset)) // `r`.
                      let s := calldataload(add(signature.offset, 0x20))
                      let v := calldataload(add(signature.offset, 0x21))
                      if eq(l, 64) {
                          v := add(shr(255, s), 27)
                          s := shr(1, shl(1, s))
                      }
                      if iszero(lt(s, _HALF_N_PLUS_1)) {
                          v := xor(v, 7)
                          s := sub(N, s)
                      }
                      mstore(0x21, v)
                      mstore(0x20, s)
                      result := keccak256(0x00, 0x41)
                      mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                      break
                  }
                  // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                  if iszero(lt(sub(l, 64), 2)) {
                      calldatacopy(mload(0x40), signature.offset, l)
                      // `bytes4(keccak256("InvalidSignatureLength"))`.
                      result := xor(keccak256(mload(0x40), l), 0xd62f1ab2)
                  }
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, r) // `r`.
                  let v := add(shr(255, vs), 27)
                  let s := shr(1, shl(1, vs))
                  mstore(0x21, v)
                  mstore(0x20, s)
                  result := keccak256(0x00, 0x41)
                  mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, r) // `r`.
                  if iszero(lt(s, _HALF_N_PLUS_1)) {
                      v := xor(v, 7)
                      s := sub(N, s)
                  }
                  mstore(0x21, v)
                  mstore(0x20, s)
                  result := keccak256(0x00, 0x41)
                  mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   EMPTY CALLDATA HELPERS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an empty calldata bytes.
          function emptySignature() internal pure returns (bytes calldata signature) {
              /// @solidity memory-safe-assembly
              assembly {
                  signature.length := 0
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Library for converting numbers into strings and other string operations.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
      ///
      /// @dev Note:
      /// For performance and bytecode compactness, most of the string operations are restricted to
      /// byte strings (7-bit ASCII), except where otherwise specified.
      /// Usage of byte string operations on charsets with runes spanning two or more bytes
      /// can lead to undefined behavior.
      library LibString {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                        CUSTOM ERRORS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The length of the output is too small to contain all the hex digits.
          error HexLengthInsufficient();
          /// @dev The length of the string is more than 32 bytes.
          error TooBigForSmallString();
          /// @dev The input string must be a 7-bit ASCII.
          error StringNot7BitASCII();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         CONSTANTS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The constant returned when the `search` is not found in the string.
          uint256 internal constant NOT_FOUND = type(uint256).max;
          /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
          uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
          /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
          uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
          /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
          uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
          /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
          uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
          /// @dev Lookup for '0123456789'.
          uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
          /// @dev Lookup for '0123456789abcdefABCDEF'.
          uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
          /// @dev Lookup for '01234567'.
          uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
          /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~ \\t\
      \
      \\x0b\\x0c'.
          uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
          /// @dev Lookup for '!"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~'.
          uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
          /// @dev Lookup for ' \\t\
      \
      \\x0b\\x0c'.
          uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     DECIMAL OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the base 10 decimal representation of `value`.
          function toString(uint256 value) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
                  // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
                  // We will need 1 word for the trailing zeros padding, 1 word for the length,
                  // and 3 words for a maximum of 78 digits.
                  result := add(mload(0x40), 0x80)
                  mstore(0x40, add(result, 0x20)) // Allocate memory.
                  mstore(result, 0) // Zeroize the slot after the string.
                  let end := result // Cache the end of the memory to calculate the length later.
                  let w := not(0) // Tsk.
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let temp := value } 1 {} {
                      result := add(result, w) // `sub(result, 1)`.
                      // Store the character to the pointer.
                      // The ASCII index of the '0' character is 48.
                      mstore8(result, add(48, mod(temp, 10)))
                      temp := div(temp, 10) // Keep dividing `temp` until zero.
                      if iszero(temp) { break }
                  }
                  let n := sub(end, result)
                  result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the base 10 decimal representation of `value`.
          function toString(int256 value) internal pure returns (string memory result) {
              if (value >= 0) return toString(uint256(value));
              unchecked {
                  result = toString(~uint256(value) + 1);
              }
              /// @solidity memory-safe-assembly
              assembly {
                  // We still have some spare memory space on the left,
                  // as we have allocated 3 words (96 bytes) for up to 78 digits.
                  let n := mload(result) // Load the string length.
                  mstore(result, 0x2d) // Store the '-' character.
                  result := sub(result, 1) // Move back the string pointer by a byte.
                  mstore(result, add(n, 1)) // Update the string length.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   HEXADECIMAL OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the hexadecimal representation of `value`,
          /// left-padded to an input length of `length` bytes.
          /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
          /// giving a total length of `length * 2 + 2` bytes.
          /// Reverts if `length` is too small for the output to contain all the digits.
          function toHexString(uint256 value, uint256 length)
              internal
              pure
              returns (string memory result)
          {
              result = toHexStringNoPrefix(value, length);
              /// @solidity memory-safe-assembly
              assembly {
                  let n := add(mload(result), 2) // Compute the length.
                  mstore(result, 0x3078) // Store the "0x" prefix.
                  result := sub(result, 2) // Move the pointer.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`,
          /// left-padded to an input length of `length` bytes.
          /// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte,
          /// giving a total length of `length * 2` bytes.
          /// Reverts if `length` is too small for the output to contain all the digits.
          function toHexStringNoPrefix(uint256 value, uint256 length)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
                  // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
                  // We add 0x20 to the total and round down to a multiple of 0x20.
                  // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
                  result := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
                  mstore(0x40, add(result, 0x20)) // Allocate memory.
                  mstore(result, 0) // Zeroize the slot after the string.
                  let end := result // Cache the end to calculate the length later.
                  // Store "0123456789abcdef" in scratch space.
                  mstore(0x0f, 0x30313233343536373839616263646566)
                  let start := sub(result, add(length, length))
                  let w := not(1) // Tsk.
                  let temp := value
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for {} 1 {} {
                      result := add(result, w) // `sub(result, 2)`.
                      mstore8(add(result, 1), mload(and(temp, 15)))
                      mstore8(result, mload(and(shr(4, temp), 15)))
                      temp := shr(8, temp)
                      if iszero(xor(result, start)) { break }
                  }
                  if temp {
                      mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                      revert(0x1c, 0x04)
                  }
                  let n := sub(end, result)
                  result := sub(result, 0x20)
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
          /// As address are 20 bytes long, the output will left-padded to have
          /// a length of `20 * 2 + 2` bytes.
          function toHexString(uint256 value) internal pure returns (string memory result) {
              result = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let n := add(mload(result), 2) // Compute the length.
                  mstore(result, 0x3078) // Store the "0x" prefix.
                  result := sub(result, 2) // Move the pointer.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x".
          /// The output excludes leading "0" from the `toHexString` output.
          /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
          function toMinimalHexString(uint256 value) internal pure returns (string memory result) {
              result = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
                  let n := add(mload(result), 2) // Compute the length.
                  mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
                  result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero.
                  mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
          /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
          function toMinimalHexStringNoPrefix(uint256 value)
              internal
              pure
              returns (string memory result)
          {
              result = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present.
                  let n := mload(result) // Get the length.
                  result := add(result, o) // Move the pointer, accounting for leading zero.
                  mstore(result, sub(n, o)) // Store the length, accounting for leading zero.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is encoded using 2 hexadecimal digits per byte.
          /// As address are 20 bytes long, the output will left-padded to have
          /// a length of `20 * 2` bytes.
          function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                  // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
                  // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
                  result := add(mload(0x40), 0x80)
                  mstore(0x40, add(result, 0x20)) // Allocate memory.
                  mstore(result, 0) // Zeroize the slot after the string.
                  let end := result // Cache the end to calculate the length later.
                  mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
                  let w := not(1) // Tsk.
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let temp := value } 1 {} {
                      result := add(result, w) // `sub(result, 2)`.
                      mstore8(add(result, 1), mload(and(temp, 15)))
                      mstore8(result, mload(and(shr(4, temp), 15)))
                      temp := shr(8, temp)
                      if iszero(temp) { break }
                  }
                  let n := sub(end, result)
                  result := sub(result, 0x20)
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
          /// and the alphabets are capitalized conditionally according to
          /// https://eips.ethereum.org/EIPS/eip-55
          function toHexStringChecksummed(address value) internal pure returns (string memory result) {
              result = toHexString(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
                  let o := add(result, 0x22)
                  let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
                  let t := shl(240, 136) // `0b10001000 << 240`
                  for { let i := 0 } 1 {} {
                      mstore(add(i, i), mul(t, byte(i, hashed)))
                      i := add(i, 1)
                      if eq(i, 20) { break }
                  }
                  mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
                  o := add(o, 0x20)
                  mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
          function toHexString(address value) internal pure returns (string memory result) {
              result = toHexStringNoPrefix(value);
              /// @solidity memory-safe-assembly
              assembly {
                  let n := add(mload(result), 2) // Compute the length.
                  mstore(result, 0x3078) // Store the "0x" prefix.
                  result := sub(result, 2) // Move the pointer.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hexadecimal representation of `value`.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexStringNoPrefix(address value) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  // Allocate memory.
                  // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
                  // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
                  // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
                  mstore(0x40, add(result, 0x80))
                  mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
                  result := add(result, 2)
                  mstore(result, 40) // Store the length.
                  let o := add(result, 0x20)
                  mstore(add(o, 40), 0) // Zeroize the slot after the string.
                  value := shl(96, value)
                  // We write the string from rightmost digit to leftmost digit.
                  // The following is essentially a do-while loop that also handles the zero case.
                  for { let i := 0 } 1 {} {
                      let p := add(o, add(i, i))
                      let temp := byte(i, value)
                      mstore8(add(p, 1), mload(and(temp, 15)))
                      mstore8(p, mload(shr(4, temp)))
                      i := add(i, 1)
                      if eq(i, 20) { break }
                  }
              }
          }
          /// @dev Returns the hex encoded string from the raw bytes.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexString(bytes memory raw) internal pure returns (string memory result) {
              result = toHexStringNoPrefix(raw);
              /// @solidity memory-safe-assembly
              assembly {
                  let n := add(mload(result), 2) // Compute the length.
                  mstore(result, 0x3078) // Store the "0x" prefix.
                  result := sub(result, 2) // Move the pointer.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the hex encoded string from the raw bytes.
          /// The output is encoded using 2 hexadecimal digits per byte.
          function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let n := mload(raw)
                  result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
                  mstore(result, add(n, n)) // Store the length of the output.
                  mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
                  let o := add(result, 0x20)
                  let end := add(raw, n)
                  for {} iszero(eq(raw, end)) {} {
                      raw := add(raw, 1)
                      mstore8(add(o, 1), mload(and(mload(raw), 15)))
                      mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                      o := add(o, 2)
                  }
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(0x40, add(o, 0x20)) // Allocate memory.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   RUNE STRING OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns the number of UTF characters in the string.
          function runeCount(string memory s) internal pure returns (uint256 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  if mload(s) {
                      mstore(0x00, div(not(0), 255))
                      mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                      let o := add(s, 0x20)
                      let end := add(o, mload(s))
                      for { result := 1 } 1 { result := add(result, 1) } {
                          o := add(o, byte(0, mload(shr(250, mload(o)))))
                          if iszero(lt(o, end)) { break }
                      }
                  }
              }
          }
          /// @dev Returns if this string is a 7-bit ASCII string.
          /// (i.e. all characters codes are in [0..127])
          function is7BitASCII(string memory s) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let mask := shl(7, div(not(0), 255))
                  let n := mload(s)
                  if n {
                      let o := add(s, 0x20)
                      let end := add(o, n)
                      let last := mload(end)
                      mstore(end, 0)
                      for {} 1 {} {
                          if and(mask, mload(o)) {
                              result := 0
                              break
                          }
                          o := add(o, 0x20)
                          if iszero(lt(o, end)) { break }
                      }
                      mstore(end, last)
                  }
              }
          }
          /// @dev Returns if this string is a 7-bit ASCII string,
          /// AND all characters are in the `allowed` lookup.
          /// Note: If `s` is empty, returns true regardless of `allowed`.
          function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  if mload(s) {
                      let allowed_ := shr(128, shl(128, allowed))
                      let o := add(s, 0x20)
                      for { let end := add(o, mload(s)) } 1 {} {
                          result := and(result, shr(byte(0, mload(o)), allowed_))
                          o := add(o, 1)
                          if iszero(and(result, lt(o, end))) { break }
                      }
                  }
              }
          }
          /// @dev Converts the bytes in the 7-bit ASCII string `s` to
          /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
          /// To save runtime gas, you can cache the result in an immutable variable.
          function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  if mload(s) {
                      let o := add(s, 0x20)
                      for { let end := add(o, mload(s)) } 1 {} {
                          result := or(result, shl(byte(0, mload(o)), 1))
                          o := add(o, 1)
                          if iszero(lt(o, end)) { break }
                      }
                      if shr(128, result) {
                          mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                          revert(0x1c, 0x04)
                      }
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   BYTE STRING OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // For performance and bytecode compactness, byte string operations are restricted
          // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
          // Usage of byte string operations on charsets with runes spanning two or more bytes
          // can lead to undefined behavior.
          /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`.
          function replace(string memory subject, string memory needle, string memory replacement)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let needleLen := mload(needle)
                  let replacementLen := mload(replacement)
                  let d := sub(result, subject) // Memory difference.
                  let i := add(subject, 0x20) // Subject bytes pointer.
                  let end := add(i, mload(subject))
                  if iszero(gt(needleLen, mload(subject))) {
                      let subjectSearchEnd := add(sub(end, needleLen), 1)
                      let h := 0 // The hash of `needle`.
                      if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) }
                      let s := mload(add(needle, 0x20))
                      for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 {} {
                          let t := mload(i)
                          // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches.
                          if iszero(shr(m, xor(t, s))) {
                              if h {
                                  if iszero(eq(keccak256(i, needleLen), h)) {
                                      mstore(add(i, d), t)
                                      i := add(i, 1)
                                      if iszero(lt(i, subjectSearchEnd)) { break }
                                      continue
                                  }
                              }
                              // Copy the `replacement` one word at a time.
                              for { let j := 0 } 1 {} {
                                  mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j)))
                                  j := add(j, 0x20)
                                  if iszero(lt(j, replacementLen)) { break }
                              }
                              d := sub(add(d, replacementLen), needleLen)
                              if needleLen {
                                  i := add(i, needleLen)
                                  if iszero(lt(i, subjectSearchEnd)) { break }
                                  continue
                              }
                          }
                          mstore(add(i, d), t)
                          i := add(i, 1)
                          if iszero(lt(i, subjectSearchEnd)) { break }
                      }
                  }
                  let n := add(sub(d, add(result, 0x20)), end)
                  // Copy the rest of the string one word at a time.
                  for {} lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) }
                  let o := add(i, d)
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(0x40, add(o, 0x20)) // Allocate memory.
                  mstore(result, n) // Store the length.
              }
          }
          /// @dev Returns the byte index of the first location of `needle` in `subject`,
          /// needleing from left to right, starting from `from`.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
          function indexOf(string memory subject, string memory needle, uint256 from)
              internal
              pure
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := not(0) // Initialize to `NOT_FOUND`.
                  for { let subjectLen := mload(subject) } 1 {} {
                      if iszero(mload(needle)) {
                          result := from
                          if iszero(gt(from, subjectLen)) { break }
                          result := subjectLen
                          break
                      }
                      let needleLen := mload(needle)
                      let subjectStart := add(subject, 0x20)
                      subject := add(subjectStart, from)
                      let end := add(sub(add(subjectStart, subjectLen), needleLen), 1)
                      let m := shl(3, sub(0x20, and(needleLen, 0x1f)))
                      let s := mload(add(needle, 0x20))
                      if iszero(and(lt(subject, end), lt(from, subjectLen))) { break }
                      if iszero(lt(needleLen, 0x20)) {
                          for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                              if iszero(shr(m, xor(mload(subject), s))) {
                                  if eq(keccak256(subject, needleLen), h) {
                                      result := sub(subject, subjectStart)
                                      break
                                  }
                              }
                              subject := add(subject, 1)
                              if iszero(lt(subject, end)) { break }
                          }
                          break
                      }
                      for {} 1 {} {
                          if iszero(shr(m, xor(mload(subject), s))) {
                              result := sub(subject, subjectStart)
                              break
                          }
                          subject := add(subject, 1)
                          if iszero(lt(subject, end)) { break }
                      }
                      break
                  }
              }
          }
          /// @dev Returns the byte index of the first location of `needle` in `subject`,
          /// needleing from left to right.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
          function indexOf(string memory subject, string memory needle)
              internal
              pure
              returns (uint256 result)
          {
              result = indexOf(subject, needle, 0);
          }
          /// @dev Returns the byte index of the first location of `needle` in `subject`,
          /// needleing from right to left, starting from `from`.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
          function lastIndexOf(string memory subject, string memory needle, uint256 from)
              internal
              pure
              returns (uint256 result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  for {} 1 {} {
                      result := not(0) // Initialize to `NOT_FOUND`.
                      let needleLen := mload(needle)
                      if gt(needleLen, mload(subject)) { break }
                      let w := result
                      let fromMax := sub(mload(subject), needleLen)
                      if iszero(gt(fromMax, from)) { from := fromMax }
                      let end := add(add(subject, 0x20), w)
                      subject := add(add(subject, 0x20), from)
                      if iszero(gt(subject, end)) { break }
                      // As this function is not too often used,
                      // we shall simply use keccak256 for smaller bytecode size.
                      for { let h := keccak256(add(needle, 0x20), needleLen) } 1 {} {
                          if eq(keccak256(subject, needleLen), h) {
                              result := sub(subject, add(end, 1))
                              break
                          }
                          subject := add(subject, w) // `sub(subject, 1)`.
                          if iszero(gt(subject, end)) { break }
                      }
                      break
                  }
              }
          }
          /// @dev Returns the byte index of the first location of `needle` in `subject`,
          /// needleing from right to left.
          /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found.
          function lastIndexOf(string memory subject, string memory needle)
              internal
              pure
              returns (uint256 result)
          {
              result = lastIndexOf(subject, needle, type(uint256).max);
          }
          /// @dev Returns true if `needle` is found in `subject`, false otherwise.
          function contains(string memory subject, string memory needle) internal pure returns (bool) {
              return indexOf(subject, needle) != NOT_FOUND;
          }
          /// @dev Returns whether `subject` starts with `needle`.
          function startsWith(string memory subject, string memory needle)
              internal
              pure
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let needleLen := mload(needle)
                  // Just using keccak256 directly is actually cheaper.
                  // forgefmt: disable-next-item
                  result := and(
                      iszero(gt(needleLen, mload(subject))),
                      eq(
                          keccak256(add(subject, 0x20), needleLen),
                          keccak256(add(needle, 0x20), needleLen)
                      )
                  )
              }
          }
          /// @dev Returns whether `subject` ends with `needle`.
          function endsWith(string memory subject, string memory needle)
              internal
              pure
              returns (bool result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let needleLen := mload(needle)
                  // Whether `needle` is not longer than `subject`.
                  let inRange := iszero(gt(needleLen, mload(subject)))
                  // Just using keccak256 directly is actually cheaper.
                  // forgefmt: disable-next-item
                  result := and(
                      eq(
                          keccak256(
                              // `subject + 0x20 + max(subjectLen - needleLen, 0)`.
                              add(add(subject, 0x20), mul(inRange, sub(mload(subject), needleLen))),
                              needleLen
                          ),
                          keccak256(add(needle, 0x20), needleLen)
                      ),
                      inRange
                  )
              }
          }
          /// @dev Returns `subject` repeated `times`.
          function repeat(string memory subject, uint256 times)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLen := mload(subject)
                  if iszero(or(iszero(times), iszero(subjectLen))) {
                      result := mload(0x40)
                      subject := add(subject, 0x20)
                      let o := add(result, 0x20)
                      for {} 1 {} {
                          // Copy the `subject` one word at a time.
                          for { let j := 0 } 1 {} {
                              mstore(add(o, j), mload(add(subject, j)))
                              j := add(j, 0x20)
                              if iszero(lt(j, subjectLen)) { break }
                          }
                          o := add(o, subjectLen)
                          times := sub(times, 1)
                          if iszero(times) { break }
                      }
                      mstore(o, 0) // Zeroize the slot after the string.
                      mstore(0x40, add(o, 0x20)) // Allocate memory.
                      mstore(result, sub(o, add(result, 0x20))) // Store the length.
                  }
              }
          }
          /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
          /// `start` and `end` are byte offsets.
          function slice(string memory subject, uint256 start, uint256 end)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let subjectLen := mload(subject)
                  if iszero(gt(subjectLen, end)) { end := subjectLen }
                  if iszero(gt(subjectLen, start)) { start := subjectLen }
                  if lt(start, end) {
                      result := mload(0x40)
                      let n := sub(end, start)
                      let i := add(subject, start)
                      let w := not(0x1f)
                      // Copy the `subject` one word at a time, backwards.
                      for { let j := and(add(n, 0x1f), w) } 1 {} {
                          mstore(add(result, j), mload(add(i, j)))
                          j := add(j, w) // `sub(j, 0x20)`.
                          if iszero(j) { break }
                      }
                      let o := add(add(result, 0x20), n)
                      mstore(o, 0) // Zeroize the slot after the string.
                      mstore(0x40, add(o, 0x20)) // Allocate memory.
                      mstore(result, n) // Store the length.
                  }
              }
          }
          /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
          /// `start` is a byte offset.
          function slice(string memory subject, uint256 start)
              internal
              pure
              returns (string memory result)
          {
              result = slice(subject, start, type(uint256).max);
          }
          /// @dev Returns all the indices of `needle` in `subject`.
          /// The indices are byte offsets.
          function indicesOf(string memory subject, string memory needle)
              internal
              pure
              returns (uint256[] memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let searchLen := mload(needle)
                  if iszero(gt(searchLen, mload(subject))) {
                      result := mload(0x40)
                      let i := add(subject, 0x20)
                      let o := add(result, 0x20)
                      let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1)
                      let h := 0 // The hash of `needle`.
                      if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) }
                      let s := mload(add(needle, 0x20))
                      for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 {} {
                          let t := mload(i)
                          // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches.
                          if iszero(shr(m, xor(t, s))) {
                              if h {
                                  if iszero(eq(keccak256(i, searchLen), h)) {
                                      i := add(i, 1)
                                      if iszero(lt(i, subjectSearchEnd)) { break }
                                      continue
                                  }
                              }
                              mstore(o, sub(i, add(subject, 0x20))) // Append to `result`.
                              o := add(o, 0x20)
                              i := add(i, searchLen) // Advance `i` by `searchLen`.
                              if searchLen {
                                  if iszero(lt(i, subjectSearchEnd)) { break }
                                  continue
                              }
                          }
                          i := add(i, 1)
                          if iszero(lt(i, subjectSearchEnd)) { break }
                      }
                      mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`.
                      // Allocate memory for result.
                      // We allocate one more word, so this array can be recycled for {split}.
                      mstore(0x40, add(o, 0x20))
                  }
              }
          }
          /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
          function split(string memory subject, string memory delimiter)
              internal
              pure
              returns (string[] memory result)
          {
              uint256[] memory indices = indicesOf(subject, delimiter);
              /// @solidity memory-safe-assembly
              assembly {
                  let w := not(0x1f)
                  let indexPtr := add(indices, 0x20)
                  let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
                  mstore(add(indicesEnd, w), mload(subject))
                  mstore(indices, add(mload(indices), 1))
                  for { let prevIndex := 0 } 1 {} {
                      let index := mload(indexPtr)
                      mstore(indexPtr, 0x60)
                      if iszero(eq(index, prevIndex)) {
                          let element := mload(0x40)
                          let l := sub(index, prevIndex)
                          mstore(element, l) // Store the length of the element.
                          // Copy the `subject` one word at a time, backwards.
                          for { let o := and(add(l, 0x1f), w) } 1 {} {
                              mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                              o := add(o, w) // `sub(o, 0x20)`.
                              if iszero(o) { break }
                          }
                          mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the string.
                          // Allocate memory for the length and the bytes, rounded up to a multiple of 32.
                          mstore(0x40, add(element, and(add(l, 0x3f), w)))
                          mstore(indexPtr, element) // Store the `element` into the array.
                      }
                      prevIndex := add(index, mload(delimiter))
                      indexPtr := add(indexPtr, 0x20)
                      if iszero(lt(indexPtr, indicesEnd)) { break }
                  }
                  result := indices
                  if iszero(mload(delimiter)) {
                      result := add(indices, 0x20)
                      mstore(result, sub(mload(indices), 2))
                  }
              }
          }
          /// @dev Returns a concatenated string of `a` and `b`.
          /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
          function concat(string memory a, string memory b)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let w := not(0x1f)
                  let aLen := mload(a)
                  // Copy `a` one word at a time, backwards.
                  for { let o := and(add(aLen, 0x20), w) } 1 {} {
                      mstore(add(result, o), mload(add(a, o)))
                      o := add(o, w) // `sub(o, 0x20)`.
                      if iszero(o) { break }
                  }
                  let bLen := mload(b)
                  let output := add(result, aLen)
                  // Copy `b` one word at a time, backwards.
                  for { let o := and(add(bLen, 0x20), w) } 1 {} {
                      mstore(add(output, o), mload(add(b, o)))
                      o := add(o, w) // `sub(o, 0x20)`.
                      if iszero(o) { break }
                  }
                  let totalLen := add(aLen, bLen)
                  let last := add(add(result, 0x20), totalLen)
                  mstore(last, 0) // Zeroize the slot after the string.
                  mstore(result, totalLen) // Store the length.
                  mstore(0x40, add(last, 0x20)) // Allocate memory.
              }
          }
          /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function toCase(string memory subject, bool toUpper)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let n := mload(subject)
                  if n {
                      result := mload(0x40)
                      let o := add(result, 0x20)
                      let d := sub(subject, result)
                      let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                      for { let end := add(o, n) } 1 {} {
                          let b := byte(0, mload(add(d, o)))
                          mstore8(o, xor(and(shr(b, flags), 0x20), b))
                          o := add(o, 1)
                          if eq(o, end) { break }
                      }
                      mstore(result, n) // Store the length.
                      mstore(o, 0) // Zeroize the slot after the string.
                      mstore(0x40, add(o, 0x20)) // Allocate memory.
                  }
              }
          }
          /// @dev Returns a string from a small bytes32 string.
          /// `s` must be null-terminated, or behavior will be undefined.
          function fromSmallString(bytes32 s) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let n := 0
                  for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\\0'.
                  mstore(result, n) // Store the length.
                  let o := add(result, 0x20)
                  mstore(o, s) // Store the bytes of the string.
                  mstore(add(o, n), 0) // Zeroize the slot after the string.
                  mstore(0x40, add(result, 0x40)) // Allocate memory.
              }
          }
          /// @dev Returns the small string, with all bytes after the first null byte zeroized.
          function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\\0'.
                  mstore(0x00, s)
                  mstore(result, 0x00)
                  result := mload(0x00)
              }
          }
          /// @dev Returns the string as a normalized null-terminated small string.
          function toSmallString(string memory s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(s)
                  if iszero(lt(result, 33)) {
                      mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                      revert(0x1c, 0x04)
                  }
                  result := shl(shl(3, sub(32, result)), mload(add(s, result)))
              }
          }
          /// @dev Returns a lowercased copy of the string.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function lower(string memory subject) internal pure returns (string memory result) {
              result = toCase(subject, false);
          }
          /// @dev Returns an UPPERCASED copy of the string.
          /// WARNING! This function is only compatible with 7-bit ASCII strings.
          function upper(string memory subject) internal pure returns (string memory result) {
              result = toCase(subject, true);
          }
          /// @dev Escapes the string to be used within HTML tags.
          function escapeHTML(string memory s) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let end := add(s, mload(s))
                  let o := add(result, 0x20)
                  // Store the bytes of the packed offsets and strides into the scratch space.
                  // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
                  mstore(0x1f, 0x900094)
                  mstore(0x08, 0xc0000000a6ab)
                  // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
                  mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
                  for {} iszero(eq(s, end)) {} {
                      s := add(s, 1)
                      let c := and(mload(s), 0xff)
                      // Not in `["\\"","'","&","<",">"]`.
                      if iszero(and(shl(c, 1), 0x500000c400000000)) {
                          mstore8(o, c)
                          o := add(o, 1)
                          continue
                      }
                      let t := shr(248, mload(c))
                      mstore(o, mload(and(t, 0x1f)))
                      o := add(o, shr(5, t))
                  }
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(result, sub(o, add(result, 0x20))) // Store the length.
                  mstore(0x40, add(o, 0x20)) // Allocate memory.
              }
          }
          /// @dev Escapes the string to be used within double-quotes in a JSON.
          /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
          function escapeJSON(string memory s, bool addDoubleQuotes)
              internal
              pure
              returns (string memory result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  let o := add(result, 0x20)
                  if addDoubleQuotes {
                      mstore8(o, 34)
                      o := add(1, o)
                  }
                  // Store "\\\\u0000" in scratch space.
                  // Store "0123456789abcdef" in scratch space.
                  // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
                  // into the scratch space.
                  mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
                  // Bitmask for detecting `["\\"","\\\\"]`.
                  let e := or(shl(0x22, 1), shl(0x5c, 1))
                  for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                      s := add(s, 1)
                      let c := and(mload(s), 0xff)
                      if iszero(lt(c, 0x20)) {
                          if iszero(and(shl(c, 1), e)) {
                              // Not in `["\\"","\\\\"]`.
                              mstore8(o, c)
                              o := add(o, 1)
                              continue
                          }
                          mstore8(o, 0x5c) // "\\\\".
                          mstore8(add(o, 1), c)
                          o := add(o, 2)
                          continue
                      }
                      if iszero(and(shl(c, 1), 0x3700)) {
                          // Not in `["\\b","\\t","\
      ","\\f","\\d"]`.
                          mstore8(0x1d, mload(shr(4, c))) // Hex value.
                          mstore8(0x1e, mload(and(c, 15))) // Hex value.
                          mstore(o, mload(0x19)) // "\\\\u00XX".
                          o := add(o, 6)
                          continue
                      }
                      mstore8(o, 0x5c) // "\\\\".
                      mstore8(add(o, 1), mload(add(c, 8)))
                      o := add(o, 2)
                  }
                  if addDoubleQuotes {
                      mstore8(o, 34)
                      o := add(1, o)
                  }
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(result, sub(o, add(result, 0x20))) // Store the length.
                  mstore(0x40, add(o, 0x20)) // Allocate memory.
              }
          }
          /// @dev Escapes the string to be used within double-quotes in a JSON.
          function escapeJSON(string memory s) internal pure returns (string memory result) {
              result = escapeJSON(s, false);
          }
          /// @dev Encodes `s` so that it can be safely used in a URI,
          /// just like `encodeURIComponent` in JavaScript.
          /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
          /// See: https://datatracker.ietf.org/doc/html/rfc2396
          /// See: https://datatracker.ietf.org/doc/html/rfc3986
          function encodeURIComponent(string memory s) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40)
                  // Store "0123456789ABCDEF" in scratch space.
                  // Uppercased to be consistent with JavaScript's implementation.
                  mstore(0x0f, 0x30313233343536373839414243444546)
                  let o := add(result, 0x20)
                  for { let end := add(s, mload(s)) } iszero(eq(s, end)) {} {
                      s := add(s, 1)
                      let c := and(mload(s), 0xff)
                      // If not in `[0-9A-Z-a-z-_.!~*'()]`.
                      if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) {
                          mstore8(o, 0x25) // '%'.
                          mstore8(add(o, 1), mload(and(shr(4, c), 15)))
                          mstore8(add(o, 2), mload(and(c, 15)))
                          o := add(o, 3)
                          continue
                      }
                      mstore8(o, c)
                      o := add(o, 1)
                  }
                  mstore(result, sub(o, add(result, 0x20))) // Store the length.
                  mstore(o, 0) // Zeroize the slot after the string.
                  mstore(0x40, add(o, 0x20)) // Allocate memory.
              }
          }
          /// @dev Returns whether `a` equals `b`.
          function eq(string memory a, string memory b) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
              }
          }
          /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
          function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // These should be evaluated on compile time, as far as possible.
                  let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
                  let x := not(or(m, or(b, add(m, and(b, m)))))
                  let r := shl(7, iszero(iszero(shr(128, x))))
                  r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
                  r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
                  r := or(r, shl(4, lt(0xffff, shr(r, x))))
                  r := or(r, shl(3, lt(0xff, shr(r, x))))
                  // forgefmt: disable-next-item
                  result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                      xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
              }
          }
          /// @dev Packs a single string with its length into a single word.
          /// Returns `bytes32(0)` if the length is zero or greater than 31.
          function packOne(string memory a) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  // We don't need to zero right pad the string,
                  // since this is our own custom non-standard packing scheme.
                  result :=
                      mul(
                          // Load the length and the bytes.
                          mload(add(a, 0x1f)),
                          // `length != 0 && length < 32`. Abuses underflow.
                          // Assumes that the length is valid and within the block gas limit.
                          lt(sub(mload(a), 1), 0x1f)
                      )
              }
          }
          /// @dev Unpacks a string packed using {packOne}.
          /// Returns the empty string if `packed` is `bytes32(0)`.
          /// If `packed` is not an output of {packOne}, the output behavior is undefined.
          function unpackOne(bytes32 packed) internal pure returns (string memory result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := mload(0x40) // Grab the free memory pointer.
                  mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes).
                  mstore(result, 0) // Zeroize the length slot.
                  mstore(add(result, 0x1f), packed) // Store the length and bytes.
                  mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes.
              }
          }
          /// @dev Packs two strings with their lengths into a single word.
          /// Returns `bytes32(0)` if combined length is zero or greater than 30.
          function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let aLen := mload(a)
                  // We don't need to zero right pad the strings,
                  // since this is our own custom non-standard packing scheme.
                  result :=
                      mul(
                          or( // Load the length and the bytes of `a` and `b`.
                          shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))),
                          // `totalLen != 0 && totalLen < 31`. Abuses underflow.
                          // Assumes that the lengths are valid and within the block gas limit.
                          lt(sub(add(aLen, mload(b)), 1), 0x1e)
                      )
              }
          }
          /// @dev Unpacks strings packed using {packTwo}.
          /// Returns the empty strings if `packed` is `bytes32(0)`.
          /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
          function unpackTwo(bytes32 packed)
              internal
              pure
              returns (string memory resultA, string memory resultB)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  resultA := mload(0x40) // Grab the free memory pointer.
                  resultB := add(resultA, 0x40)
                  // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
                  mstore(0x40, add(resultB, 0x40))
                  // Zeroize the length slots.
                  mstore(resultA, 0)
                  mstore(resultB, 0)
                  // Store the lengths and bytes.
                  mstore(add(resultA, 0x1f), packed)
                  mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
                  // Right pad with zeroes.
                  mstore(add(add(resultA, 0x20), mload(resultA)), 0)
                  mstore(add(add(resultB, 0x20), mload(resultB)), 0)
              }
          }
          /// @dev Directly returns `a` without copying.
          function directReturn(string memory a) internal pure {
              assembly {
                  // Assumes that the string does not start from the scratch space.
                  let retStart := sub(a, 0x20)
                  let retUnpaddedSize := add(mload(a), 0x40)
                  // Right pad with zeroes. Just in case the string is produced
                  // by a method that doesn't zero right pad.
                  mstore(add(retStart, retUnpaddedSize), 0)
                  mstore(retStart, 0x20) // Store the return offset.
                  // End the transaction, returning the string.
                  return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
      library MerkleProofLib {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*            MERKLE PROOF VERIFICATION OPERATIONS            */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
          function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
              internal
              pure
              returns (bool isValid)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if mload(proof) {
                      // Initialize `offset` to the offset of `proof` elements in memory.
                      let offset := add(proof, 0x20)
                      // Left shift by 5 is equivalent to multiplying by 0x20.
                      let end := add(offset, shl(5, mload(proof)))
                      // Iterate over proof elements to compute root hash.
                      for {} 1 {} {
                          // Slot of `leaf` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(leaf, mload(offset)))
                          // Store elements to hash contiguously in scratch space.
                          // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                          mstore(scratch, leaf)
                          mstore(xor(scratch, 0x20), mload(offset))
                          // Reuse `leaf` to store the hash to reduce stack operations.
                          leaf := keccak256(0x00, 0x40)
                          offset := add(offset, 0x20)
                          if iszero(lt(offset, end)) { break }
                      }
                  }
                  isValid := eq(leaf, root)
              }
          }
          /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
          function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
              internal
              pure
              returns (bool isValid)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if proof.length {
                      // Left shift by 5 is equivalent to multiplying by 0x20.
                      let end := add(proof.offset, shl(5, proof.length))
                      // Initialize `offset` to the offset of `proof` in the calldata.
                      let offset := proof.offset
                      // Iterate over proof elements to compute root hash.
                      for {} 1 {} {
                          // Slot of `leaf` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(leaf, calldataload(offset)))
                          // Store elements to hash contiguously in scratch space.
                          // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                          mstore(scratch, leaf)
                          mstore(xor(scratch, 0x20), calldataload(offset))
                          // Reuse `leaf` to store the hash to reduce stack operations.
                          leaf := keccak256(0x00, 0x40)
                          offset := add(offset, 0x20)
                          if iszero(lt(offset, end)) { break }
                      }
                  }
                  isValid := eq(leaf, root)
              }
          }
          /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
          /// given `proof` and `flags`.
          ///
          /// Note:
          /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
          ///   will always return false.
          /// - The sum of the lengths of `proof` and `leaves` must never overflow.
          /// - Any non-zero word in the `flags` array is treated as true.
          /// - The memory offset of `proof` must be non-zero
          ///   (i.e. `proof` is not pointing to the scratch space).
          function verifyMultiProof(
              bytes32[] memory proof,
              bytes32 root,
              bytes32[] memory leaves,
              bool[] memory flags
          ) internal pure returns (bool isValid) {
              // Rebuilds the root by consuming and producing values on a queue.
              // The queue starts with the `leaves` array, and goes into a `hashes` array.
              // After the process, the last element on the queue is verified
              // to be equal to the `root`.
              //
              // The `flags` array denotes whether the sibling
              // should be popped from the queue (`flag == true`), or
              // should be popped from the `proof` (`flag == false`).
              /// @solidity memory-safe-assembly
              assembly {
                  // Cache the lengths of the arrays.
                  let leavesLength := mload(leaves)
                  let proofLength := mload(proof)
                  let flagsLength := mload(flags)
                  // Advance the pointers of the arrays to point to the data.
                  leaves := add(0x20, leaves)
                  proof := add(0x20, proof)
                  flags := add(0x20, flags)
                  // If the number of flags is correct.
                  for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                      // For the case where `proof.length + leaves.length == 1`.
                      if iszero(flagsLength) {
                          // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                          isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                          break
                      }
                      // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                      let proofEnd := add(proof, shl(5, proofLength))
                      // We can use the free memory space for the queue.
                      // We don't need to allocate, since the queue is temporary.
                      let hashesFront := mload(0x40)
                      // Copy the leaves into the hashes.
                      // Sometimes, a little memory expansion costs less than branching.
                      // Should cost less, even with a high free memory offset of 0x7d00.
                      leavesLength := shl(5, leavesLength)
                      for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                          mstore(add(hashesFront, i), mload(add(leaves, i)))
                      }
                      // Compute the back of the hashes.
                      let hashesBack := add(hashesFront, leavesLength)
                      // This is the end of the memory for the queue.
                      // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                      flagsLength := add(hashesBack, shl(5, flagsLength))
                      for {} 1 {} {
                          // Pop from `hashes`.
                          let a := mload(hashesFront)
                          // Pop from `hashes`.
                          let b := mload(add(hashesFront, 0x20))
                          hashesFront := add(hashesFront, 0x40)
                          // If the flag is false, load the next proof,
                          // else, pops from the queue.
                          if iszero(mload(flags)) {
                              // Loads the next proof.
                              b := mload(proof)
                              proof := add(proof, 0x20)
                              // Unpop from `hashes`.
                              hashesFront := sub(hashesFront, 0x20)
                          }
                          // Advance to the next flag.
                          flags := add(flags, 0x20)
                          // Slot of `a` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(a, b))
                          // Hash the scratch space and push the result onto the queue.
                          mstore(scratch, a)
                          mstore(xor(scratch, 0x20), b)
                          mstore(hashesBack, keccak256(0x00, 0x40))
                          hashesBack := add(hashesBack, 0x20)
                          if iszero(lt(hashesBack, flagsLength)) { break }
                      }
                      isValid :=
                          and(
                              // Checks if the last value in the queue is same as the root.
                              eq(mload(sub(hashesBack, 0x20)), root),
                              // And whether all the proofs are used, if required.
                              eq(proofEnd, proof)
                          )
                      break
                  }
              }
          }
          /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
          /// given `proof` and `flags`.
          ///
          /// Note:
          /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
          ///   will always return false.
          /// - Any non-zero word in the `flags` array is treated as true.
          /// - The calldata offset of `proof` must be non-zero
          ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
          function verifyMultiProofCalldata(
              bytes32[] calldata proof,
              bytes32 root,
              bytes32[] calldata leaves,
              bool[] calldata flags
          ) internal pure returns (bool isValid) {
              // Rebuilds the root by consuming and producing values on a queue.
              // The queue starts with the `leaves` array, and goes into a `hashes` array.
              // After the process, the last element on the queue is verified
              // to be equal to the `root`.
              //
              // The `flags` array denotes whether the sibling
              // should be popped from the queue (`flag == true`), or
              // should be popped from the `proof` (`flag == false`).
              /// @solidity memory-safe-assembly
              assembly {
                  // If the number of flags is correct.
                  for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                      // For the case where `proof.length + leaves.length == 1`.
                      if iszero(flags.length) {
                          // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                          // forgefmt: disable-next-item
                          isValid := eq(
                              calldataload(
                                  xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                              ),
                              root
                          )
                          break
                      }
                      // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                      let proofEnd := add(proof.offset, shl(5, proof.length))
                      // We can use the free memory space for the queue.
                      // We don't need to allocate, since the queue is temporary.
                      let hashesFront := mload(0x40)
                      // Copy the leaves into the hashes.
                      // Sometimes, a little memory expansion costs less than branching.
                      // Should cost less, even with a high free memory offset of 0x7d00.
                      calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                      // Compute the back of the hashes.
                      let hashesBack := add(hashesFront, shl(5, leaves.length))
                      // This is the end of the memory for the queue.
                      // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                      flags.length := add(hashesBack, shl(5, flags.length))
                      // We don't need to make a copy of `proof.offset` or `flags.offset`,
                      // as they are pass-by-value (this trick may not always save gas).
                      for {} 1 {} {
                          // Pop from `hashes`.
                          let a := mload(hashesFront)
                          // Pop from `hashes`.
                          let b := mload(add(hashesFront, 0x20))
                          hashesFront := add(hashesFront, 0x40)
                          // If the flag is false, load the next proof,
                          // else, pops from the queue.
                          if iszero(calldataload(flags.offset)) {
                              // Loads the next proof.
                              b := calldataload(proof.offset)
                              proof.offset := add(proof.offset, 0x20)
                              // Unpop from `hashes`.
                              hashesFront := sub(hashesFront, 0x20)
                          }
                          // Advance to the next flag offset.
                          flags.offset := add(flags.offset, 0x20)
                          // Slot of `a` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(a, b))
                          // Hash the scratch space and push the result onto the queue.
                          mstore(scratch, a)
                          mstore(xor(scratch, 0x20), b)
                          mstore(hashesBack, keccak256(0x00, 0x40))
                          hashesBack := add(hashesBack, 0x20)
                          if iszero(lt(hashesBack, flags.length)) { break }
                      }
                      isValid :=
                          and(
                              // Checks if the last value in the queue is same as the root.
                              eq(mload(sub(hashesBack, 0x20)), root),
                              // And whether all the proofs are used, if required.
                              eq(proofEnd, proof.offset)
                          )
                      break
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   EMPTY CALLDATA HELPERS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an empty calldata bytes32 array.
          function emptyProof() internal pure returns (bytes32[] calldata proof) {
              /// @solidity memory-safe-assembly
              assembly {
                  proof.length := 0
              }
          }
          /// @dev Returns an empty calldata bytes32 array.
          function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
              /// @solidity memory-safe-assembly
              assembly {
                  leaves.length := 0
              }
          }
          /// @dev Returns an empty calldata bool array.
          function emptyFlags() internal pure returns (bool[] calldata flags) {
              /// @solidity memory-safe-assembly
              assembly {
                  flags.length := 0
              }
          }
      }
      

      File 2 of 2: ArchetypeLogicErc721a
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.9.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.9.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 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
      // ArchetypePayouts v0.7.0
      //
      //        d8888                 888               888
      //       d88888                 888               888
      //      d88P888                 888               888
      //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
      //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
      //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
      //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
      // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
      //                                                            888 888
      //                                                       Y8b d88P 888
      //
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      error InvalidLength();
      error InvalidSplitShares();
      error TransferFailed();
      error BalanceEmpty();
      error NotApprovedToWithdraw();
      contract ArchetypePayouts {
        event Withdrawal(address indexed src, address token, uint256 wad);
        event FundsAdded(address indexed recipient, address token, uint256 amount);
        mapping(address => mapping(address => uint256)) private _balance;
        mapping(address => mapping(address => bool)) private _approvals;
        function updateBalances(
          uint256 totalAmount,
          address token,
          address[] calldata recipients,
          uint16[] calldata splits
        ) public payable {
          if (recipients.length != splits.length) {
            revert InvalidLength();
          }
          uint256 totalShares = 0;
          for (uint256 i = 0; i < splits.length; i++) {
            totalShares += splits[i];
          }
          if (totalShares != 10000) {
            revert InvalidSplitShares();
          }
          if (token == address(0)) {
            // ETH payments
            uint256 totalReceived = msg.value;
            for (uint256 i = 0; i < recipients.length; i++) {
              if (splits[i] > 0) {
                uint256 amountToAdd = (totalReceived * splits[i]) / 10000;
                _balance[recipients[i]][token] += amountToAdd;
                emit FundsAdded(recipients[i], token, amountToAdd);
              }
            }
          } else {
            // ERC20 payments
            IERC20 paymentToken = IERC20(token);
            bool success = paymentToken.transferFrom(msg.sender, address(this), totalAmount);
            if (!success) {
              revert TransferFailed();
            }
            for (uint256 i = 0; i < recipients.length; i++) {
              if (splits[i] > 0) {
                uint256 amountToAdd = (totalAmount * splits[i]) / 10000;
                _balance[recipients[i]][token] += amountToAdd;
                emit FundsAdded(recipients[i], token, amountToAdd);
              }
            }
          }
        }
        function withdraw() external {
          address msgSender = msg.sender;
          _withdraw(msgSender, msgSender, address(0));
        }
        function withdrawTokens(address[] memory tokens) external {
          address msgSender = msg.sender;
          for (uint256 i = 0; i < tokens.length; i++) {
            _withdraw(msgSender, msgSender, tokens[i]);
          }
        }
        function withdrawFrom(address from, address to) public {
          if (from != msg.sender && !_approvals[from][to]) {
            revert NotApprovedToWithdraw();
          }
          _withdraw(from, to, address(0));
        }
        function withdrawTokensFrom(
          address from,
          address to,
          address[] memory tokens
        ) public {
          if (from != msg.sender && !_approvals[from][to]) {
            revert NotApprovedToWithdraw();
          }
          for (uint256 i = 0; i < tokens.length; i++) {
            _withdraw(from, to, tokens[i]);
          }
        }
        function _withdraw(
          address from,
          address to,
          address token
        ) internal {
          uint256 wad;
          wad = _balance[from][token];
          _balance[from][token] = 0;
          if (wad == 0) {
            revert BalanceEmpty();
          }
          if (token == address(0)) {
            bool success = false;
            (success, ) = to.call{ value: wad }("");
            if (!success) {
              revert TransferFailed();
            }
          } else {
            IERC20 erc20Token = IERC20(token);
            bool success = erc20Token.transfer(to, wad);
            if (!success) {
              revert TransferFailed();
            }
          }
          emit Withdrawal(from, token, wad);
        }
        function approveWithdrawal(address delegate, bool approved) external {
          _approvals[msg.sender][delegate] = approved;
        }
        function isApproved(address from, address delegate) external view returns (bool) {
          return _approvals[from][delegate];
        }
        function balance(address recipient) external view returns (uint256) {
          return _balance[recipient][address(0)];
        }
        function balanceToken(address recipient, address token) external view returns (uint256) {
          return _balance[recipient][token];
        }
      }
      // SPDX-License-Identifier: MIT
      // ArchetypeLogic v0.8.0
      //
      //        d8888                 888               888
      //       d88888                 888               888
      //      d88P888                 888               888
      //     d88P 888 888d888 .d8888b 88888b.   .d88b.  888888 888  888 88888b.   .d88b.
      //    d88P  888 888P"  d88P"    888 "88b d8P  Y8b 888    888  888 888 "88b d8P  Y8b
      //   d88P   888 888    888      888  888 88888888 888    888  888 888  888 88888888
      //  d8888888888 888    Y88b.    888  888 Y8b.     Y88b.  Y88b 888 888 d88P Y8b.
      // d88P     888 888     "Y8888P 888  888  "Y8888   "Y888  "Y88888 88888P"   "Y8888
      //                                                            888 888
      //                                                       Y8b d88P 888
      //                                                        "Y88P"  888
      pragma solidity ^0.8.20;
      import "../ArchetypePayouts.sol";
      import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "solady/src/utils/MerkleProofLib.sol";
      import "solady/src/utils/ECDSA.sol";
      error InvalidConfig();
      error MintNotYetStarted();
      error MintEnded();
      error WalletUnauthorizedToMint();
      error InsufficientEthSent();
      error ExcessiveEthSent();
      error Erc20BalanceTooLow();
      error MaxSupplyExceeded();
      error ListMaxSupplyExceeded();
      error NumberOfMintsExceeded();
      error MintingPaused();
      error InvalidReferral();
      error InvalidSignature();
      error MaxBatchSizeExceeded();
      error BurnToMintDisabled();
      error NotTokenOwner();
      error NotPlatform();
      error NotOwner();
      error NotShareholder();
      error NotApprovedToTransfer();
      error InvalidAmountOfTokens();
      error WrongPassword();
      error LockedForever();
      error Blacklisted();
      //
      // STRUCTS
      //
      struct Auth {
        bytes32 key;
        bytes32[] proof;
      }
      struct BonusDiscount {
        uint16 numMints;
        uint16 numBonusMints;
      }
      struct Config {
        string baseUri;
        address affiliateSigner;
        uint32 maxSupply;
        uint32 maxBatchSize;
        uint16 affiliateFee; //BPS
        uint16 affiliateDiscount; //BPS
        uint16 defaultRoyalty; //BPS
      }
      // allocation splits for withdrawn owner funds, must sum to 100%
      struct PayoutConfig {
        uint16 ownerBps;
        uint16 platformBps;
        uint16 partnerBps;
        uint16 superAffiliateBps;
        address partner;
        address superAffiliate;
        address ownerAltPayout;
      }
      struct Options {
        bool uriLocked;
        bool maxSupplyLocked;
        bool affiliateFeeLocked;
        bool ownerAltPayoutLocked;
      }
      struct AdvancedInvite {
        uint128 price;
        uint128 reservePrice;
        uint128 delta;
        uint32 start;
        uint32 end;
        uint32 limit;
        uint32 maxSupply;
        uint32 interval;
        uint32 unitSize; // mint 1 get x
        address tokenAddress;
        bool isBlacklist;
      }
      struct Invite {
        uint128 price;
        uint32 start;
        uint32 end;
        uint32 limit;
        uint32 maxSupply;
        uint32 unitSize; // mint 1 get x
        address tokenAddress;
        bool isBlacklist;
      }
      struct BurnInvite {
        IERC721 burnErc721;
        address burnAddress;
        address tokenAddress;
        uint128 price; // flat price - does not support discounts
        bool reversed; // side of the ratio (false=burn {ratio} get 1, true=burn 1 get {ratio})
        uint16 ratio;
        uint32 start;
        uint32 end;
        uint64 limit;
      }
      struct ValidationArgs {
        address owner;
        address affiliate;
        uint256 quantity;
        uint256 curSupply;
        uint256 listSupply;
      }
      // UPDATE CONSTANTS BEFORE DEPLOY
      address constant PLATFORM = 0x8952caF7E5bf1fe63ebe94148ca802F3eF127C98;
      address constant BATCH = 0x6Bc558A6DC48dEfa0e7022713c23D65Ab26e4Fa7;
      address constant PAYOUTS = 0xaAfdfA4a935d8511bF285af11A0544ce7e4a1199;
      uint16 constant MAXBPS = 5000; // max fee or discount is 50%
      uint32 constant UINT32_MAX = 2**32 - 1;
      library ArchetypeLogicErc721a {
        //
        // EVENTS
        //
        event Invited(bytes32 indexed key, bytes32 indexed cid);
        event BurnInvited(bytes32 indexed key, bytes32 indexed cid);
        event Referral(address indexed affiliate, address token, uint128 wad, uint256 numMints);
        event Withdrawal(address indexed src, address token, uint128 wad);
        // calculate price based on affiliate usage and mint discounts
        function computePrice(
          AdvancedInvite storage invite,
          uint16 affiliateDiscount,
          uint256 numTokens,
          uint256 listSupply,
          bool affiliateUsed
        ) public view returns (uint256) {
          uint256 price = invite.price;
          uint256 cost;
          if (invite.interval > 0 && invite.delta > 0) {
            // Apply dutch pricing
            uint256 diff = (((block.timestamp - invite.start) / invite.interval) * invite.delta);
            if (price > invite.reservePrice) {
              if (diff > price - invite.reservePrice) {
                price = invite.reservePrice;
              } else {
                price = price - diff;
              }
            } else if (price < invite.reservePrice) {
              if (diff > invite.reservePrice - price) {
                price = invite.reservePrice;
              } else {
                price = price + diff;
              }
            }
            cost = price * numTokens;
          } else if (invite.interval == 0 && invite.delta > 0) {
            // Apply linear curve
            uint256 lastPrice = price + invite.delta * listSupply;
            cost = lastPrice * numTokens + (invite.delta * numTokens * (numTokens - 1)) / 2;
          } else {
            cost = price * numTokens;
          }
          if (affiliateUsed) {
            cost = cost - ((cost * affiliateDiscount) / 10000);
          }
          return cost;
        }
        function bonusMintsAwarded(uint256 numNfts, uint256 packedDiscount) internal pure returns (uint256) {
          for (uint8 i = 0; i < 8; i++) {
              uint32 discount = uint32((packedDiscount >> (32 * i)) & 0xFFFFFFFF);
              uint16 tierNumMints = uint16(discount >> 16);
              uint16 tierBonusMints = uint16(discount);
              if (tierNumMints == 0) {
                  break; // End of valid discounts
              }
              if (numNfts >= tierNumMints) {
                  return (numNfts / tierNumMints) * tierBonusMints;
              }
          }
          return 0;
        }
        function validateMint(
          AdvancedInvite storage i,
          Config storage config,
          Auth calldata auth,
          mapping(address => mapping(bytes32 => uint256)) storage minted,
          bytes calldata signature,
          ValidationArgs memory args,
          uint128 cost
        ) public view {
          address msgSender = _msgSender();
          if (args.affiliate != address(0)) {
            if (
              args.affiliate == PLATFORM || args.affiliate == args.owner || args.affiliate == msgSender
            ) {
              revert InvalidReferral();
            }
            validateAffiliate(args.affiliate, signature, config.affiliateSigner);
          }
          if (i.limit == 0) {
            revert MintingPaused();
          }
          if (!i.isBlacklist) {
            if (!verify(auth, i.tokenAddress, msgSender)) {
              revert WalletUnauthorizedToMint();
            }
          } else {
            if (verify(auth, i.tokenAddress, msgSender)) {
              revert Blacklisted();
            }
          }
          if (block.timestamp < i.start) {
            revert MintNotYetStarted();
          }
          if (i.end > i.start && block.timestamp > i.end) {
            revert MintEnded();
          }
          if (i.limit < i.maxSupply) {
            uint256 totalAfterMint = minted[msgSender][auth.key] + args.quantity;
            if (totalAfterMint > i.limit) {
              revert NumberOfMintsExceeded();
            }
          }
          if (i.maxSupply < config.maxSupply) {
            uint256 totalAfterMint = args.listSupply + args.quantity;
            if (totalAfterMint > i.maxSupply) {
              revert ListMaxSupplyExceeded();
            }
          }
          if (args.quantity > config.maxBatchSize) {
            revert MaxBatchSizeExceeded();
          }
          if ((args.curSupply + args.quantity) > config.maxSupply) {
            revert MaxSupplyExceeded();
          }
          if (i.tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(i.tokenAddress);
            if (erc20Token.allowance(msgSender, address(this)) < cost) {
              revert NotApprovedToTransfer();
            }
            if (erc20Token.balanceOf(msgSender) < cost) {
              revert Erc20BalanceTooLow();
            }
            if (msg.value != 0) {
              revert ExcessiveEthSent();
            }
          } else {
            if (msg.value < cost) {
              revert InsufficientEthSent();
            }
          }
        }
        function validateBurnToMint(
          BurnInvite storage burnInvite,
          Config storage config,
          Auth calldata auth,
          uint256[] calldata tokenIds,
          uint256 curSupply,
          mapping(address => mapping(bytes32 => uint256)) storage minted,
          uint128 cost
        ) public view {
          if (burnInvite.limit == 0) {
            revert MintingPaused();
          }
          if (block.timestamp < burnInvite.start) {
            revert MintNotYetStarted();
          }
          if (burnInvite.end > burnInvite.start && block.timestamp > burnInvite.end) {
            revert MintEnded();
          }
          // check if msgSender owns tokens and has correct approvals
          address msgSender = _msgSender();
          for (uint256 i; i < tokenIds.length; ) {
            if (burnInvite.burnErc721.ownerOf(tokenIds[i]) != msgSender) {
              revert NotTokenOwner();
            }
            unchecked {
              ++i;
            }
          }
          if (!verify(auth, burnInvite.tokenAddress, msgSender)) {
            revert WalletUnauthorizedToMint();
          }
          if (!burnInvite.burnErc721.isApprovedForAll(msgSender, address(this))) {
            revert NotApprovedToTransfer();
          }
          uint256 quantity;
          if (burnInvite.reversed) {
            quantity = tokenIds.length * burnInvite.ratio;
          } else {
            if (tokenIds.length % burnInvite.ratio != 0) {
              revert InvalidAmountOfTokens();
            }
            quantity = tokenIds.length / burnInvite.ratio;
          }
          if (quantity > config.maxBatchSize) {
            revert MaxBatchSizeExceeded();
          }
          if (burnInvite.limit < config.maxSupply) {
            uint256 totalAfterMint = minted[msgSender][keccak256(abi.encodePacked("burn", auth.key))] +
              quantity;
            if (totalAfterMint > burnInvite.limit) {
              revert NumberOfMintsExceeded();
            }
          }
          if ((curSupply + quantity) > config.maxSupply) {
            revert MaxSupplyExceeded();
          }
          if (burnInvite.tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(burnInvite.tokenAddress);
            if (erc20Token.allowance(msgSender, address(this)) < cost) {
              revert NotApprovedToTransfer();
            }
            if (erc20Token.balanceOf(msgSender) < cost) {
              revert Erc20BalanceTooLow();
            }
            if (msg.value != 0) {
              revert ExcessiveEthSent();
            }
          } else {
            if (msg.value < cost) {
              revert InsufficientEthSent();
            }
          }
        }
        function updateBalances(
          address tokenAddress,
          Config storage config,
          mapping(address => uint128) storage _ownerBalance,
          mapping(address => mapping(address => uint128)) storage _affiliateBalance,
          address affiliate,
          uint256 quantity,
          uint128 value
        ) public {
          uint128 affiliateWad;
          if (affiliate != address(0)) {
            affiliateWad = (value * config.affiliateFee) / 10000;
            _affiliateBalance[affiliate][tokenAddress] += affiliateWad;
            emit Referral(affiliate, tokenAddress, affiliateWad, quantity);
          }
          uint128 balance = _ownerBalance[tokenAddress];
          uint128 ownerWad = value - affiliateWad;
          _ownerBalance[tokenAddress] = balance + ownerWad;
          if (tokenAddress != address(0)) {
            IERC20 erc20Token = IERC20(tokenAddress);
            bool success = erc20Token.transferFrom(_msgSender(), address(this), value);
            if (!success) {
              revert TransferFailed();
            }
          }
        }
        function withdrawTokensAffiliate(
          mapping(address => mapping(address => uint128)) storage _affiliateBalance,
          address[] calldata tokens
        ) public {
          address msgSender = _msgSender();
          for (uint256 i; i < tokens.length; i++) {
            address tokenAddress = tokens[i];
            uint128 wad = _affiliateBalance[msgSender][tokenAddress];
            _affiliateBalance[msgSender][tokenAddress] = 0;
            if (wad == 0) {
              revert BalanceEmpty();
            }
            if (tokenAddress == address(0)) {
              bool success = false;
              (success, ) = msgSender.call{ value: wad }("");
              if (!success) {
                revert TransferFailed();
              }
            } else {
              IERC20 erc20Token = IERC20(tokenAddress);
              bool success = erc20Token.transfer(msgSender, wad);
              if (!success) {
                revert TransferFailed();
              }
            }
            emit Withdrawal(msgSender, tokenAddress, wad);
          }
        }
        function withdrawTokens(
          PayoutConfig storage payoutConfig,
          mapping(address => uint128) storage _ownerBalance,
          address owner,
          address[] calldata tokens
        ) public {
          address msgSender = _msgSender();
          for (uint256 i; i < tokens.length; i++) {
            address tokenAddress = tokens[i];
            uint128 wad;
            if (
              msgSender == owner ||
              msgSender == PLATFORM ||
              msgSender == payoutConfig.partner ||
              msgSender == payoutConfig.superAffiliate ||
              msgSender == payoutConfig.ownerAltPayout
            ) {
              wad = _ownerBalance[tokenAddress];
              _ownerBalance[tokenAddress] = 0;
            } else {
              revert NotShareholder();
            }
            if (wad == 0) {
              revert BalanceEmpty();
            }
            if (payoutConfig.ownerAltPayout == address(0)) {
              address[] memory recipients = new address[](4);
              recipients[0] = owner;
              recipients[1] = PLATFORM;
              recipients[2] = payoutConfig.partner;
              recipients[3] = payoutConfig.superAffiliate;
              uint16[] memory splits = new uint16[](4);
              splits[0] = payoutConfig.ownerBps;
              splits[1] = payoutConfig.platformBps;
              splits[2] = payoutConfig.partnerBps;
              splits[3] = payoutConfig.superAffiliateBps;
              if (tokenAddress == address(0)) {
                ArchetypePayouts(PAYOUTS).updateBalances{ value: wad }(
                  wad,
                  tokenAddress,
                  recipients,
                  splits
                );
              } else {
                ArchetypePayouts(PAYOUTS).updateBalances(wad, tokenAddress, recipients, splits);
              }
            } else {
              uint256 ownerShare = (uint256(wad) * payoutConfig.ownerBps) / 10000;
              uint256 remainingShare = wad - ownerShare;
              if (tokenAddress == address(0)) {
                (bool success, ) = payable(payoutConfig.ownerAltPayout).call{ value: ownerShare }("");
                if (!success) revert TransferFailed();
              } else {
                IERC20(tokenAddress).transfer(payoutConfig.ownerAltPayout, ownerShare);
              }
              address[] memory recipients = new address[](3);
              recipients[0] = PLATFORM;
              recipients[1] = payoutConfig.partner;
              recipients[2] = payoutConfig.superAffiliate;
              uint16[] memory splits = new uint16[](3);
              uint16 remainingBps = 10000 - payoutConfig.ownerBps;
              splits[1] = uint16((uint256(payoutConfig.partnerBps) * 10000) / remainingBps);
              splits[2] = uint16((uint256(payoutConfig.superAffiliateBps) * 10000) / remainingBps);
              splits[0] = 10000 - splits[1] - splits[2];
              if (tokenAddress == address(0)) {
                ArchetypePayouts(PAYOUTS).updateBalances{ value: remainingShare }(
                  remainingShare,
                  tokenAddress,
                  recipients,
                  splits
                );
              } else {
                ArchetypePayouts(PAYOUTS).updateBalances(
                  remainingShare,
                  tokenAddress,
                  recipients,
                  splits
                );
              }
            }
            emit Withdrawal(msgSender, tokenAddress, wad);
          }
        }
        function validateAffiliate(
          address affiliate,
          bytes calldata signature,
          address affiliateSigner
        ) public view {
          bytes32 signedMessagehash = ECDSA.toEthSignedMessageHash(
            keccak256(abi.encodePacked(affiliate))
          );
          address signer = ECDSA.recover(signedMessagehash, signature);
          if (signer != affiliateSigner) {
            revert InvalidSignature();
          }
        }
        function verify(
          Auth calldata auth,
          address tokenAddress,
          address account
        ) public pure returns (bool) {
          // keys 0-255 and tokenAddress are public
          if (uint256(auth.key) <= 0xff || auth.key == keccak256(abi.encodePacked(tokenAddress))) {
            return true;
          }
          return MerkleProofLib.verify(auth.proof, auth.key, keccak256(abi.encodePacked(account)));
        }
        function _msgSender() internal view returns (address) {
          return msg.sender == BATCH ? tx.origin : msg.sender;
        }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Gas optimized ECDSA wrapper.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
      ///
      /// @dev Note:
      /// - The recovery functions use the ecrecover precompile (0x1).
      /// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
      ///   This is for more safety by default.
      ///   Use the `tryRecover` variants if you need to get the zero address back
      ///   upon recovery failure instead.
      /// - As of Solady version 0.0.134, all `bytes signature` variants accept both
      ///   regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
      ///   See: https://eips.ethereum.org/EIPS/eip-2098
      ///   This is for calldata efficiency on smart accounts prevalent on L2s.
      ///
      /// WARNING! Do NOT directly use signatures as unique identifiers:
      /// - The recovery operations do NOT check if a signature is non-malleable.
      /// - Use a nonce in the digest to prevent replay attacks on the same contract.
      /// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
      ///   EIP-712 also enables readable signing of typed data for better user safety.
      /// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
      library ECDSA {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                         CONSTANTS                          */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The order of the secp256k1 elliptic curve.
          uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
          /// @dev `N/2 + 1`. Used for checking the malleability of the signature.
          uint256 private constant _HALF_N_PLUS_1 =
              0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                        CUSTOM ERRORS                       */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev The signature is invalid.
          error InvalidSignature();
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                    RECOVERY OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function recoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              result, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the EIP-2098 short form signature defined by `r` and `vs`.
          function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                  mstore(0x40, r)
                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the signature defined by `v`, `r`, `s`.
          function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, and(v, 0xff))
                  mstore(0x40, r)
                  mstore(0x60, s)
                  result :=
                      mload(
                          staticcall(
                              gas(), // Amount of gas left for the transaction.
                              1, // Address of `ecrecover`.
                              0x00, // Start of input.
                              0x80, // Size of input.
                              0x01, // Start of output.
                              0x20 // Size of output.
                          )
                      )
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  if iszero(returndatasize()) {
                      mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
                      revert(0x1c, 0x04)
                  }
                  mstore(0x60, 0) // Restore the zero slot.
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   TRY-RECOVER OPERATIONS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // WARNING!
          // These functions will NOT revert upon recovery failure.
          // Instead, they will return the zero address upon recovery failure.
          // It is critical that the returned address is NEVER compared against
          // a zero address (e.g. an uninitialized address variable).
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function tryRecover(bytes32 hash, bytes memory signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  for {} 1 {} {
                      mstore(0x00, hash)
                      mstore(0x40, mload(add(signature, 0x20))) // `r`.
                      if eq(mload(signature), 64) {
                          let vs := mload(add(signature, 0x40))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(mload(signature), 65) {
                          mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
                          mstore(0x60, mload(add(signature, 0x40))) // `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
          function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  result := 1
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  for {} 1 {} {
                      if eq(signature.length, 64) {
                          let vs := calldataload(add(signature.offset, 0x20))
                          mstore(0x20, add(shr(255, vs), 27)) // `v`.
                          mstore(0x40, calldataload(signature.offset)) // `r`.
                          mstore(0x60, shr(1, shl(1, vs))) // `s`.
                          break
                      }
                      if eq(signature.length, 65) {
                          mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
                          calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
                          break
                      }
                      result := 0
                      break
                  }
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          result, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the EIP-2098 short form signature defined by `r` and `vs`.
          function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, add(shr(255, vs), 27)) // `v`.
                  mstore(0x40, r)
                  mstore(0x60, shr(1, shl(1, vs))) // `s`.
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          1, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /// @dev Recovers the signer's address from a message digest `hash`,
          /// and the signature defined by `v`, `r`, `s`.
          function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
              internal
              view
              returns (address result)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  let m := mload(0x40) // Cache the free memory pointer.
                  mstore(0x00, hash)
                  mstore(0x20, and(v, 0xff))
                  mstore(0x40, r)
                  mstore(0x60, s)
                  pop(
                      staticcall(
                          gas(), // Amount of gas left for the transaction.
                          1, // Address of `ecrecover`.
                          0x00, // Start of input.
                          0x80, // Size of input.
                          0x40, // Start of output.
                          0x20 // Size of output.
                      )
                  )
                  mstore(0x60, 0) // Restore the zero slot.
                  // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
                  result := mload(xor(0x60, returndatasize()))
                  mstore(0x40, m) // Restore the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                     HASHING OPERATIONS                     */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an Ethereum Signed Message, created from a `hash`.
          /// This produces a hash corresponding to the one signed with the
          /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
          /// JSON-RPC method as part of EIP-191.
          function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  mstore(0x20, hash) // Store into scratch space for keccak256.
                  mstore(0x00, "\\x00\\x00\\x00\\x00\\x19Ethereum Signed Message:\
      32") // 28 bytes.
                  result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
              }
          }
          /// @dev Returns an Ethereum Signed Message, created from `s`.
          /// This produces a hash corresponding to the one signed with the
          /// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
          /// JSON-RPC method as part of EIP-191.
          /// Note: Supports lengths of `s` up to 999999 bytes.
          function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
              /// @solidity memory-safe-assembly
              assembly {
                  let sLength := mload(s)
                  let o := 0x20
                  mstore(o, "\\x19Ethereum Signed Message:\
      ") // 26 bytes, zero-right-padded.
                  mstore(0x00, 0x00)
                  // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
                  for { let temp := sLength } 1 {} {
                      o := sub(o, 1)
                      mstore8(o, add(48, mod(temp, 10)))
                      temp := div(temp, 10)
                      if iszero(temp) { break }
                  }
                  let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
                  // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
                  returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
                  mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
                  result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
                  mstore(s, sLength) // Restore the length.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                  CANONICAL HASH FUNCTIONS                  */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          // The following functions returns the hash of the signature in it's canonicalized format,
          // which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
          // If `s` is greater than `N / 2` then it will be converted to `N - s`
          // and the `v` value will be flipped.
          // If the signature has an invalid length, or if `v` is invalid,
          // a uniquely corrupt hash will be returned.
          // These functions are useful for "poor-mans-VRF".
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  let l := mload(signature)
                  for {} 1 {} {
                      mstore(0x00, mload(add(signature, 0x20))) // `r`.
                      let s := mload(add(signature, 0x40))
                      let v := mload(add(signature, 0x41))
                      if eq(l, 64) {
                          v := add(shr(255, s), 27)
                          s := shr(1, shl(1, s))
                      }
                      if iszero(lt(s, _HALF_N_PLUS_1)) {
                          v := xor(v, 7)
                          s := sub(N, s)
                      }
                      mstore(0x21, v)
                      mstore(0x20, s)
                      result := keccak256(0x00, 0x41)
                      mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                      break
                  }
                  // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                  if iszero(lt(sub(l, 64), 2)) {
                      // `bytes4(keccak256("InvalidSignatureLength"))`.
                      result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
                  }
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHashCalldata(bytes calldata signature)
              internal
              pure
              returns (bytes32 result)
          {
              // @solidity memory-safe-assembly
              assembly {
                  let l := signature.length
                  for {} 1 {} {
                      mstore(0x00, calldataload(signature.offset)) // `r`.
                      let s := calldataload(add(signature.offset, 0x20))
                      let v := calldataload(add(signature.offset, 0x21))
                      if eq(l, 64) {
                          v := add(shr(255, s), 27)
                          s := shr(1, shl(1, s))
                      }
                      if iszero(lt(s, _HALF_N_PLUS_1)) {
                          v := xor(v, 7)
                          s := sub(N, s)
                      }
                      mstore(0x21, v)
                      mstore(0x20, s)
                      result := keccak256(0x00, 0x41)
                      mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
                      break
                  }
                  // If the length is neither 64 nor 65, return a uniquely corrupted hash.
                  if iszero(lt(sub(l, 64), 2)) {
                      calldatacopy(mload(0x40), signature.offset, l)
                      // `bytes4(keccak256("InvalidSignatureLength"))`.
                      result := xor(keccak256(mload(0x40), l), 0xd62f1ab2)
                  }
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, r) // `r`.
                  let v := add(shr(255, vs), 27)
                  let s := shr(1, shl(1, vs))
                  mstore(0x21, v)
                  mstore(0x20, s)
                  result := keccak256(0x00, 0x41)
                  mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
              }
          }
          /// @dev Returns the canonical hash of `signature`.
          function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
              // @solidity memory-safe-assembly
              assembly {
                  mstore(0x00, r) // `r`.
                  if iszero(lt(s, _HALF_N_PLUS_1)) {
                      v := xor(v, 7)
                      s := sub(N, s)
                  }
                  mstore(0x21, v)
                  mstore(0x20, s)
                  result := keccak256(0x00, 0x41)
                  mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   EMPTY CALLDATA HELPERS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an empty calldata bytes.
          function emptySignature() internal pure returns (bytes calldata signature) {
              /// @solidity memory-safe-assembly
              assembly {
                  signature.length := 0
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      /// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
      /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
      /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
      /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
      library MerkleProofLib {
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*            MERKLE PROOF VERIFICATION OPERATIONS            */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
          function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
              internal
              pure
              returns (bool isValid)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if mload(proof) {
                      // Initialize `offset` to the offset of `proof` elements in memory.
                      let offset := add(proof, 0x20)
                      // Left shift by 5 is equivalent to multiplying by 0x20.
                      let end := add(offset, shl(5, mload(proof)))
                      // Iterate over proof elements to compute root hash.
                      for {} 1 {} {
                          // Slot of `leaf` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(leaf, mload(offset)))
                          // Store elements to hash contiguously in scratch space.
                          // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                          mstore(scratch, leaf)
                          mstore(xor(scratch, 0x20), mload(offset))
                          // Reuse `leaf` to store the hash to reduce stack operations.
                          leaf := keccak256(0x00, 0x40)
                          offset := add(offset, 0x20)
                          if iszero(lt(offset, end)) { break }
                      }
                  }
                  isValid := eq(leaf, root)
              }
          }
          /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
          function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
              internal
              pure
              returns (bool isValid)
          {
              /// @solidity memory-safe-assembly
              assembly {
                  if proof.length {
                      // Left shift by 5 is equivalent to multiplying by 0x20.
                      let end := add(proof.offset, shl(5, proof.length))
                      // Initialize `offset` to the offset of `proof` in the calldata.
                      let offset := proof.offset
                      // Iterate over proof elements to compute root hash.
                      for {} 1 {} {
                          // Slot of `leaf` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(leaf, calldataload(offset)))
                          // Store elements to hash contiguously in scratch space.
                          // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
                          mstore(scratch, leaf)
                          mstore(xor(scratch, 0x20), calldataload(offset))
                          // Reuse `leaf` to store the hash to reduce stack operations.
                          leaf := keccak256(0x00, 0x40)
                          offset := add(offset, 0x20)
                          if iszero(lt(offset, end)) { break }
                      }
                  }
                  isValid := eq(leaf, root)
              }
          }
          /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
          /// given `proof` and `flags`.
          ///
          /// Note:
          /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
          ///   will always return false.
          /// - The sum of the lengths of `proof` and `leaves` must never overflow.
          /// - Any non-zero word in the `flags` array is treated as true.
          /// - The memory offset of `proof` must be non-zero
          ///   (i.e. `proof` is not pointing to the scratch space).
          function verifyMultiProof(
              bytes32[] memory proof,
              bytes32 root,
              bytes32[] memory leaves,
              bool[] memory flags
          ) internal pure returns (bool isValid) {
              // Rebuilds the root by consuming and producing values on a queue.
              // The queue starts with the `leaves` array, and goes into a `hashes` array.
              // After the process, the last element on the queue is verified
              // to be equal to the `root`.
              //
              // The `flags` array denotes whether the sibling
              // should be popped from the queue (`flag == true`), or
              // should be popped from the `proof` (`flag == false`).
              /// @solidity memory-safe-assembly
              assembly {
                  // Cache the lengths of the arrays.
                  let leavesLength := mload(leaves)
                  let proofLength := mload(proof)
                  let flagsLength := mload(flags)
                  // Advance the pointers of the arrays to point to the data.
                  leaves := add(0x20, leaves)
                  proof := add(0x20, proof)
                  flags := add(0x20, flags)
                  // If the number of flags is correct.
                  for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
                      // For the case where `proof.length + leaves.length == 1`.
                      if iszero(flagsLength) {
                          // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                          isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
                          break
                      }
                      // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                      let proofEnd := add(proof, shl(5, proofLength))
                      // We can use the free memory space for the queue.
                      // We don't need to allocate, since the queue is temporary.
                      let hashesFront := mload(0x40)
                      // Copy the leaves into the hashes.
                      // Sometimes, a little memory expansion costs less than branching.
                      // Should cost less, even with a high free memory offset of 0x7d00.
                      leavesLength := shl(5, leavesLength)
                      for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
                          mstore(add(hashesFront, i), mload(add(leaves, i)))
                      }
                      // Compute the back of the hashes.
                      let hashesBack := add(hashesFront, leavesLength)
                      // This is the end of the memory for the queue.
                      // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                      flagsLength := add(hashesBack, shl(5, flagsLength))
                      for {} 1 {} {
                          // Pop from `hashes`.
                          let a := mload(hashesFront)
                          // Pop from `hashes`.
                          let b := mload(add(hashesFront, 0x20))
                          hashesFront := add(hashesFront, 0x40)
                          // If the flag is false, load the next proof,
                          // else, pops from the queue.
                          if iszero(mload(flags)) {
                              // Loads the next proof.
                              b := mload(proof)
                              proof := add(proof, 0x20)
                              // Unpop from `hashes`.
                              hashesFront := sub(hashesFront, 0x20)
                          }
                          // Advance to the next flag.
                          flags := add(flags, 0x20)
                          // Slot of `a` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(a, b))
                          // Hash the scratch space and push the result onto the queue.
                          mstore(scratch, a)
                          mstore(xor(scratch, 0x20), b)
                          mstore(hashesBack, keccak256(0x00, 0x40))
                          hashesBack := add(hashesBack, 0x20)
                          if iszero(lt(hashesBack, flagsLength)) { break }
                      }
                      isValid :=
                          and(
                              // Checks if the last value in the queue is same as the root.
                              eq(mload(sub(hashesBack, 0x20)), root),
                              // And whether all the proofs are used, if required.
                              eq(proofEnd, proof)
                          )
                      break
                  }
              }
          }
          /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
          /// given `proof` and `flags`.
          ///
          /// Note:
          /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
          ///   will always return false.
          /// - Any non-zero word in the `flags` array is treated as true.
          /// - The calldata offset of `proof` must be non-zero
          ///   (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
          function verifyMultiProofCalldata(
              bytes32[] calldata proof,
              bytes32 root,
              bytes32[] calldata leaves,
              bool[] calldata flags
          ) internal pure returns (bool isValid) {
              // Rebuilds the root by consuming and producing values on a queue.
              // The queue starts with the `leaves` array, and goes into a `hashes` array.
              // After the process, the last element on the queue is verified
              // to be equal to the `root`.
              //
              // The `flags` array denotes whether the sibling
              // should be popped from the queue (`flag == true`), or
              // should be popped from the `proof` (`flag == false`).
              /// @solidity memory-safe-assembly
              assembly {
                  // If the number of flags is correct.
                  for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
                      // For the case where `proof.length + leaves.length == 1`.
                      if iszero(flags.length) {
                          // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
                          // forgefmt: disable-next-item
                          isValid := eq(
                              calldataload(
                                  xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
                              ),
                              root
                          )
                          break
                      }
                      // The required final proof offset if `flagsLength` is not zero, otherwise zero.
                      let proofEnd := add(proof.offset, shl(5, proof.length))
                      // We can use the free memory space for the queue.
                      // We don't need to allocate, since the queue is temporary.
                      let hashesFront := mload(0x40)
                      // Copy the leaves into the hashes.
                      // Sometimes, a little memory expansion costs less than branching.
                      // Should cost less, even with a high free memory offset of 0x7d00.
                      calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
                      // Compute the back of the hashes.
                      let hashesBack := add(hashesFront, shl(5, leaves.length))
                      // This is the end of the memory for the queue.
                      // We recycle `flagsLength` to save on stack variables (sometimes save gas).
                      flags.length := add(hashesBack, shl(5, flags.length))
                      // We don't need to make a copy of `proof.offset` or `flags.offset`,
                      // as they are pass-by-value (this trick may not always save gas).
                      for {} 1 {} {
                          // Pop from `hashes`.
                          let a := mload(hashesFront)
                          // Pop from `hashes`.
                          let b := mload(add(hashesFront, 0x20))
                          hashesFront := add(hashesFront, 0x40)
                          // If the flag is false, load the next proof,
                          // else, pops from the queue.
                          if iszero(calldataload(flags.offset)) {
                              // Loads the next proof.
                              b := calldataload(proof.offset)
                              proof.offset := add(proof.offset, 0x20)
                              // Unpop from `hashes`.
                              hashesFront := sub(hashesFront, 0x20)
                          }
                          // Advance to the next flag offset.
                          flags.offset := add(flags.offset, 0x20)
                          // Slot of `a` in scratch space.
                          // If the condition is true: 0x20, otherwise: 0x00.
                          let scratch := shl(5, gt(a, b))
                          // Hash the scratch space and push the result onto the queue.
                          mstore(scratch, a)
                          mstore(xor(scratch, 0x20), b)
                          mstore(hashesBack, keccak256(0x00, 0x40))
                          hashesBack := add(hashesBack, 0x20)
                          if iszero(lt(hashesBack, flags.length)) { break }
                      }
                      isValid :=
                          and(
                              // Checks if the last value in the queue is same as the root.
                              eq(mload(sub(hashesBack, 0x20)), root),
                              // And whether all the proofs are used, if required.
                              eq(proofEnd, proof.offset)
                          )
                      break
                  }
              }
          }
          /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
          /*                   EMPTY CALLDATA HELPERS                   */
          /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
          /// @dev Returns an empty calldata bytes32 array.
          function emptyProof() internal pure returns (bytes32[] calldata proof) {
              /// @solidity memory-safe-assembly
              assembly {
                  proof.length := 0
              }
          }
          /// @dev Returns an empty calldata bytes32 array.
          function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
              /// @solidity memory-safe-assembly
              assembly {
                  leaves.length := 0
              }
          }
          /// @dev Returns an empty calldata bool array.
          function emptyFlags() internal pure returns (bool[] calldata flags) {
              /// @solidity memory-safe-assembly
              assembly {
                  flags.length := 0
              }
          }
      }